{"type":"meta","slug":"failure-routing-and-escalation","base_branch":"origin/main","gate0_mode":"strict","preset":"codex","land_mode":"merge-to-main"}
{"type":"goal","text":"Make seat bindings arrays so every role escalates model-by-model on repeated failure, route every v2 failure class deliberately, and add the deterministic tier-0 auto-fix seam ahead of any LLM dispatch."}
{"type":"methodology","text":"Extend the deployed v2 engine (modules/harness/v2/**, lib/gates.sh) only. Never touch modules/harness/src/ — untracked, undeployed, absent from every bundle. Loop mechanics are proven on the free offline canary preset; add canary arms, never alter an existing one."}
{"type":"intent","text":"Repeated fixer failure today burns identical dispatches on the same model. Escalating to a stronger model on each retry, and spending zero dispatches on mechanical slop a deterministic pass can fix, is what turns the harness into a usable software factory."}
{"type":"direction","verbatim":"roles: fixer=[luna/xhigh,terra/medium,terra/high] - so the array will be: First go to luna/xhigh, if luna failed, terra/medium, if terra/medium failed, go to terra/high , and if there is only 1 model: no escalation possible. if there is more models, more escalation. array can be as long as i want. i can put 5 models there if i want to. it will not override maximum budget for retry. if a setting says that max retries is 3, having 5 models in the fixer array will not be useful, should emit a warning, not crash."}
{"type":"direction","verbatim":"every role can have an array. lets just put it this way. it should be a primitive for all roles, and escalation should be a primitive for all roles that are relevant."}
{"type":"direction","verbatim":"if we allow a gate to be red and continue, it's not a gate at all. the exception i was talking about is 'gate is wrong, gate needs fixing'. i dont want a good red gate to pass just because 'it's baseline red'."}
{"type":"direction","verbatim":"docs create a diff. no-op verification is no verification. verification = op. it may be no-write verification, but not no-op."}
{"type":"direction","verbatim":"needs full observability about what the resolver decided and why"}
{"type":"direction","verbatim":"If agent is failed, so its failed."}
{"type":"deferred","text":"The tier-0 auto-fix CATALOGUE — which concrete deterministic fixes ship.","reason":"Operator directed a separate brainstorm: mine ~/.claude/projects transcripts for recurring agentic mistakes with the ca.sh composer, then triage each category safe/unsafe. This plan ships only the registry seam."}
{"type":"session_memory","note":"lib/gates.sh resolves from the bundle and run-tests.sh unsets HARNESS_ENGINE_DEV, so any acceptance command that shells out to gates.sh or runplan MUST export HARNESS_ENGINE_DEV=1 scoped to that subprocess or it tests bundle 0.1.120 and fails on the missing summary."}
{"type":"session_memory","note":"This worktree has no .claude/scripts/ship.sh (gitignored, machine-local). meta.land_mode is stamped merge-to-main, matching the main checkout's frozen wrapper, so run-plan rung 2 regenerates it non-interactively."}
{"type":"session_memory","note":"Landing does NOT deploy. modules/harness/v2/** and lib/gates.sh ship only via bin/harness-release.sh bump then release. Do that AFTER the run returns, never as a plan task — flipping CURRENT mid-run swaps the engine underneath runplan."}
{"type":"session_memory","note":"presets/adapters.json has no -xhigh model ids; gpt-5.6-{sol,terra,luna}-{low,medium,high} only. A luna/xhigh rung is unbindable until that family is registered."}
{"type":"anchor","path":"docs/specs/2026-08-07-failure-routing-and-escalation-design.md","what":"spec"}
{"type":"anchor","path":"docs/plans/2026-08-07-failure-routing-and-escalation.md","what":"plan"}
{"type":"anchor","path":"modules/harness/CLAUDE.md","what":"engine orientation — bundle resolution, canary preset, reply markers"}
{"type":"task","id":"t1","wave":1,"deps":[],"seat":"coder","tier":"medium","desc":"Read docs/plans/2026-08-07-failure-routing-and-escalation.md Task 1 (Gate red-check summary) for the full contract — it is authoritative for every signature, literal string, and journal record shape. lib/gates.sh writes a machine-readable green/red/checks JSON summary when HARNESS_GATE_SUMMARY names a path, and behaves byte-identically when it does not. Touch ONLY the files listed in files_modify. NEVER edit modules/harness/src/ — it is untracked, undeployed, and absent from every engine bundle.","files_modify":["modules/harness/lib/gates.sh","modules/harness/test/gate-summary.sh"],"verify":["cd modules/harness && bash test/gate-summary.sh"],"acceptance":["test/gate-summary.sh exits 0 and prints gate-summary: ok","gate0 run with HARNESS_GATE_SUMMARY unset creates no summary file and keeps its current exit codes"],"requires_decision":null}
{"type":"task","id":"t2","wave":1,"deps":[],"seat":"coder","tier":"medium","desc":"Read docs/plans/2026-08-07-failure-routing-and-escalation.md Task 2 (Array seat value in schema and validator) for the full contract — it is authoritative for every signature, literal string, and journal record shape. Add a bindingChain seat form (minItems 1, maxItems 8) to the preset schema and validate every chain element like a standalone seat, naming the element index in errors. Touch ONLY the files listed in files_modify. NEVER edit modules/harness/src/ — it is untracked, undeployed, and absent from every engine bundle.","files_modify":["modules/harness/spec/presets.schema.json","modules/harness/presets/_validate.mjs"],"verify":["cd modules/harness && node presets/_validate.mjs --self-test","cd modules/harness && node presets/_validate.mjs"],"acceptance":["a 3-element chain seat validates","a 9-element chain is rejected naming maxItems","a chain element with an unregistered model is rejected naming seats.fixer[1]","every shipped preset in presets/*.json still validates unchanged"],"requires_decision":null}
{"type":"task","id":"t3","wave":1,"deps":[],"seat":"coder","tier":"medium","desc":"Read docs/plans/2026-08-07-failure-routing-and-escalation.md Task 3 (Array-aware seat resolution and the fix ladder) for the full contract — it is authoritative for every signature, literal string, and journal record shape. resolveSeat accepts an array seat with an optional rungIndex; add buildFixLadder and resolveIndependentFixBinding with exact back-compat for a bare fixer plus stronger-fixer. Touch ONLY the files listed in files_modify. NEVER edit modules/harness/src/ — it is untracked, undeployed, and absent from every engine bundle.","files_modify":["modules/harness/v2/seats.js","modules/harness/v2/test/seats.test.js"],"verify":["cd modules/harness && node --test v2/test/seats.test.js"],"acceptance":["buildFixLadder on a bare fixer plus stronger-fixer preset returns rung ids dependency-repair, fixer#1, fixer#2","a 4-element fixer array returns 5 rungs","a 1-element fixer array returns 2 rungs","resolveSeat with an out-of-range rungIndex throws naming the rung","resolveIndependentFixBinding returns null when the resolver binding matches a burned binding"],"requires_decision":null}
{"type":"task","id":"t4","wave":1,"deps":[],"seat":"coder","tier":"medium","desc":"Read docs/plans/2026-08-07-failure-routing-and-escalation.md Task 4 (Tier-0 auto-fix registry seam) for the full contract — it is authoritative for every signature, literal string, and journal record shape. New v2/autofix.js holding the AutoFix registry and runAutoFix: only safe entries run, only inside the task file claims, and registration rejects entries touching gate config, test files, or .warnignore. The registry ships EMPTY. Touch ONLY the files listed in files_modify. NEVER edit modules/harness/src/ — it is untracked, undeployed, and absent from every engine bundle.","files_modify":["modules/harness/v2/autofix.js","modules/harness/v2/test/autofix.test.js","modules/harness/v2/test/index.js"],"verify":["cd modules/harness && node --test v2/test/autofix.test.js v2/test/suite-manifest.test.js"],"acceptance":["an empty registry returns applied and changed both empty","a fixture safe entry runs and reports its changed files","a fixture unsafe entry never runs","an entry is passed only the subset of files inside allowedFiles","registering an entry declaring .warnignore throws naming the entry id","suite-manifest sees autofix.test.js registered in v2/test/index.js"],"requires_decision":null}
{"type":"task","id":"t5","wave":2,"deps":["t1"],"seat":"coder","tier":"medium","desc":"Read docs/plans/2026-08-07-failure-routing-and-escalation.md Task 5 (Surface red checks from the gate) for the full contract — it is authoritative for every signature, literal string, and journal record shape. runRealGate passes HARNESS_GATE_SUMMARY and returns green plus redChecks, where null means attribution unavailable and never crashes on a version-skewed bundle. Touch ONLY the files listed in files_modify. NEVER edit modules/harness/src/ — it is untracked, undeployed, and absent from every engine bundle.","files_modify":["modules/harness/v2/gate.js","modules/harness/v2/test/gate.test.js"],"verify":["cd modules/harness && node --test v2/test/gate.test.js"],"acceptance":["a green real gate returns green true and redChecks empty array","a red real gate returns the failing check name in redChecks","a gates.sh that ignores HARNESS_GATE_SUMMARY yields redChecks null with the run still completing","a corrupt summary file yields redChecks null and no throw"],"requires_decision":null}
{"type":"task","id":"t6","wave":2,"deps":["t3"],"seat":"coder","tier":"medium","desc":"Read docs/plans/2026-08-07-failure-routing-and-escalation.md Task 6 (Verify-fixer loop walks the chain) for the full contract — it is authoritative for every signature, literal string, and journal record shape. verifyWithFixerRetries resolves attempt N from fixer rung N of the ladder, stays capped by MAX_VERIFY_FIXER_ATTEMPTS, and writes no fix.rung records. Touch ONLY the files listed in files_modify. NEVER edit modules/harness/src/ — it is untracked, undeployed, and absent from every engine bundle.","files_modify":["modules/harness/v2/run.js","modules/harness/v2/test/run-quality.test.js"],"verify":["cd modules/harness && node --test v2/test/run-quality.test.js"],"acceptance":["a 2-element fixer chain failing verify twice journals verify.retry with rung fixer#1 then fixer#2 and dispatches two different bindings","a 1-element chain journals fixer#1 twice","a 3-element chain still makes at most 2 verify attempts","no fix.rung record is written by this loop"],"requires_decision":null}
{"type":"task","id":"t7","wave":3,"deps":["t3","t5"],"seat":"coder","tier":"medium","desc":"Read docs/plans/2026-08-07-failure-routing-and-escalation.md Task 7 (Gate-fix ladder walk, stamp, and resume) for the full contract — it is authoritative for every signature, literal string, and journal record shape. Replace the RUNGS constant with a per-task ladder, stamp it as fix.ladder before the first fix.rung, resume from the stamp rather than the current preset, and pass reviseAfterReviewFail its binding so exactly one walker owns the chain. Touch ONLY the files listed in files_modify. NEVER edit modules/harness/src/ — it is untracked, undeployed, and absent from every engine bundle.","files_modify":["modules/harness/v2/quality.js","modules/harness/v2/test/quality.test.js"],"verify":["cd modules/harness && node --test v2/test/quality.test.js"],"acceptance":["a 4-rung ladder all-red produces exactly 4 gate-fix dispatches with of:4","killing after fixer#2 and resuming returns fixer#3","resuming after the preset chain was replaced still returns fixer#3 from the stamp with no quarantine","a legacy history whose fix.rung records lack of resumes without quarantine","a 5-rung chain under a 3-attempt budget warns once, journals preset.chain-truncated, and the run proceeds","reviseAfterReviewFail writes no fix.rung record"],"requires_decision":null}
{"type":"task","id":"t8","wave":4,"deps":["t5","t7"],"seat":"coder","tier":"medium","desc":"Read docs/plans/2026-08-07-failure-routing-and-escalation.md Task 8 (Keep-vs-revert and scope-violation routing) for the full contract — it is authoritative for every signature, literal string, and journal record shape. Hoist a pre-rung-1 workspace snapshot, apply the first-match keep-vs-revert precedence table on red-set divergence, and route a second file-claim escape to the dependency-repair seat before quarantine. Touch ONLY the files listed in files_modify. NEVER edit modules/harness/src/ — it is untracked, undeployed, and absent from every engine bundle.","files_modify":["modules/harness/v2/quality.js","modules/harness/v2/test/quality.test.js"],"verify":["cd modules/harness && node --test v2/test/quality.test.js"],"acceptance":["a grown red set journals fix.tree action revert","rung 1 to 2 unchanged journals keep","rung 2 to 3 unchanged journals revert","the reverted tree equals the pre-rung-1 snapshot and not the task base","redChecks null on either side journals keep","a second file-claim escape journals fix.scope-routed and dispatches the resolver seat before any quarantine"],"requires_decision":null}
{"type":"task","id":"t9","wave":4,"deps":["t7"],"seat":"coder","tier":"medium","desc":"Read docs/plans/2026-08-07-failure-routing-and-escalation.md Task 9 (Run-level escalation ceiling and defective-gate halt) for the full contract — it is authoritative for every signature, literal string, and journal record shape. Count escalated dispatches across the run and halt between tasks at ceil(taskCount/2); run gate0 on the untouched base before wave 1 and halt with the verbatim two-cause message when it is red. Touch ONLY the files listed in files_modify. NEVER edit modules/harness/src/ — it is untracked, undeployed, and absent from every engine bundle.","files_modify":["modules/harness/v2/run.js","modules/harness/v2/test/run.test.js"],"verify":["cd modules/harness && node --test v2/test/run.test.js"],"acceptance":["a 4-task run with a forced ceiling of 1 halts after the task that crossed it with that task ladder complete and run.escalation-ceiling journaled","a run whose base gate is red halts before wave 1 with the two-cause message and gate.defective journaled","a run whose base goes red mid-run halts naming the poisoning task"],"requires_decision":null}
{"type":"task","id":"t10","wave":5,"deps":["t4","t5","t7","t8"],"seat":"coder","tier":"medium","desc":"Read docs/plans/2026-08-07-failure-routing-and-escalation.md Task 10 (Tier-0 invocation before the ladder) for the full contract — it is authoritative for every signature, literal string, and journal record shape. Call runAutoFix before every rung when redChecks is a non-empty array, re-run only those checks, and contain any autofix error so tier 0 can never fail a task. Touch ONLY the files listed in files_modify. NEVER edit modules/harness/src/ — it is untracked, undeployed, and absent from every engine bundle.","files_modify":["modules/harness/v2/quality.js","modules/harness/v2/test/quality.test.js"],"verify":["cd modules/harness && node --test v2/test/quality.test.js"],"acceptance":["with a fixture safe entry that turns the red check green the quality phase completes with zero fix.rung records and one autofix.applied","with an empty registry the ladder runs exactly as before","redChecks null produces no autofix record at all","a throwing entry journals autofix.failed and the ladder still runs"],"requires_decision":null}
