Skip to content

fix(context): overflow classifier gets non-overflow guards + two behavioral detectors (#414) - #437

Merged
justrach merged 1 commit into
mainfrom
feat/prime-414-overflow
Aug 6, 2026
Merged

fix(context): overflow classifier gets non-overflow guards + two behavioral detectors (#414)#437
justrach merged 1 commit into
mainfrom
feat/prime-414-overflow

Conversation

@justrach

@justrach justrach commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Implements #414. Classification moved to a new agent_overflow.zig (policy was at 589/600), with the order guards → codes → needles, all case-insensitive (a Title-Cased rejection used to slip past and wedge the session).

The three additions

  1. Non-overflow guards win outright: Bedrock's ThrottlingException: Too many tokens and friends (rate limits, quota, TPM/RPM) classify as throttle and ride the existing retry/Retry-After path instead of triggering a pointless compaction. Deliberately scoped: overloaded/server_error were considered and rejected — a guard that swallows a real overflow wedges the session (the codex/gpt-5.x: a single-turn tool-output burst overflows the context window before auto-compaction fires (no pre-send local-estimate gate) #193 symptom), strictly worse than one wasted compaction. Documented in-source.
  2. Silent overflow (z.ai shape): HTTP 200, empty completion, usage input at/over the window → overflow recovery at all three success sites (responses + streamed + non-streamed), sharing the same pin-meter → trim → retry-once machinery as the error path.
  3. Upstream truncation (MiMo shape): finish_reason: length, zero output, input ≥99% of window → named distinctly instead of shipping as an inexplicably short answer. A normal max-tokens completion is blocked by three independent conditions; over-window-but-answered means the window figure is stale, and trimming real history over a bad constant is the costlier mistake.

Also expanded the needle table with 13 provider-real phrasings (Bedrock, Gemini, Copilot, xAI, Groq, llama.cpp, Kimi, Mistral, Ollama + two generic fallbacks — which are exactly what makes the Bedrock guard load-bearing).

Experiments

  • 37-row guard/needle table (9 guard rows, 13 regression rows proving every original pattern keeps its class, 15 new-provider rows) + 18-row behavioral table with real-shaped JSON across all three wire formats, including 8 must-NOT-trip rows.
  • PTY E2E: scripts/test-pty-overflow.py extended with 3 scenarios (throttle must-not-pin; silent-200 recovers; truncate-then-length named) — 5/5 pass. Break-and-revert: unwiring the detector reproduces the exact Overflow classifier: non-overflow guard patterns + behavioral detectors #414 symptom ([compaction failed: empty summary] wedge).

Gates

Tests 994 → 1000 (+6 exactly); all 9 golden eval files PASS; fmt + line-guard clean (agent_overflow.zig 512, policy 543↓, main.zig held at 600 via one comment rewrap); changelog under v0.0.241 (unreleased).

Pre-existing quirks confirmed on pristine base builds, not this change: the tier1 tests check can't read a count off a cached build, and the PTY harness has an intermittent /help-echo race. Both being filed separately.

Part of the prime-agent adoption batch.

…vioral detectors (#414)

The #193 classifier was six case-sensitive substrings and a two-code list.
Three gaps, all reported against real providers:

1. Nothing was checked BEFORE the overflow patterns. Bedrock formats
   throttling as "ThrottlingException: Too many tokens, please wait before
   trying again." — wording that collides head-on with the kind of generic
   "too many tokens" fallback a broad table needs. Classifying that as
   overflow discards real conversation to fix a problem compaction cannot
   fix, and shadows the Retry-After ladder that would have worked. A guard
   list now wins outright, scoped to throttling/quota: the family whose
   remedy is "wait", not "send less". Broader guards (a bare "overloaded",
   a generic "server_error") were deliberately rejected — a guard that
   swallows a REAL overflow wedges the session, which is worse.

2. Some providers accept an over-window input and never send an error.
   z.ai returns HTTP 200 with an empty completion; the only evidence is
   usage.input at or over the window. Classified from the usage now, and
   recovered through the existing pin-trim-retry path.

3. Others truncate the input to fit and report finish_reason=length with
   zero output — the model answered a conversation we did not send. Named
   distinctly (a notice, a trace note, telemetry, and last_api_error when
   recovery is unavailable) instead of reaching the user as an
   inexplicably short answer. Conservative by construction: output_tokens
   must be exactly 0, no content, and the input must fill >= 99% of the
   window, so an ordinary max-tokens completion cannot trip it.

The pattern table also grew to the phrasings twelve more providers actually
send, and matches case-insensitively — a Title-Cased rejection used to miss
and wedge the session. The guards are what make that breadth safe.

Classification moved to agent_overflow.zig; agent_request_policy.zig was at
the 600-line cap and re-exports it, keeping its two original test names so
the behavioral eval harness does not read the move as deleted coverage.

Tests: table-driven fixtures for every guard, every added provider phrasing,
and regression rows for all six original patterns proving no reclassification
(37 rows), plus 18 completion fixtures covering both detectors and the shapes
that must NOT trip them. scripts/test-pty-overflow.py gains three real-PTY
scenarios (Bedrock throttle stays on the retry path; the silent 200 compacts
and retries; truncate-then-length is reported). Suite 994 -> 1000; golden
eval harness byte-identical to a pristine build of the base commit.

Co-Authored-By: Codegraff <blackfloofie@codegraff.com>
@justrach
justrach merged commit 466bb1d into main Aug 6, 2026
6 checks passed
@justrach
justrach deleted the feat/prime-414-overflow branch August 6, 2026 09:26
justrach added a commit that referenced this pull request Aug 6, 2026
integration: the prime-adoption batch + engine slice 1c (merges #433 #434 #435 #436 #437 #438)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants