# Context Budget — Session Handoff, Not a Stop (soc-5qit) **Prior framing (deprecated):** context exhaustion was a third stop reason that wrote sticky `DORMANT` and ended the run. **Current framing (soc-5qit, 2026-05-21):** context exhaustion is a *session-handoff signal*. The loop is continuous across compactions; a heavy session writes a non-sticky `HANDOFF` marker, exits the current cron-fire turn cleanly, and the NEXT cron-fire (running on harness-compacted or fresh context) automatically clears HANDOFF in Step 1 and resumes. This change was forced by an operator failure mode (2026-05-20→21): a cron-driven /evolve session shipped 6 PRs, accumulated context, then sticky-DORMANT'd despite 10+ unblocked P1 beads in `bd ready`. The prior design treated "this Claude session can't safely take on more work" as "the work itself is done" — a category error. The cron loop spans many sessions; one session's context is irrelevant to whether the backlog has work. ## Why it's a real failure mode (still true) A long session that has shipped 5+ productive cycles accumulates context: read files cache, tool-result trails, prior `/rpi` discovery findings. When cycle N+1 selects an item that requires a fresh deep read of unfamiliar surfaces, the cycle either: - Forces a scout-mode return because the context is too heavy to execute (correct outcome, but the skill must not treat this as terminal). - Attempts execution and produces shallow work that the regression gate or self-review catches (recovery cost > the cycle's value). The fix is to surface this as a handoff to the next session, not stop the loop. ## The counter and the signal Maintain `context_streak` in `.agents/evolve/session-state.json`. Increment when ANY of these are true at end-of-cycle: - Cycle result is `scout` AND the scout's `disposition` says "context too heavy" - Cycle result is `harvested` AND the harvest was a context-budget defer (vs. a feature-suggestion) - The /rpi cycle aborted before commit because discovery context overflowed Reset to 0 when a productive `improved` cycle lands. ```bash context_streak=$(jq -r '.context_streak // 0' .agents/evolve/session-state.json) if [ "$context_streak" -ge 2 ]; then echo "CONTEXT_BUDGET_EXHAUSTED after $context_streak heavy-context cycles." echo "Writing HANDOFF (non-sticky) and exiting this cron-fire turn." # HANDOFF carries ONLY the parked work refs and the timestamp. # It is NOT a sticky stop. Step 1 of the next cron-fire clears it automatically. cat > .agents/evolve/HANDOFF <", "work_ref": "", "milestone": "Context-handoff after 2 heavy cycles. Parked: . Next cron-fire (compacted/fresh) resumes."} ``` The next operator session reads this entry, knows what was parked, and can either continue manually or wait for the next cron-fire to handle it. ## Configurable threshold Default `context_streak` threshold is 2. Override with `EVOLVE_CONTEXT_STREAK_LIMIT=N` env var if a particular session needs to tolerate more heavy cycles (rare). ## ScheduleWakeup interaction When running the Claude-Code-harness self-perpetuation mode (see `references/autonomous-execution.md`), a context-handoff exit MUST NOT call `ScheduleWakeup` from the same heavy-context turn — re-firing would re-load the heavy context. The cron schedule (or operator's external trigger) handles the re-fire. By the time the next cron-fire runs, the harness has compacted or the session has rolled, and HANDOFF auto-clears. ## What this is NOT - **Not a stop reason.** Stop reasons are operator override, max-cycles cap, regression-breaker, and genuine stagnation (bd ready=0 AND harvested=0 AND failing-goals=0 AND generators dry). - **Not a sticky marker.** DORMANT is sticky-with-auto-clear-on-new-work; HANDOFF is fully non-sticky and clears on next read. - **Not a "the work is done" signal.** It explicitly says "this session can't safely continue; the work is parked." The next session does the work. ## Cross-reference - `SKILL.md` Step 1 — HANDOFF auto-clear on cron-fire-N+1 - `SKILL.md` Step 7 — context exhaustion explicitly removed from stop reasons list - `references/scout-mode.md` — scope-filter splits or defers; never bails