test: compaction storm -- an irreducible protected message re-escalates to level 8 on every request (#fwut) - #34
Conversation
…es to level 8 every request (#fwut)
A forked sub-agent whose sole user message (or system prompt) alone exceeds
compact_threshold * budget makes _exceeds_threshold permanently true. The
"sticky state alone is sufficient -- nothing NEW to decide" fast path in
_compact_ephemeral becomes unreachable, so the full ladder re-runs to level 8
on EVERY request, reduces nothing, and emits a context:compaction event and a
notice each time.
Measured on team-shared (2026-09-05, read-only, $0):
445ac89c..._anchors-amp-dev-architect (sonnet-5) 969 events / 988 requests
strategy_level 8 on 969/969 . after_tokens > target on 969/969
after_tokens > budget on 968/969 . user_messages_stubbed 0 on 969/969
floor = the SOLE user message: 516,184 chars ~ 124,338 est tok
= 94.8% of the 131,104 budget, md5-identical across all 988 requests
877774a5..._foundation-explorer (opus-5) 2,165 events, same shape
floor = the SYSTEM prompt: 402,949 chars ~ 100,737 est tok
Not one session: across the 431-session population, 75.5% of ALL compaction
events are at level 8, and 93.4% of a forked sub-agent's compactions are --
against 2.9% for root sessions.
Three protection rules make the floor irreducible: system messages are never
compacted; user messages are never removed; and Level 8's stub -- the only
lever left -- is skipped twice over, by `first_user_idx != last_user_idx` (a
sub-agent has exactly ONE user message, so first == last) and by
`isinstance(content, str)` (the payload's content is a list of blocks).
Test-only. No runtime behaviour changed. 5 characterisation tests pin today's
behaviour (both floor shapes, the isinstance guard alone, the storm itself,
the per-request notice) and 1 xfail(strict=True) carries the fail-before
assertion, verified failing on main with "10 escalations for 10 requests".
Suite: 102 passed, 1 xfailed (baseline 97 passed).
Full analysis: docs/lanes/fwut-compaction-storm/DONE-NOTE.md
Manager verification — a real defect, honestly characterized. Merging as finding+test.Head What I re-derived myselfThe fast path IS unreachable — confirmed by reading, not inferring. needs_escalation = self._exceeds_threshold(current_tokens, budget)
if not needs_escalation:
# Sticky state alone already keeps us under the threshold ...
return final_messagesIf a single structurally-protected message alone sits at/above The threshold arithmetic holds, but one intermediate does not reproduce — and it fails in the SAFE direction. The module's estimator is So the floor is further above the gate than stated, not closer. The conclusion is unaffected; the stated char→token intermediate is off by ~3.6% and should not be quoted as exact. The gate I was asked to check, answered plainly: there is NO fail-before that fails on mainI ran the branch's test file against current main: That is not a gap being hidden; it is the lane's stated design, at line 51 of its own test:
This is the correct pattern for a defect you are characterizing and deliberately not fixing. And declining to fix it here was right. The floor is a forked sub-agent's sole user message — the delegation payload, Suite on branch: 102 passed, 1 xfailed. Closing the loop on PR #33 — this session was 76% of the sonnet-5 signalRe-ran the lane's committed
Both arms were dominated by a single pathological session (sonnet 75.6%, opus 82.3%). Removing both — the only principled cut — clears the pre-registered 2.0× line at 2.72×, consistent with the hunt's own leave-top-3-out variant (2.61×). Posted to PR #33 as well. |
Test-only. No runtime behaviour changed. Diagnoses a measured production pathology and pins it with a fail-before assertion. Item
model_performance-fwut. Spend $0.00 against a $0.00 authority — all evidence is read-only graph queries andgrep.Full analysis:
docs/lanes/fwut-compaction-storm/DONE-NOTE.mdThe defect
When a single structurally-protected message is, on its own, at or above
compact_threshold × budget,_exceeds_thresholdis permanently true._compact_ephemeral's "sticky state alone is sufficient — nothing NEW to decide" fast path becomes unreachable, so the full escalation ladder re-runs to level 8 on every single request, reduces nothing, and emits acontext:compactionevent and a compaction notice each time.Three protection rules make the floor irreducible, and they interlock:
_remove_messages_with_protection: "Removal candidates exclude ALL user messages"first_user_idx != last_user_idx)isinstance(content, str)(inline and in_stub_user_message)Two independent guards, either sufficient. Observable result:
user_messages_stubbed == 0on all 969 events — the one lever that could shrink the floor never fired once._finalize_compaction_with_statsalready diagnoses this (it logs the un-reducible floor by name) but only when the result is over budget, and it does not stop the loop.The measurement (team-shared context-intelligence graph, 2026-09-05)
0000000000000000-445ac89c107c4f52_anchors-amp-dev-architect— claude-sonnet-5, 969context:compactionevents across 988llm:requests in 3h26m:strategy_levelafter_tokens > target_tokensafter_tokens > budgetuser_messages_stubbedbudget/targetafter_messagesbefore_messagesFirst event, whole story in five numbers:
Three messages; the last level of the ladder; 694 tokens freed; still 65,075 above target.
The floor, from the raw request body (per-message sizes only):
Message [0] is the delegation payload of a forked sub-agent (
session:fork,anchors-amp-dev:architect).est(msg[0]) 124,338 + est(system) 6,289 = 130,627— exactly the observedafter_tokens. Its md5 is identical at the first request and the last, 988 requests later.A second flavour of the same defect, verified on the other arm's outlier —
877774a55cd1430c_foundation-explorer(claude-opus-5): 2,165 events, level 8 on 2,165/2,165, over target on 2,165/2,165, 0 stubs — but here the floor is the system prompt: 402,949 chars ≈ 100,737 est tok. Sonnet's floor is the user message; opus's is the system prompt. Both shapes are covered by the tests.Not one session
Across the full population (431 sessions, ≥3 compactions since 2026-08-01):
strategy_level 8.strategy_level 8Compaction is healthy in root sessions and pathological in forked sub-agents — a 32× difference — across ≥14 agent types in five bundles (
foundation-git-opsalone: 10,761 events / 33 sessions).Why the history "regrows" — it never shrank
Compaction here is ephemeral by construction (
_compact_ephemeral: "returns a NEW list — the source messages are NEVER modified").self.messagesis append-only; the compacted view is rebuilt from the full history every request. So the reported187 → 8 / 192 → 7 / 196 → 6sequence is not a compact-and-regrow cycle:before_messagesis monotone 3 → 5,174 across all 969 events, and187 → 192 → 196is simply +5, +4 — one agent turn's worth of growth.What this PR adds
tests/test_compaction_storm_irreducible_floor.pymain— the sole-user-message floor (sonnet shape), the system-prompt floor (opus shape), theisinstance(content, str)guard in isolation, the storm itself (10 requests → 10 compaction events withafter_tokenspinned), and the notice re-attached every request.xfail(strict=True)carrying the fail-before assertion. Verified failing onmainfor the right reason:with
after_tokenspinned at 9,756/9,757 against a 10,000 budget (97.6%) whilebefore_messagesgrows 25 → 35 — a faithful 13×-smaller replica of production's 99.6%.strict=Trueis deliberate: it stays green while the defect exists and turns into a hard failure the moment it is fixed, which is the signal to delete the marker.Suite
Baseline (
git stash, i.e.f2dbde9untouched):97 passed in 5.11s.Net +5 passing, +1 strict xfail, 0 regressions, 0 runtime lines changed — default-mode behaviour is byte-identical because no module source was touched.
Why no fix is proposed here
The honest fix is a behaviour change (recognise an irreducible floor, stop re-escalating, stop re-attaching a notice promising a reduction that did not happen). Under a wins-only merge policy that needs a measured run, and this lane's authority is $0.00 — correctly sized for a forensic task, and it funded the whole diagnosis. Shipping an unmeasured default change would be exactly the "unproven default-off feature" already reverted once in
e9ac159. The evidence, the reproduction and the assertion are here; the fix decision belongs to the owner. Priced ask for landing it with evidence: unit-level $0 (this xfail flips), plus one S5 A/B at2 runs × 2 arms × $2.58 ÷ 0.667 valid = $15.48.Explicitly not PR #33
PR #33 concerns a byte-identical, stale tail notice. Here the notice's stats genuinely change every turn because a fresh compaction really did run. Different pathology, same surface — and this PR changes no notice text and does not touch #33's subject. The relationship is quantified in the DONE-NOTE §6.