Warp-Drive State Reference
Warp-Drive State Reference
Section titled “Warp-Drive State Reference”The warp-drive state machine, exhaustively. State file shape, the state-machine CLI, and the full phase-transition table. Extracted from the warp-drive guide’s internals section.
Internals Reference
Section titled “Internals Reference”For contributors and power users.
State File
Section titled “State File”Location: <project>/.claude/.warp-drive-state.json
This is what state-machine.js init produces (with a few rows populated after the first transitions). Keep this in sync with the state object literal at the bottom of init() in scripts/warp-drive/state-machine.js.
{ "version": 1, "session": { "started_at": "2026-05-08T15:56:59.401Z", "started_at_epoch": 1778255819402, "pid": 51506, "level": 3, "github_user": "paulirv", "initial_dirt": [] }, "phase": "coding", "requirement": "42", "branch": "feature/issue-42-title", "chunk": { "index": 1, "total": 5, "acs": [["AC-01","AC-02"],["AC-03"]] }, "checklist": { "docs_updated": false, "tests_passed": false, "committed": false, "report_filed": false }, "budgets": { "phase_started_at": "2026-05-08T15:57:53.487Z", "retry_count": 0, "coding_cycles": 0 }, "aborted": false, "edit_counts": { "src/file.ts": 3 }, "doom_loop_events": [], "gates": { "code_complete": { "semantic": "diff_relevance_check" }, "docs_updated": { "semantic": "ac_count_check" } }, "completed_chunks": [], "history": [ { "phase": "prerequisites", "at": "..." }, { "phase": "discovering", "at": "...", "from": "prerequisites", "event": "prerequisites_ok", "source": "agent" } ], "pre_commit_head": "<sha recorded at tests_passed — the committed precondition, #1835>", "last_bash_eval": { "last_tool_command": "...", "payload_shape": { "tool_response": ["stdout", "stderr"], "tool_output": "null" }, "at": "..." }, "harness_defects": [], "metrics": { "commits": 0, "reports_filed": 0, "tests_run": 0, "chunks_completed": 0, "session_duration_minutes": 0 }, "token_usage": { "session_total": null, "chunk_snapshots": [], "current_chunk_started_at": null }, "main_branch": "master", "session_branch": null, "merge_target": "master", "merge_strategy": "ff-only"}Fields appear roughly in this order in the file. Notes on the less-obvious ones:
session.started_at_epochandsession.github_userare populated atinit; the latter comes fromgh api user -q .login(empty string ifghis unauthenticated, which silently disables@mentionnotifications).session.initial_dirt(#1756) is the working-tree dirt the run inherited, snapshotted atinitviagit status --porcelain -uall(nullwhen git could not be read). The segmentation checkpoint classifies against it so an operator’s uncommitted changes are refused with their own message rather than reported as the loop’s unfinished work — see Session segmentation.aborted: falseflips totruewhen the engine routes through theabortedphase. Cleanup messaging keys off this flag.gatesis seeded with the two semantic gates (diff_relevance_check,ac_count_check) plus anyverification_gatesfrom_workflowconfig. Thetargetsarrays from config land alongsidesemanticon the matching event keys.metrics.session_duration_minutesis updated when the engine writes the state — useful when reporting fromwarp status.main_branch/session_branch/merge_target/merge_strategyare populated from~/.claude/scripts/branch-detect.shatinitand consulted duringmerging.- After
budget_exceeded,budgetsgainsexceeded_reasons,exceeded_types,exceeded_at, andexceeded_from_phaseto record the trip. budgets.budget_auto_continuescounts the bounded L4 auto-continues taken (see below);budget_extensionscounts all continues (human or auto).checkpointandsession.segments[]appear only once a long run has segmented (see Session segmentation below).checkpointrecords the last clean boundary (at,segment_index,tokens_at,chunks_at,reason);session.segments[]is the append-only audit trail of segments ended so far.session.completed_issues(#992) accumulates the issue number of each work item as it finishes (recorded on entry toawaiting_continue). Together withsession.kickoff.issueit defines how far an issue-scoped run may still reach: the kickoff scope minus the completed list is the remaining authorized work, computed bykickoffScope()(scripts/warp-drive/kickoff.js) and surfaced asscopein thestatusoutput.
Bounded budget_exceeded auto-continue (Level 4, #873)
Section titled “Bounded budget_exceeded auto-continue (Level 4, #873)”budget_exceeded is normally a mandatory human checkpoint at every level. Under automation Level 4 it may become a bounded auto-continue — the one breaker A4 relaxes, and never silently:
- Opt-in. Off unless
_workflow.max_budget_auto_continues > 0. At 0 (the default) — and at L2/L3 always — the breaker is unchanged. - Bounded. Under L4 the loop may auto-continue at most
max_budget_auto_continuestimes for soft breakers (no-progress, cycle, phase-time, chunk, qa). Each auto-continue incrementsbudgets.budget_auto_continues; once it reaches the cap, the breaker halts for a human. - Hard ceiling always halts. The cost/token/dollar ceiling (
cost_budget_exceeded) is never auto-continued past, even under L4 with budget left. - Reviewable. Every auto-continue is logged and recorded as a
decisionissue.
The decision is a pure function (scripts/warp-drive/budget-policy.js), consulted at runtime via the budget-decision CLI action (exit 0 = may auto-continue, 3 = halt). The counter advances only on transition <root> budget_continue --data '{"auto":true}'; a human-approved budget_continue does not count against the cap.
Session segmentation (long runs, #870)
Section titled “Session segmentation (long runs, #870)”A long L3/L4 run degrades in quality as one Claude context accumulates hours of history. Session segmentation lets the loop checkpoint at a clean per-requirement boundary, end the session, and resume in a fresh context — reusing the existing substrate (the persisted state file + the warp-drive-inject.sh resume hook + a cdfork-style fresh-process spawn). There is no new resume engine; the only new pieces are the threshold trigger and the clean-boundary checkpoint.
-
Default-on, opt-out.
segment_after_tokensdefaults to120000(#959 — keeping segments below the ~150k high-cost context regime);segment_after_chunksdefaults to0. The default is resolved in exactly one place (the state-machine config layer,WORKFLOW_DEFAULTS), and an explicit_workflowvalue always wins — an explicitsegment_after_tokens: 0(withsegment_after_chunksunset/0) fully disables segmentation. -
Boundary. Segmentation is only evaluated at
awaiting_continue(a requirement is done and merged onto the integration branch, git is clean). Thecheckpointaction refuses to run at any other phase, or over a working tree that carries real changes — see the classification below for what “real” means. -
Clean means clean of work (#1756). The checkpoint classifies each dirty path rather than counting them, because a
--flightplanrun’s provision overlay rewrites the fenced cdprov-managed block in the tracked.gitignorefor the entire window in which segmentation can fire:Classification Outcome Refusal kindThe overlay’s own ignore-block edit Tolerated — bookkeeping, not work — Changes that pre-date the session Refused: commit or stash them outside the run preexistingAnything else Refused: the requirement is not committed yet inflightOverlay tolerance is narrow by design and requires both conditions: an overlay is actually applied (
.claude/.provision-overlay.jsonexists — the same signalcdprov overlay statusreports), and the working.gitignoreis byte-identical toHEADonce the fenced block is stripped from both sides. An edit anywhere outside the fence is never excused, and neither is a managed-block change with no overlay active — so a run that uses no overlay behaves exactly as it did before.session.initial_dirt— agit status --porcelain -uallpath snapshot taken atinit— is the baseline separating dirt the run inherited from dirt it created. When both are present the refusal reportsinflight(the condition the run itself owns) and lists the pre-existing paths underalso_preexisting. A session with no baseline reads every path as in-flight, the stricter reading. Note that a path dirty atinit, later committed, then dirtied again still classifies as pre-existing — the baseline is a path set, not a content hash. -
Per-segment trigger, not cumulative. The threshold measures tokens/chunks accrued since the last checkpoint (
tokens_at/chunks_atanchors), computed from the monotonictoken_usage.chunk_snapshotssum. Because the state file persists across a segment, a cumulative measure would re-trip the threshold immediately in the fresh session and loop forever — the per-segment delta resets the budget at each checkpoint. -
Level floor. Only Level 3+ auto-segments (where
awaiting_continuealready auto-continues). At L2 the boundary asks a human, so there is nothing to auto-segment. -
Seam-aware (#960).
work_selectedrecords the item’s seam metadata tostate.work_meta({issue, cap, area}— parent cap fromPart of #NN,area:label slug; best-effort, nulls on failure). Atawaiting_continuethe shared segment context compares it against the next queued item’s metadata (seamBetween): if a known-on-both-sides cap or area differs, the lowersegment_at_seam_tokensbar (default40000) governs instead ofsegment_after_tokens. Missing metadata never seams;0disables the seam trigger; the full segmentation opt-out keeps it inert. Seam trips returnkind: "seam"and asegment at seam: …reason, which also becomes the checkpoint log line. -
State is preserved, not deleted. A checkpoint writes
state.checkpoint+ appendssession.segments[]and leaves the phase atawaiting_continue— the fresh session resumes from the same state file via the inject hook, losing no state. (The state file is only deleted when the whole run reachescompleted.) -
Scope-bounded (#992). The decision’s
moreWorkinput is computed within the run’s kickoff scope: an issue-scoped run (session.kickoff.issue) measures its issue list minussession.completed_issues(deterministic, no queue peek), so a scoped-and-done run never segments — and theawaiting_continuecaller routesmoreWork: falsetocontinue_noinstead of continuing or widening into the approved queue.
The decision is a pure function (scripts/warp-drive/segmentation.js), consulted via the segment-decision CLI action (exit 0 = segment, 3 = keep going in-process). The checkpoint action records the boundary. warp status surfaces segmentation (the thresholds, the per-segment usage so far, the checkpoint lineage, and session.segments[]).
State Machine CLI
Section titled “State Machine CLI”node ~/.claude/scripts/warp-drive/state-machine.js <action> <project-root> [args]| Action | Purpose |
|---|---|
init |
Create new session: init <root> --level 2 --issue 42 --pid $PPID |
status |
Show current state (exit 2 if no session) |
declared |
List the declared automatic behaviours with, per entry, whether the source hook is present in $BOB_HOME/hooks and wired in $BOB_HOME/settings.json (--json; exit 3 when a promise cannot be kept) |
budget-decision |
L4 bounded-auto-continue verdict for the current budget_exceeded halt (exit 0 = may auto-continue, 3 = halt) — see above |
segment-decision |
Should the run segment at the current awaiting_continue boundary? (exit 0 = segment, 3 = keep going) — see Session segmentation |
checkpoint |
Record a segmentation checkpoint (clean boundary; keeps state, doesn’t change phase): checkpoint <root> [--data '{"reason":"..."}'] |
decisions-digest |
Print the session-end decisions digest (markdown) aggregating state.decisions[] for the session summary — one-place review/reversal, cross-links the tool-level audit log (#877) |
transition |
Move phases: transition <root> <event> [--data '{"key":"val"}'] |
inject |
Get phase context for SessionStart hook |
gate |
Check if an operation is allowed: gate <root> git_commit |
verify |
Run verification gates: verify <root> code_complete |
abort |
Shorthand for transition <root> abort |
reset |
Delete state file immediately |
Declared automatic behaviours (#1834)
Section titled “Declared automatic behaviours (#1834)”The automatic behaviours BoB promises — hook auto-advances, gates, detectors — are declared once, in scripts/warp-drive/declared-behaviours.js, each with a stable fingerprint of the form <kind>:<source>:<phase>→<event> (gates omit the event). A fingerprint names the declared behaviour, never a diagnosed cause, so later tooling can key compensation events and bug de-dup on it without re-diagnosing (#1833).
| Fingerprint | Hook | Promise |
|---|---|---|
hook:warp-drive-commit-detector:committing→committed |
PostToolUse (Bash) warp-drive-commit-detector.sh |
A successful git commit during committing advances to reporting — no manual committed transition |
hook:warp-drive-docs-detector:updating_docs→docs_updated |
PostToolUse (Edit|Write|Bash) warp-drive-docs-detector.sh |
During updating_docs, a successful gh issue edit <req> --body… (the AC tick) or a docs/ edit advances to testing (#1852) |
gate:warp-drive-gate:pre-commit |
PreToolUse (Bash) warp-drive-gate.sh |
A real git commit (argv-position detection, #858) is blocked outside committing |
The registry is the source the state machine renders from: the advances text for the two hook-fired phases (shown in warp status / warp viz tooltips) is generated from the registry entry, so the promise a human reads and the fingerprint tooling keys on cannot drift apart. tests/test-declared-behaviours.js (make test-declared-behaviours) asserts every fingerprint names an existing hook file and a valid (phase, event) pair in the transition table, and that every hook that calls state-machine.js transition has an entry.
node ~/.claude/scripts/warp-drive/state-machine.js declared "$(pwd)" [--json]present = the hook file exists in $BOB_HOME/hooks; wired = $BOB_HOME/settings.json references it (null when that file is unreadable — unknown is not the same as unwired). Exit 3 when any declared behaviour is missing or unwired: a promise the runtime cannot keep.
Compensation detection (#1835)
Section titled “Compensation detection (#1835)”The state machine — not the model — notices when an agent compensates for a declared automatic behaviour that did not happen. A hand-fired committed “because the hook didn’t” used to be indistinguishable from the hook working; that is how #1830 was normalised into memory and loop notes for two months.
Source attribution. Every hook that calls state-machine.js transition exports WARP_HOOK_SOURCE=<hook-name> first. transition records source: "hook:<name>" or source: "agent" on the history[] entry and in its JSON output (warp status --log shows it).
What is recorded. When an event that a kind: hook registry entry promises arrives with source: "agent" in that entry’s phase, transition appends to harness_defects[] in the state file:
{ "fingerprint": "hook:warp-drive-commit-detector:committing→committed", "phase": "committing", "event": "committed", "at": "2026-08-21T05:19:02Z", "session_id": "…", "last_tool_command": "git commit -q -F -", "payload_shape": { "tool_response": ["interrupted", "isImage", "noOutputExpected", "stderr", "stdout"], "tool_output": "null" }, "payload_file": ".claude/harness-defects/hook_warp-drive-commit-detector_committing_committed-2026-08-21T05-19-02-719Z.json"}and returns "compensation": {"fingerprint"} plus a COMPENSATION RECORDED system message so the agent knows it is compensating. last_tool_command / payload_shape come from last_bash_eval, which warp-drive-commit-detector.sh writes on every evaluated Bash call during committing (before any gate, so a failed commit is captured); payload_shape holds only the top-level keys (or JSON type) of tool_response / tool_output, never values. The entries feed defect filing (#1836); they are never deleted by the loop.
Live payload (#1839). Shape alone cannot reproduce a dead hook, so the detector hooks (warp-drive-commit-detector.sh in committing, warp-drive-docs-detector.sh in updating_docs) also keep the full redacted payload of the last call they evaluated at .claude/.last-hook-payload.json — every string truncated to 400 chars, token-shaped values (ghp_…, sk-…, xox…, AKIA…) replaced with <redacted>, stamped _captured_by / _captured_at; the same redaction is applied to last_tool_command. When transition records a compensation it copies that file to .claude/harness-defects/<fingerprint>-<timestamp>.json (fingerprint characters outside [A-Za-z0-9_.-] become _) and sets payload_file to the project-relative path; null when nothing was captured or the capture is not valid JSON. harness-defect-report.js reads payload_file and appends a ## Payload fenced JSON block (bounded at 6000 chars, the file path named) to the created issue body and to each recurrence comment, so the filed bug carries what the hook actually saw. Both paths are derived session state, gitignored, and reaped with the other .claude/ residue. A captured payload that exposes a new harness shape is also the raw material for a fixture — scripts/capture-hook-fixture.js record writes the same redacted form into tests/fixtures/hooks/.
Precondition rules. A compensation may never run the state ahead of reality, so an agent-sourced declared event is accepted only if the fact it claims is observable:
| Event | Accepted only if | Otherwise |
|---|---|---|
committed |
git rev-parse HEAD differs from pre_commit_head (recorded at tests_passed; falls back to the last commit_hashes[] sha) |
exit 1, error: "Compensation precondition unmet", precondition names HEAD as unmoved, phase unchanged |
docs_updated |
the current issue body (gh issue view <req> --json body) contains at least one - [x] |
exit 1, precondition says no checked AC, phase unchanged |
Both guards fail open when the fact cannot be verified (not a git repo; gh unreachable) — blocking the loop on a forge outage would be worse than a missed refusal. Hook-sourced events are never guarded or recorded: the hook observed the fact directly.
What this means for an agent. The committing and updating_docs phase instructions no longer say “fire it yourself if the hook didn’t”. If the phase is still committing after git log -1 shows the commit, the hook is broken; the right move is to record that (the manual fire does it for you) and carry on — not to add a “known quirk” to memory.
Outward filing of harness defects (#1836)
Section titled “Outward filing of harness defects (#1836)”harness_defects[] entries would be just another accommodation if they stayed on the state file. scripts/warp-drive/harness-defect-report.js <project-root> [--dry-run] [--json] is the channel that crosses into the maintainer’s tracker. The state machine runs it on entry to chunk_complete and session_ending (after the state is written; best-effort — a failure is reported as harness_defect_filing: {result: failed} in the transition output and never blocks; _workflow.harness_defect_filing: false opts out).
Per unreported fingerprint it either bumps the existing open harness-defect issue — found by gh issue list --label harness-defect --search "<fingerprint>" and confirmed by the body marker <!-- harness-defect: <fingerprint> --> — updating its Recurrences: N · first seen · last seen line and commenting (project, session id, date, compensation one-liner), or creates one in the /report-bug structure with labels bug, harness-defect, p3-medium. A second occurrence anywhere in the fleet lands on the same issue; it never opens a duplicate.
Routing keys on the fingerprint’s <source>: a BoB hook/script → the bigbrain tracker (resolved from the BOB_SOURCE checkout’s origin remote — never hard-coded; BOB_BIGBRAIN_REPO overrides); project-owned configuration (dev-json*, dev-health*, project-hook*, reap*, checks*) → the project repo (from its origin); unknown → both, cross-linked.
Workaround provenance (#1837). Once a filed defect has an issue number, the report also records the compensation as a loop note tagged defect: {repo, number} (kind gotcha, summary <fingerprint> did not fire — agent fired <event> by hand in <phase>), so the project’s next run sees it as [workaround for open <repo>#<n>] rather than a permanent rule. init runs loop-notes.js retire on entry to prerequisites and surfaces loop_notes_retire: {text, retired, fixed[], remaining} in its response (the retired N workaround note(s) (fixed: #a, #b) line is also prepended to the phase instruction); a project with no tagged notes makes no forge call. A queued create (forge degraded, number unknown) records no note — an untagged compensation is exactly what the tagging exists to prevent. Closure lookups share scripts/lib/defect-status.js (see the loop-memory reference).
Visibility and escalation (#1838). scripts/warp-drive/harness-defect-summary.js is the read side of the same two sources. state [<root>] summarises the state file’s reported and unreported entries as {open, recurrences, fingerprints[], issues[], reported, unreported} — the cdb line harness-defects: N open (M recurrences) and the fleet snapshot’s per-project harness_defects field read it. issues / report list the open harness-defect issues on bigbrain and the project repo (recurrences and first/last seen from the Recurrences: line, affected projects from the body plus Recurred on comments) — report prints the /what-next --report block and nothing at all when nothing is open; an unreachable forge is stated, never read as zero. escalate bumps any bigbrain issue at/over _workflow.harness_defect_escalate_after (default 3; 0 disables) to p2-high with a comment carrying the <!-- harness-defect-escalated --> marker, skipping issues already at p1/p2 or already marked (idempotent); harness-defect-report.js runs it after each filing pass and surfaces the result as escalation. cdb prints the state line under each project (and under cdb --check <project>) only when the count is non-zero; scripts/fleet/audit.js snapshot records harness_defects: {open, recurrences, unreported, fingerprints[], issues[]} per present project from the state file alone (a snapshot never calls the forge), summary.harness_defects rolls it up per machine (null when no project records any), and audit.js view renders a “Harness defects” table — per-machine rows plus a FLEET row that de-dupes fingerprints across machines — in both the human table and --json. make test-harness-defect-summary (state, issues, report, escalate, the cdb line) and make test-fleet-audit (snapshot field, roll-up, view) cover these against fixture state files and issue payloads.
Every gh call goes through the bin/gh shim: when the forge is degraded the create/edit/comment is spooled to the #1750 write-ahead queue (the entry is stamped reported.queued: true with issue: null so it is not re-filed) and replays on recovery. Reported entries carry reported: {repo, issue, at[, queued, also]}; --dry-run prints the plan (reads only); re-running is idempotent. make test-harness-defect-report covers create, bump, routing, dry-run, idempotence and the degraded-forge spool against the real shim.
Transition Table
Section titled “Transition Table”Every phase lists its valid events and where they lead. This matches the TRANSITIONS object in scripts/warp-drive/state-machine.js.
| Phase | Event | Next Phase |
|---|---|---|
| idle | start | prerequisites |
| idle | abort | aborted |
| prerequisites | prerequisites_ok | discovering |
| prerequisites | abort | aborted |
| discovering | work_selected | planning |
| discovering | no_work | session_ending |
| discovering | abort | aborted |
| planning | plan_ready | chunking |
| planning | work_skipped | discovering |
| planning | abort | aborted |
| chunking | chunks_defined | coding |
| chunking | abort | aborted |
| coding | code_complete | updating_docs |
| coding | abort | aborted |
| updating_docs | docs_updated | testing |
| updating_docs | abort | aborted |
| testing | tests_passed | committing |
| testing | tests_failed | coding |
| testing | abort | aborted |
| committing | committed | reporting |
| committing | commit_with_doc_gate | doc_drift_check |
| committing | abort | aborted |
| doc_drift_check | drift_clean | reporting |
| doc_drift_check | drift_blocked | coding |
| doc_drift_check | abort | aborted |
| reporting | report_filed | chunk_complete |
| reporting | abort | aborted |
| chunk_complete | next_chunk | coding |
| chunk_complete | requirement_done | requirement_complete |
| chunk_complete | abort | aborted |
| requirement_complete | merge_ready | merging |
| requirement_complete | abort | aborted |
| merging | merged | awaiting_continue |
| merging | merge_failed | merging |
| merging | push_failed | merging |
| merging | abort | aborted |
| awaiting_continue | continue_yes | discovering |
| awaiting_continue | continue_no | session_ending |
| awaiting_continue | abort | aborted |
| session_ending | session_ended | completed |
| session_ending | abort | aborted |
| budget_exceeded | budget_continue | the interrupted phase (budgets.exceeded_from_phase, #1183); falls back to coding |
| budget_exceeded | budget_abort | aborted |
| budget_exceeded | abort | aborted |
| aborted | abort_resolved | completed |
| aborted | abort_cleanup_failed | completed |
| aborted | restart | idle |
Skipping blocked items (work_skipped, #1270). An item discovered at planning to have zero agent-side work remaining (every open AC is human-only, a blocked label, or an open-todo gate) is skipped state-only: work_skipped records {issue, reason, at} to state.skipped_issues, clears the selection, and returns to discovering — no commit, no prerequisites re-run. Skipped issues are excluded from re-discovery for the remainder of the run (segment-context scope math, the discovery cache’s emitted queue, and the resumed-segment context injection all honour the list) and are surfaced by warp status (a Skipped: line) and the session summary (“Skipped (blocked)” section). The exit is deliberately planning-only: blockage is discovered when the issue is first read; once coding has begun, uncommitted edits may exist, so mid-coding blockage stays on the error-escalation/todo path.
Re-validation sub-mode (revalidating, #1008). Planning has a display-only sub-mode with no new transition edges: when work_selected crosses a cap/area seam (same seamBetween signal as #960 — both sides known and different; the first selection of a run and missing metadata never arm it), the state machine sets state.revalidation.pending with the from/to boundary, the transition’s planning instruction gains a RE-VALIDATION (#1008) addendum, and displayPhase reports revalidating until the pass is recorded. The pass itself is scripts/warp-drive/revalidate.js: gate <issue> compares HEAD against the issue’s <!-- approved-at: sha --> marker (exit 0 = fresh, near no-op; exit 3 = moved/unknown → lightweight AC re-check, drift classified by the #878 reversibility engine), and record appends the findings to state.revalidation.entries plus a phase-history line (event: "revalidation", rendered by warp log) and clears the pending flag. The flag is re-armed or cleared on every selection, so an unresolved pass can never leak onto the next item. Gated by _workflow.revalidate_on_area_entry (default on); scope-safe under #992 (re-validates only the already-selected item). See the how-to section.
Budget-trigger reasons. When an enforced budget trips, the engine routes the current phase into budget_exceeded and records the reason on state.budgets.exceeded_reasons. The reasons are:
| Reason | Source | Default Threshold | Config Key |
|---|---|---|---|
phase_timeout |
Phase elapsed too long | 30 min | max_phase_minutes (+ phase_timeout_enforcement) |
retry_exceeded |
Per-chunk retry counter exceeded | 5 retries | max_retries_per_chunk |
coding_cycles_exceeded |
Code/test cycles for this chunk exceeded | 3 cycles | max_coding_cycles |
no_progress |
Consecutive identical failed attempts — same failure signature AND no new diff (stagnation, not raw attempts) | 3 attempts | max_no_progress |
total_chunks_exceeded |
Chunks completed in this session exceeded hard limit. Each human-approved budget_continue extends the cap by 10 (budgets.max_total_chunks_extension, #1149). Gates starting new chunk work only — wrap-up transitions that land/close in-flight work (merge_ready, qa_passed/qa_skipped, merged/merge_failed/push_failed, promoted/promote_skipped/promote_failed, continue_no) are exempt (#1183) |
20 chunks | max_total_chunks |
session_timeout |
Total session duration exceeded hard limit | 480 min | max_session_minutes |
phase_timeout is only enforced (rather than a soft warning) when phase_timeout_enforcement is block or abort — see Configuration. The other five are always enforced.