diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 5638444b..b2e7033e 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -4,14 +4,14 @@ "owner": { "name": "Ljferrer", "url": "https://github.com/Ljferrer" }, "metadata": { "description": "WAR — Work·Audit·Refine: Claude-native multi-agent execution of a multi-phase implementation plan.", - "version": "0.14.58" + "version": "0.14.59" }, "plugins": [ { "name": "work-audit-refine", "source": "./", "description": "Execute a multi-phase implementation plan with fresh worker agents, independent read-only auditors, and a serial refine/merge queue — phase by phase, gated by you, opening one PR at the end.", - "version": "0.14.58", + "version": "0.14.59", "author": { "name": "Ljferrer" }, "keywords": ["agent-teams","workflows","orchestration","code-review","merge-queue","multi-agent","gastown"], "category": "workflow" diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 474025ba..1c13480e 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "work-audit-refine", "description": "WAR — Work·Audit·Refine. A Claude-native Agent-Teams + Workflows orchestration skill that executes a detailed multi-phase implementation plan: it breaks phases into GitHub issues, then per phase spins up fresh worker agents in isolated worktrees, independent read-only auditors (severity-gated, unanimous), and a serial refine/merge queue, landing each phase on a working branch and opening one PR to the landing branch at the end.", - "version": "0.14.58", + "version": "0.14.59", "author": { "name": "Ljferrer", "url": "https://github.com/Ljferrer" }, "license": "MIT", "homepage": "https://github.com/Ljferrer/WorkAuditRefine", diff --git a/CONTEXT.md b/CONTEXT.md index 0412198f..1bc7ffe5 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -813,7 +813,8 @@ Lead at the decompose gate / an escalation adjudication (scope deltas routed to scoring keys on it: version precedence (task instruction > red-team adjudication > plan body literal) and the adjudication-match confirmation-note rule. _Avoid_: "override", "waiver" — a row records a ruling already made and routed; it never waives a gate, -floor, or backstop (ADR 0017). +floor, or backstop (ADR 0017), and a row is **never mined from arbitrary prose** — rows come only from +the two named producers. **topology-void**: A plan clause anchored on git topology that does not exist under WAR's fast-forward per-task merges (a diff --git a/README.md b/README.md index b2440158..5ba83315 100644 --- a/README.md +++ b/README.md @@ -337,7 +337,7 @@ A version bump **must** update all four version slots across three files togethe ## Status -**0.14.58** — Standing-record truth for the `land-advance` exit contract, plus the missing precheck-arm test. Every live record of `cmd_land_advance`'s exit codes now states the contract as **0/2/3/6**, matching what the script's own header comment already said: the land-path lesson `docs/learnings/land-advance-push-first-cas-rejected-token.md` prefetched into worker and auditor seats, its portability-stripped twin in the `docs/seed/` corpus that fresh installs receive (re-packed with a regenerated manifest entry), and ADR 0023 — whose amendment sentence is narrowed in place to stop claiming the exit contract is byte-unchanged, and whose decision (B) normal-path bullet now defers to that amendment. Each names exit 6 (wrong-HEAD precheck refusal — nothing pushed, local and origin refs untouched, never a reland) and exit 3's widened unresolvable-`HEAD`/`` triggers; the lesson also gains a `wrong-HEAD precheck` keyword so land-path seats retrieve it. New case **T2.5d** in `skills/war/assets/provision-worktrees.test.sh` gives the previously untested unresolvable-HEAD arm deterministic coverage on an orphan-HEAD fixture — exit exactly 3, a `could not resolve HEAD to a commit` die line, local and origin refs byte-unchanged — refuting the phase-close waiver that had called that arm unfixturable, and the T2.9 route-identity census is rewritten count-free so future exit-3 cases cannot re-stale it. `skills/war/assets/provision-worktrees.sh` is byte-untouched: this release ships corrected records and new test coverage, no behavior change. +**0.14.59** — Standing records realigned with live behavior: a prose, contract, and guard-text truth sweep across eight drifts between what WAR's runbooks and contracts describe and what its engine actually does. `skills/war/references/schemas.md` now documents the `ledger.json` top-level `adjudications` key (sibling of `phases`/`pr_url?`, accumulating run-long, rows recorded **verbatim as threaded** in either args-contract shape — a preformatted string or `{ adjudicated|value, supersedes }`), so the recovery relaunch's promised full re-thread is lossless by construction; the `Optional adjudications` args paragraph and `skills/war/SKILL.md` step 5 both cite that key. The `held:land-failed` Outcome-handling bullet now states **both** paths by which a gate-time `environment` failure reaches that hold — a primary-land arm whose bounded fresh-env re-land came back `environment`-classified a second time (retry spent; expect a persistent environment and inspect before re-running), and a baseline-proceed arm for which no `environment-proceed` retry is ever dispatched, the two `*-proceed` flavors deliberately never chaining, so the first manual re-run is genuinely the first fresh attempt; the prior sentence asserted the spent-retry case unconditionally. `### Recovery relaunch`'s shared-mechanics list gains a fourth **Adjudication continuity** duty, so both relaunch entry points re-thread the accumulated adjudication set instead of only the held-partial-phase runbook. The run-manifest contract widens by one per-phase `envelope` aggregate (`totalTokens` / `totalToolCalls` / `agentCount`, each number-or-null, whole object nullable) on the MUST-carry list of both binding records, `## Checkpoint` gains a fail-open **Manifest stamp** bullet, and `/war-review` sources totals from that envelope — falling back to transcript mining, the input/output/cache split staying mined-or-`n/a` and labelled best-effort rather than cross-summable — plus a new **unfinalized phase record** friction signal; the manifest stays fail-open telemetry no code reads back, and ADR 0008's git > labels > ledger resume ordering is untouched. The adjudication-provenance doctrine anchor — rows come only from the two named producers — is restored to `CONTEXT.md`'s **Adjudication** glossary term and drift-locked by three new construct-anchored locks in `skills/war/assets/skill-doc-contracts.test.mjs` (the restored anchor; the schemas.md ledger key; the both-arms shape of the land-failed bullet). The auditor `git branch` guard's deny message and header comment drop a blanket read-flag characterization that never described the guard's own two read arms, now naming both in full — value-carrying flags `=`-attached, and the bare read flags enumerated — with every `case` arm, and so every allow/deny outcome, byte-unchanged. The T2.9 route-identity census in `skills/war/assets/provision-worktrees.test.sh` drops a uniqueness overclaim two auditor seats code-traced false: the push-error branch is not the only silent exit-3 route, the post-push origin-readback mismatch is silent too. **No behavior change:** `skills/war/assets/workflow-template.js`, `skills/war/assets/land-decision.mjs`, every merge-path floor, and every guard case arm are byte-untouched — this release ships corrected records, one corrected deny string, and three new doc-contract locks. ## License diff --git a/docs/learnings/full-gates-green-end-state-soft-without-threaded-gate-log-artifact.md b/docs/learnings/full-gates-green-end-state-soft-without-threaded-gate-log-artifact.md index 34b69059..d3af19dc 100644 --- a/docs/learnings/full-gates-green-end-state-soft-without-threaded-gate-log-artifact.md +++ b/docs/learnings/full-gates-green-end-state-soft-without-threaded-gate-log-artifact.md @@ -5,9 +5,9 @@ metadata: node_type: memory type: project provenance: code-verified - promoted: dev/2026-07-24-land-advance-exit-contract-truth@phase-2 + promoted: dev/2026-07-24-runbook-and-standing-record-coherence@phase-1 slug: full-gates-green-end-state-soft-without-threaded-gate-log-artifact - phase: "red-team-fallback-and-anchor-hygiene/phase-2 (Release, task 2.1) +3 recurrences (latest land-advance-exit-contract-truth/phase-2 Release task 2.1, 2026-07-24)" + phase: "red-team-fallback-and-anchor-hygiene/phase-2 (Release, task 2.1) +4 recurrences (latest runbook-and-standing-record-coherence/phase-1-integrated-tip gate-audit, 2026-07-24)" keywords: - full gates green - gate-log artifact @@ -22,6 +22,10 @@ metadata: - mechanical bump - version-slots.test.mjs arbiter - lock-step equality + - integrated-tip gate-audit + - self spot-verify + - git rev-parse HEAD + - pin proof tags: - audit-pipeline - gate-audit @@ -31,7 +35,7 @@ metadata: created: 2026-07-15 updated: 2026-07-24 originSessionId: e11422bd-1b49-4d13-9840-37a67306b3f5 - modified: 2026-07-24T21:37:05.481Z + modified: 2026-07-25T07:05:55.757Z --- **Local recurrence copy** of the repo-root lesson at `docs/learnings/full-gates-green-end-state-soft-without-threaded-gate-log-artifact.md` @@ -133,3 +137,59 @@ directly confirmed, not just audit-log-trusted. split continues to be the correct, non-escalating resolution across a fourth distinct plan/campaign — no drift in the pattern, no new lesson warranted, only occurrence-count/date freshness. + +## Recurrence 4 (2026-07-24, plan `2026-07-24-runbook-and-standing-record-coherence`, phase-1-integrated-tip gate-audit) + +Same missing-artifact shape, a different seat: not the per-task/version-slot `phase-N-end-state` +gate-audit, but the **integrated-tip** gate-audit pass (`task: "phase-1-integrated-tip"`, +`authoritative: true`). The spawn threaded the gate-log artifact path but **no stamped `pin_status` +token** (none of CONFIRMED / BENIGN-ADVANCE / STALE-MISMATCH / ERROR). Per the standing rule this is +a SOFT cannot-confirm, never a hold — but this seat did not stop at recording the gap: it ran the +**optional read-only spot-verify** instead (`git -C <_refinery-worktree> rev-parse HEAD` compared +against the threaded gate-HEAD SHA, plus `git status` clean, plus the captured gate log's own +scout-manifest-surface lines independently naming the same absolute `_refinery` path) and used that +to **fully confirm** — not just SOFT-note — that the tree provably corresponds to the gate-HEAD SHA a +provably-unrun mapped test would have surfaced as HARD. Verdict: `gate-audit:approve`, `hard:false`, +recorded as a Nit/`note` for evidence-chain completeness, with a suggested_fix to thread the +`pin_status` token into the integrated-tip dispatch "the same way the per-task gate-audit seat +receives it." + +**New nuance over Recurrences 1-3:** those were all per-task/version-slot End-state audits with no +escape hatch beyond "record SOFT and move on." An **integrated-tip** gate-audit seat has one extra +tool available — it can independently re-derive the pin proof via a read-only `git rev-parse +HEAD`/`git status` spot-check against the worktree the gate log itself names, converting a +missing-`pin_status`-token gap from "unconfirmable, SOFT" into "independently confirmed, HARD path +stayed available." Future integrated-tip gate-audit seats facing the same missing token should +attempt this spot-verify before defaulting to a bare SOFT note. + +**`code-verified`** the `pin_status` concept and its four-value enum are live in this repo at the +landed tip `3f136c0327713487768aed59f986b665b07f9cb6` — confirmed present in +`skills/war/assets/workflow-template.js`, `skills/war/assets/workflow-template.test.mjs`, +`agents/war-refiner.md`, `agents/war-auditor.md`, `CONTEXT.md`, and +`docs/adr/0024-audit-gate-verdicts-integrated-tip-captured-evidence.md` (read via the `_refinery` +worktree matching that SHA, gitdir physical path containing this plan's slug — +`.claude/worktrees/2026-07-24-runbook-and-standing-record-coherence-2026-07-24/_refinery/`). + +## Recurrence 5 (2026-07-24/25, plan `2026-07-24-runbook-and-standing-record-coherence`, phase 2 "Release", task 2.1) — back to the per-task/version-slot shape, with self-mitigation spelled out + +Fifth occurrence, back to the Recurrences 1-3 per-task/version-slot `phase-N-end-state` shape (not +the integrated-tip variant of Recurrence 4): End state 10 required all four version slots bumped in +lock-step to the next free patch (`0.14.59`), `version-slots.test.mjs` named as arbiter. This pass +carried no stamped `pin_status` token and no captured gate-log artifact path, so the commit body's +"929/929 pass 0 fail, 26 shell suites" claim is unverified evidence — SOFT, never a hold, per the +standing rule; verdict stayed `gate-audit:approve`, `hard:false`, `disposition:note`. + +**What this occurrence adds:** the auditor's own rationale spelled out its mitigation chain in full +rather than just citing the rule — confirmed the tip with `git rev-parse`, confirmed +`git status --porcelain` empty in the `_refinery` worktree, and read all four slots directly from +the pinned blobs (all `0.14.59` bare semver, `## Releasing` section satisfying both the absence key +and the presence key). `code-verified` at the landed tip `3444016a48a3d97b5beb21fc9700bd7fa788272d` +(gitdir physical path containing this plan's slug: +`.claude/worktrees/2026-07-24-runbook-and-standing-record-coherence-2026-07-24/_refinery/`): +`.claude-plugin/plugin.json` `version`, `.claude-plugin/marketplace.json` `metadata.version` and +`plugins[0].version`, and the README `## Status` line all read `0.14.59` at that tip, and +`skills/war/assets/version-slots.test.mjs` is present. + +**Confirms:** the SOFT-never-hold disposition for this exact End-state shape now holds across five +occurrences and two campaigns; the mechanical-half-confirmable / execution-half-SOFT split is +stable and needs no further pattern refinement, only occurrence-count freshness. diff --git a/docs/learnings/plan-enumerated-doctrine-census-homes-list-is-illustrative-not-exhaustive.md b/docs/learnings/plan-enumerated-doctrine-census-homes-list-is-illustrative-not-exhaustive.md new file mode 100644 index 00000000..7aeb57ef --- /dev/null +++ b/docs/learnings/plan-enumerated-doctrine-census-homes-list-is-illustrative-not-exhaustive.md @@ -0,0 +1,78 @@ +--- +name: plan-enumerated-doctrine-census-homes-list-is-illustrative-not-exhaustive +description: "A plan's End-state clause naming the 'expected homes' of a repo-wide doctrine-phrase census is a convenience reminder, not the ground truth for what counts as 'unexpected' — an independently re-derived hit (e.g. via git log -G) that the list omits is still MET if it classifies to the same ruling class (a planning artifact quoting the doctrine), never a hold" +metadata: + node_type: memory + type: project + provenance: code-verified + slug: plan-enumerated-doctrine-census-homes-list-is-illustrative-not-exhaustive + phase: "runbook-and-standing-record-coherence/Phase 1, Task 1.3 + phase-1-integrated-tip gate-audit" + keywords: + - doctrine census + - expected homes + - never mined from arbitrary prose + - git log -G + - wrap-tolerant census + - classification record + - planning artifact leave ruling + - red-team report + - end state 5 + - enumerate every hit + tags: + - war + - audit-pipeline + - doc-truth + - census + - standing-records + created: 2026-07-24 + originSessionId: 4eee3466-8bcc-44f9-a6c2-754d46624537 + modified: 2026-07-25T06:18:46.979Z +--- + +# A plan's enumerated "expected homes" list for a doctrine-phrase census is illustrative, not exhaustive + +**What happened (code-verified — landed tip `3f136c0327713487768aed59f986b665b07f9cb6`, read via the +`_refinery` worktree matching that SHA, gitdir physical path containing this plan's slug — +`.claude/worktrees/2026-07-24-runbook-and-standing-record-coherence-2026-07-24/_refinery/`):** plan +`2026-07-24-runbook-and-standing-record-coherence` End state 5 required a wrap-tolerant repo-wide +census of the restored "never mined from arbitrary prose" doctrine phrase to find it "at exactly the +expected homes … and nowhere else unexpected," and enumerated six expected homes (CONTEXT.md, +`skills/war/SKILL.md` step 5, the 2026-07-22 spec's correction note, a learnings lesson, the plan +itself, and its source spec). `git log -Gmined.from.arbitrary.prose --name-only` over the branch +turns up a **seventh** file the list never names: +`docs/red-team/2026-07-24-runbook-and-standing-record-coherence.md` (line 52, confirmed present at +the landed tip) — this plan's own CLEARED red-team report, which quotes the phrase to specify the +literal-string requirement for lock (a). + +**Why this is not a miss:** the plan's own census *procedure* is "enumerate and classify EVERY hit; +never match a pre-declared count" — the enumerated list is a reader's convenience reminder of the +obviously-expected homes, not the exhaustive ground truth the "nowhere else unexpected" clause is +checked against. The correct ruling for the missing seventh hit is plain by class-analogy: it is a +**planning artifact quoting the doctrine to specify it**, the identical class the plan already rules +"leave" for the plan and its source spec. Two independent auditor seats (the task-1.3 review and the +later `phase-1-integrated-tip` gate-audit, `gateEvidence: true`) both re-derived the full historical +hit set via `git log -G` and reached the same conclusion: End state 5 is **MET in substance**, and the +list's omission is a completeness gap in the done report's classification record, never grounds for a +HARD hold or an "unexpected home" verdict. + +**The pattern:** when a plan's End-state condition bundles (a) a literal enumerated list of expected +homes with (b) an open-ended "and nowhere else unexpected" tail, the list is advisory scaffolding for +the census-writer, not a closed set to diff against. An audit seat encountering a hit outside the +list should classify it by the **same ruling class** as the nearest listed analog (here: "planning +artifact quoting the doctrine — leave") rather than treating the plan's own list as incomplete or the +extra hit as a violation. Independently re-deriving the census (e.g. `git log -G +--name-only`) rather than trusting only the done report's stated classification is what catches this +class of gap — the auditor's own tool ceiling (no shell `grep` for an auditor seat; `git log -G` is +git-verb-allowlisted and available) makes this the practical re-derivation path. + +**How to apply:** when authoring a plan's doctrine/phrase-restoration census clause, either (a) drop +the enumerated "expected homes" list entirely and rely solely on "enumerate and classify every hit," +or (b) explicitly caveat the list as non-exhaustive and instruct the classifier to rule any +unlisted-but-same-class hit (a red-team report, a done report, another plan) the same way as the +nearest listed analog. When *auditing* such a clause, re-derive the hit set independently before +ruling "unexpected home" — a `git log -G` walk is cheap and catches same-class stragglers the plan's +own list missed. + +Related: [[plan-survey-token-sweep-misses-untagged-siblings]] (same family — a plan's literal-sweep +instruction is a floor for the *mechanical* step, not a completeness guarantee; there the miss was a +differently-worded sibling, here it is a same-worded hit outside a hand-enumerated allowlist). diff --git a/docs/learnings/plan-mandated-test-comment-uniqueness-claim-can-be-code-traceably-false.md b/docs/learnings/plan-mandated-test-comment-uniqueness-claim-can-be-code-traceably-false.md index 514c9e30..9ca797c8 100644 --- a/docs/learnings/plan-mandated-test-comment-uniqueness-claim-can-be-code-traceably-false.md +++ b/docs/learnings/plan-mandated-test-comment-uniqueness-claim-can-be-code-traceably-false.md @@ -5,8 +5,9 @@ metadata: node_type: memory type: project provenance: code-verified + promoted: dev/2026-07-24-land-advance-exit-contract-truth@phase-1 slug: plan-mandated-test-comment-uniqueness-claim-can-be-code-traceably-false - phase: "land-advance-exit-contract-truth/Phase 1, Task 1.2 (#1037)" + phase: "land-advance-exit-contract-truth/Phase 1, Task 1.2 (#1037) +1 recurrence (runbook-and-standing-record-coherence/Phase 1, Task 1.6, 2026-07-24)" keywords: - T2.9 - census comment @@ -19,6 +20,9 @@ metadata: - readback mismatch - audit disposition note vs absorb - plan-mandated wording + - route identity rests on + - inference no longer follows + - two silent exit-3 routes tags: - war - audit-pipeline @@ -28,7 +32,7 @@ metadata: - plan-fidelity created: 2026-07-24 originSessionId: 4eee3466-8bcc-44f9-a6c2-754d46624537 - modified: 2026-07-24T20:53:07.444Z + modified: 2026-07-25T06:17:55.101Z --- # A plan-mandated "only silent route" claim in a test comment can be false by code trace — still `note`, never `absorb` @@ -76,3 +80,40 @@ literal prose can go stale/inaccurate by construction; disposition `note`, never [[closure-rationale-infeasibility-claim-needs-code-trace-not-assertion]] (same construct family — `cmd_land_advance`'s declared-backstop un-fixturable arms; a different claim, same "trace before trusting" discipline). + +## Recurrence 1 (2026-07-24, plan `2026-07-24-runbook-and-standing-record-coherence`, Task 1.6) + +The campaign carry-over: this plan's Task 1.6 (operator-ratified "Auditor-suggested shape") corrected +the exact sentence flagged above — the T2.9 census in +`skills/war/assets/provision-worktrees.test.sh` no longer claims a single "only SILENT exit-3 route"; +it now names both silent routes ("the push-path silent ones (the push-error branch and the post-push +origin-readback mismatch) print nothing, while the rest die LOUDLY..."). **Code-verified** at the +landed tip `3f136c0327713487768aed59f986b665b07f9cb6` (read via the `_refinery` worktree matching that +SHA, gitdir physical path containing this plan's slug — +`.claude/worktrees/2026-07-24-runbook-and-standing-record-coherence-2026-07-24/_refinery/`): the +corrected paragraph is live at `skills/war/assets/provision-worktrees.test.sh` (search "route +identity rests on (b)+(c)+(d) TOGETHER"). + +**New wrinkle:** the correction's retained conclusion clause — "route identity rests on (b)+(c)+(d) +TOGETHER" — no longer *follows* from the corrected two-silent-routes premise the way it did under the +old (false) uniqueness premise. None of (b) ls-remote-succeeds, (c) token-distinctness-by-name, or +(d) die-text-absence individually or jointly separates the two *silent* exit-3 routes from each +other (both print nothing, both pass (d)'s die-text-absence check). What actually forecloses the +post-push readback-mismatch arm for T2.9's own fixture is (c)'s empirical fact that the identical +push is pre-receive-DECLINED (`push_rc != 0`, proven by the direct-push probe), plus (e)'s +origin-tip-unchanged check — neither of which the retained inference sentence names as the +discriminator. Two auditor seats (Nit, `note`) again confirmed this stays non-blocking: the text is +still byte-for-byte the plan's own mandated ("Auditor-suggested") shape, and floors (i)/(iii)/(v) are +satisfied regardless of the inference's rigor. + +**Pattern reinforced:** correcting a plan-mandated claim's *headline* falsehood (the uniqueness claim) +does not guarantee the claim's supporting *inference* becomes rigorous too — a census/comment can be +factually accurate about *what exists* (two silent routes) while its own "therefore X follows" +sentence quietly stops following from what it now lists. Audit disposition stays `note`: the plan's +mandated wording is latitude the worker must follow, not a worker defect, and rewording the inference +belongs to a future doc-truth pass explicitly scoped to it — not to the task that fixed the +uniqueness claim. + +**Anchors (verify still present before acting):** `skills/war/assets/provision-worktrees.test.sh`, +search "route identity rests on (b)+(c)+(d) TOGETHER" (the T2.9 case-comment block, immediately +preceding `PAIR9="$(setup_origin_pair)"`). diff --git a/docs/learnings/release-blurb-headline-count-word-can-mismatch-its-own-enumeration.md b/docs/learnings/release-blurb-headline-count-word-can-mismatch-its-own-enumeration.md new file mode 100644 index 00000000..813cae4b --- /dev/null +++ b/docs/learnings/release-blurb-headline-count-word-can-mismatch-its-own-enumeration.md @@ -0,0 +1,77 @@ +--- +name: release-blurb-headline-count-word-can-mismatch-its-own-enumeration +description: "A release blurb's opening count word ('a truth sweep across eight drifts') can outrun the count of items its own following sentences actually enumerate — one landed drift can have no describing sentence at all" +metadata: + node_type: memory + type: project + provenance: code-verified + slug: release-blurb-headline-count-word-can-mismatch-its-own-enumeration + phase: "runbook-and-standing-record-coherence/phase-2 (Release, task 2.1)" + keywords: + - release blurb count mismatch + - Status section headline count + - eight drifts seven described + - enumeration undercounts headline + - self-inconsistent prose count + - README Status line + - version bump release note + - campaign carry-over undescribed + tags: + - release + - prose-precision + - audit-finding + - readme-status + created: 2026-07-25 + modified: 2026-07-25T07:06:20.997Z + originSessionId: 4eee3466-8bcc-44f9-a6c2-754d46624537 +--- + +# A release blurb's headline count can outrun what it actually enumerates + +**Context (gate-audit-sourced, `phase-2-end-state` pass, task 2.1, plan +`2026-07-24-runbook-and-standing-record-coherence`, landed tip +`3444016a48a3d97b5beb21fc9700bd7fa788272d`):** the `## Status` blurb (`README.md` line 340 — +`code-verified`, read at the `_refinery` worktree whose gitdir physical path contains this plan's +slug, `.claude/worktrees/2026-07-24-runbook-and-standing-record-coherence-2026-07-24/_refinery/`, +this servitor's own cwd being a stale sibling worktree per +[[servitor-verify-on-write-worktree-can-lag-just-landed-phase]]) opens "a prose, contract, and +guard-text truth sweep across **eight** drifts," then narrates seven: ledger `adjudications` key, +the two-path `held:land-failed` bullet, the relaunch Adjudication-continuity duty, the run-manifest +envelope trio, the doctrine-anchor restore + drift-locks, the auditor deny-text correction, and the +T2.9 census wording carry-over. The eighth landed drift — a roadmap `Files` cell + `CONTEXT.md` +contention-row correction (commit `3fae45b`, both referents confirmed still present at the landed +tip: `docs/roadmaps/2026-07-22-run-resilience-and-hardening-roadmap.md` and `CONTEXT.md`'s +contention row) — has no describing sentence anywhere in the blurb, so a reader counting sentences +against the headline comes up one short with no way to reconcile it. + +**Why this is a Nit, not a defect:** the plan's End state named exactly six describable items and +all six are present in the blurb; the omitted eighth item is internal campaign bookkeeping on a +non-plugin-shipped doc (a roadmap file + a glossary contention row), not a plugin-shipped surface +the End state required narrating. Non-blocking, `disposition: note`, and not absorbable — the +`## Status` line is a release slot outside this fix-round's scope to touch incidentally. + +**The pattern:** a release blurb's opening headline count is easy to set once (or copy from an +adjudication/plan artifact that tracked N items) and then drift silently as the blurb is drafted +sentence-by-sentence — an item can be dropped from the prose (judged low-value to narrate, or +simply missed) without anyone remembering to decrement the headline number. This is the release-note +sibling of [[plan-mandated-banner-count-can-undercount-additive-drift-pins]] (a structural-test +banner's stale count, held byte-unchanged deliberately across plans) — same shape, opposite +mechanism: there the count is frozen by a plan mandate and correctly stale; here the count and the +enumeration are both freshly authored in the same release commit and simply fell out of sync with +each other, because nothing checks a blurb's headline number against its own sentence count. + +**How to apply:** when drafting or reviewing a release blurb that opens with "a sweep across N +things" (or any headline count), count the sentences/clauses that actually follow before landing — +if an item is deliberately left undescribed (e.g. because it's internal bookkeeping, not a +user-facing surface), either decrement the headline or add one clause naming it, rather than leaving +the two to silently disagree. This has no automated guard (`version-slots.test.mjs` and the doc- +contract locks check other properties of this section, not headline-vs-enumeration arithmetic), so +it is only ever caught by a careful audit read. + +Related: [[plan-mandated-banner-count-can-undercount-additive-drift-pins]] (sibling family: a count +word in a doc surface can legitimately or accidentally drift from what it counts — the discriminator +is whether a plan mandate freezes the number deliberately). [[release-blurb-overstates-guard-semantics]] +(same release-blurb-prose-precision family, different specific overclaim shape — guard-behavior +scope rather than headline arithmetic). [[servitor-verify-on-write-worktree-can-lag-just-landed-phase]] +(how the eighth drift's referents were confirmed against the actual landed tip rather than a stale +cwd). diff --git a/docs/learnings/release-blurb-overstates-guard-semantics.md b/docs/learnings/release-blurb-overstates-guard-semantics.md index fd460c43..3cb2c250 100644 --- a/docs/learnings/release-blurb-overstates-guard-semantics.md +++ b/docs/learnings/release-blurb-overstates-guard-semantics.md @@ -24,7 +24,7 @@ metadata: - "[[gitmodules-working-tree-read-vs-ref-snapshot]]" created: 2026-06-30 originSessionId: 0e364ee5-f0b3-47f6-a9e4-9bf2dd555733 - modified: 2026-07-23T21:24:23.857Z + modified: 2026-07-25T07:05:38.230Z --- # Release blurb prose overstates guard semantics @@ -127,3 +127,49 @@ surface (guard *forms* across files vs. flag *shapes* within one enumeration). **Left unfixed at land** (both times): the change would touch the README `## Status` release slot mid-phase, which is out of task 2.1's `Files:` list to touch incidentally. + +## Recurrence 4 (2026-07-24, plan `2026-07-24-runbook-and-standing-record-coherence`, phase 2 "Release", task 2.1) — "every case arm ... byte-unchanged" claims a byte-identity property one arm doesn't hold + +A fifth distinct instantiation, closest in mechanism to +[[guard-deny-string-blanket-adjective-mismatches-mixed-flag-shapes]] (same guard file, same +`branch` read-form loop, a different release cycle's touch-up of it) but a different specific +overclaim: the `## Status` blurb (`README.md` line 340 at land, landed tip +`3444016a48a3d97b5beb21fc9700bd7fa788272d`) states the auditor `git branch` guard's deny message and +header comment change "with every `case` arm, and so every allow/deny outcome, byte-unchanged," and +closes with "every guard case arm are byte-untouched." **`code-verified`** — read at the phase's +`_refinery` worktree (gitdir physical path containing this plan's slug: +`.claude/worktrees/2026-07-24-runbook-and-standing-record-coherence-2026-07-24/_refinery/`, this +servitor's own cwd being a stale sibling worktree on a different branch per +[[servitor-verify-on-write-worktree-can-lag-just-landed-phase]]): in +`hooks/validate-auditor-git.sh`, the corrected deny message lives **inside** the branch-loop's `*)` +catch-all arm's body (the `deny "git branch admits read forms only: ..."` string), so that one arm's +body is not literally byte-unchanged — only its `case` *pattern* (bare `*`) is. What genuinely is +byte-identical across every arm is the **pattern list** each arm matches on +(`--contains=*|--no-contains=*|...` and `--list|--all|...`), and therefore every allow/deny +*outcome* for any given input token — which is the substantively true and load-bearing claim. + +**The pattern, sharper than Recurrence 3's:** Recurrence 3 was one clause misapplying a uniform +descriptor to a set where one *member* used a structurally different mechanism. Here the blurb +conflates two distinct properties of the same `case` statement — "the arm's matching *pattern* is +byte-unchanged" (true, and it's what actually matters for allow/deny behavior) with "the arm's +*body* is byte-unchanged" (false for the one arm whose body is the exact site of the intentional +fix) — and states the stronger, false property using the weaker, true property's evidence. The +imprecision is self-disclosing in context (the same sentence just said the deny message changed, +and the paragraph's own closing "No behavior change" clause enumerates "one corrected deny string"), +so no reader is actually misled; both auditor seats that flagged it (task-level and gate-audit) +rated it Nit/`disposition: note`, non-blocking, not absorbable (release slot; also plan-mandated +phrasing — mirrors the plan slice's own parenthetical almost verbatim). + +**How to apply:** when a release blurb claims "every arm of `case` construct X is byte-unchanged" as +shorthand for "the allow/deny outcome didn't change," check whether the touched fix (a corrected +error string, a reworded comment) sits *inside* one of those arms' bodies. If it does, the true +claim is at the *pattern*/*outcome* level, not the *arm* level — say "every arm's pattern — and so +every allow/deny outcome — byte-unchanged" rather than "every arm ... byte-unchanged," or the two +will visibly contradict a sentence two clauses earlier that says a string inside one of those arms +changed. + +Related: [[guard-deny-string-blanket-adjective-mismatches-mixed-flag-shapes]] (same guard file, +same `branch` read-form loop, an earlier release's adjective-vs-enumeration mismatch — together +these two lessons show this one guard's deny-message precision has now tripped an auditor twice +across two different plans). [[servitor-verify-on-write-worktree-can-lag-just-landed-phase]] (how +this fact was confirmed against the actual landed tip rather than a stale cwd). diff --git a/docs/plans/2026-07-24-runbook-and-standing-record-coherence.md b/docs/plans/2026-07-24-runbook-and-standing-record-coherence.md index 73a94a31..844587b6 100644 --- a/docs/plans/2026-07-24-runbook-and-standing-record-coherence.md +++ b/docs/plans/2026-07-24-runbook-and-standing-record-coherence.md @@ -6,10 +6,14 @@ Source spec: `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design ## Commander's Intent -- **Purpose:** seven independently re-verified drifts between WAR's standing records (runbooks, - contracts, glossary, roadmap, guard text) and the behavior those records describe — none an +- **Purpose:** eight independently re-verified drifts between WAR's standing records (runbooks, + contracts, glossary, roadmap, guard text, test-comment census) and the behavior those records + describe — seven surveyed for this plan plus one **campaign carry-over** from plan 1 + (`2026-07-24-land-advance-exit-contract-truth`, Task 1.2), whose plan-mandated T2.9 census + wording two auditor seats code-traced false and the operator ratified correcting here — none an engine defect, every one misleading exactly the reader the record exists for (a recovering Lead, - a relaunch operator, a `/war-review` miner, an auditor seat hitting a deny message). Realign + a relaunch operator, a `/war-review` miner, an auditor seat hitting a deny message, a future + reader trusting a test's own account of the routes it discriminates). Realign every record with the live truth, restore the orphaned "never mined from arbitrary prose" doctrine anchor and drift-lock it, and widen the run-manifest contract so `/war-review` can honestly source token/tool totals — while the manifest stays fail-open telemetry that no code @@ -25,9 +29,9 @@ Source spec: `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design ride `deps` wave edges so no citation ever dangles. Each spec token sweep is a floor, not a ceiling — grep, adjudicate every match, then hand-scan the same-scope prose and record survey-derived corrections in the done report; a straggler outside all task footprints is - reported (`war-followup`), never edited. Wording latitude on the 4.2/4.7 replacement texts is - the worker's within the named-element floors (both arms in 4.2; the `=-attached` literal and no - blanket adjective in 4.7). + reported (`war-followup`), never edited. Wording latitude on the 4.2/4.7 replacement texts and + Task 1.6's replacement invariant sentence is the worker's within the named-element floors (both + arms in 4.2; the `=-attached` literal and no blanket adjective in 4.7; floors (i)–(v) in 1.6). - **End state:** 1. Ledger contract closes the loop: the `## ledger.json — run state` jsonc block in `skills/war/references/schemas.md` names a top-level `adjudications` key (sibling of @@ -38,8 +42,12 @@ Source spec: `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design file and `skills/war/SKILL.md` step 5's "record each row in the run ledger" both carry the parenthetical citation to that key (grep `adjudications` over both files shows the citations; each match adjudicated per spec §4.1). - 2. Two-path `held:land-failed` prose: the `- **`held:land-failed`**` Outcome-handling bullet in - `skills/war/SKILL.md` carries both arms — a primary-land arm (retry spent: the bounded + 2. Two-path `held:land-failed` prose: the `held:land-failed` Outcome-handling bullet in + `skills/war/SKILL.md` — located by its **2-space-indented, token-only** prefix + `/^ {2}- \*\*`held:land-failed`/` and terminated at the next **same-indent** `/^ {2}- \*\*/` + sibling, the construct `land-decision.test.mjs` already uses (the compact + `` - **`held:land-failed`** `` wrap has **zero** occurrences in `SKILL.md`; it is + *schemas.md*'s header form, never this file's) — carries both arms — a primary-land arm (retry spent: the bounded fresh-env re-land came back `environment`-classified a second time; expect a persistent environment, inspect before re-running) and a baseline-proceed arm (retry never dispatched — no chaining, deliberate — so the first manual re-run is genuinely the first fresh attempt) — @@ -49,9 +57,19 @@ Source spec: `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design existing root-cause prose and stay untouched). No surface in `skills/war/` or `agents/` still states the unconditional **environment-retry** "already spent" claim: the `already spent` grep + same-scope hand-scan record is in the done report, with the two known - keeps adjudicated (the sibling `environment` bullet's "the retry provably spent" — correct - for the exhaustion path it describes; `workflow-template.js`'s ace-demotion string "the - single ace attempt is already spent" — a different retry budget entirely). + keeps adjudicated (the sibling `environment` bullet's "the retry provably spent" — a + **hand-scan find, not an `already spent` grep hit**, correct for the exhaustion path it + describes; `workflow-template.js`'s ace-demotion string "the single ace attempt is already + spent" — a different retry budget entirely; the grep's own pre-change hit count over the + scope is **2** — the target sentence plus the ace-demotion string). **This absence is + mechanically guarded, not merely swept:** Task 1.3's new lock **(c)** (End state 6) asserts + the extracted `held:land-failed` bullet carries **both arms** — so leaving the single + unconditional sentence in place reds the gate. It is a **presence** key, not a + `/already\s+spent/i` absence key: the sanctioned two-path text keeps "already spent" inside + the *conditional* primary-land arm, so an absence key would red the correct text and pass + the wrong one. The grep and the Lead's integrated-tip backstop sweep are the breadth layer + over the rest of `skills/war/` + `agents/`; the lock is the depth layer on the one bullet + this plan rewrites. 3. Shared-mechanics duty present: the `### Recovery relaunch` section's **Shared mechanics (both entry points)** list contains a fourth, **Adjudication continuity** bullet — re-thread the full accumulated `args.adjudications` set from the ledger record (the `ledger.json` @@ -62,58 +80,187 @@ Source spec: `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design each number-or-null, whole object nullable) on the MUST-carry list (binding-to-attempt, null-tolerated — the `workflowRunId` posture); `skills/war/SKILL.md`'s **On phase return** stamp list includes the envelope aggregates sourced from the Workflow task-completion - envelope (unsurfaced ⇒ `null`) and `## Checkpoint` carries a **Manifest stamp (telemetry, + envelope (unsurfaced ⇒ `null`), **its `Field names follow spec §4.A` MUST-carry mirror + sentence lists the envelope aggregates in its per-phase set** (grep `MUST-carry` over + `skills/war/SKILL.md` and confirm — the schemas.md/SKILL.md binding-set pair moves in + lock-step, never one side alone), and `## Checkpoint` carries a **Manifest stamp (telemetry, fail-open)** bullet placed before the issue-lifecycle floor bullet; `skills/war-review/SKILL.md` - contains no "manifest never carries them" claim (grep `never carries` over that file is - empty), its §3 total-tokens and total-tool-calls Source cells read "manifest - `phases[].envelope`, else mined (transcripts)" with the input/output/cache split staying - mined-or-`n/a`, and §4 lists the **unfinalized phase record** friction signal (a phase - record lacking `endedAt`, `tasks`, or `land` although the run ended or a later phase - started). Sweep record (grep `manifest` over `skills/war-review/SKILL.md` + hand-scan of the - §3 table and `## Scavenge`) in the done report. + contains no "manifest never carries them" claim (the **wrap-tolerant** absence check + `tr '\n' ' ' < skills/war-review/SKILL.md | tr -s ' ' | grep -oi 'never carries'` returns + nothing — case-insensitive AND joined-line, both limbs of the backstop preamble's rule: + that file hard-wraps at ~100 columns, so a line-local grep is exactly the wrap-blindness + this plan corrects elsewhere; pre-change hit count **1**); its §3 **total tool calls** row's Source cell reads "manifest `phases[].envelope`, + else mined (transcripts)", and — because §3 today carries a **single combined row** whose + metric label *is* the split (`total tokens — input / output / cache (split when + available)`), which cannot simultaneously source from the envelope and stay mined — that + one row is **split into two**: `total tokens` with Source "manifest `phases[].envelope`, + else mined (transcripts)", and `token split — input / output / cache` with Source "mined + (transcripts), `n/a` when unsourceable". §4 lists the **unfinalized phase record** friction + signal (a phase record lacking `endedAt`, `tasks`, or `land` although the run ended or a + later phase started). Sweep record (grep `manifest` over `skills/war-review/SKILL.md` + + hand-scan of the §3 table and `## Scavenge`) in the done report. + **Envelope provenance (attested, not assumed).** The three aggregate names are the + harness's real task-completion envelope fields, observed on this campaign's own runs — WAR + plan 1 phase 1 (`agentCount` 18 / `totalTokens` 1898609 / `totalToolCalls` 409), plan 1 + phase 2 (7 / 554519 / 159), and this plan's `/red-team` run (27 / 2108110 / 508). They are + recorded here because nothing in the repo tree evidences the envelope shape, so a reader + (or an auditor seat) cannot otherwise tell an observed field from an invented one; the + null-tolerated posture stays, but it is a tolerance, not a cover for an unsourceable field. + **Charter unchanged (the Purpose's "stays fail-open telemetry that no code reads back and + ADR 0008's resume ordering is untouched" clause, made checkable here rather than left as + unverifiable Purpose prose):** the `## Run manifest` block's fail-open / never-resume-input + charter sentences are **byte-unchanged** by this plan — the widening ADDS the `envelope` + aggregate to the MUST-carry list and edits nothing that describes what the manifest is for — + and **no ADR 0008 surface is edited** (`git diff --name-only` for the phase contains no + `docs/adr/0008-*` path). A grep for readers of `.claude/war/runs/*.json` across the repo + returns no code reader, so the widening stays telemetry, never resume input. 5. Doctrine anchor restored and cited: a **wrap-tolerant** repo-wide census of the phrase - (whitespace-tolerant between tokens — the lesson body carries one occurrence wrapped across - a line break, and a single-line grep misses exactly the defect class the - misattribution-pairing lesson records) finds it at exactly the expected homes — the - `CONTEXT.md` `**Adjudication**:` term's `_Avoid_` line, `skills/war/SKILL.md` step 5's - Provenance-discipline sentence, the 2026-07-22 spec's dated correction note, the learnings - lesson body (left as a record), and the new test lock — and nowhere else unexpected - (classification record in the done report). + (whitespace-tolerant between tokens — at least one occurrence is wrapped across a line + break, so a single-line grep silently drops it, exactly the defect class the + misattribution-pairing lesson records; the census **enumerates every hit** rather than + matching a pre-declared count, because a hardcoded count rots the moment any surface + rewraps) finds it at exactly the expected homes — the `CONTEXT.md` `**Adjudication**:` + term's `_Avoid_` line, `skills/war/SKILL.md` step 5's Provenance-discipline sentence + (which Task 1.2 (e) writes carrying the **literal** phrase — see that slice's floor), the + 2026-07-22 spec's dated correction note, the learnings lesson bodies (left as records — + note `spec-non-goal-citation-of-a-doctrines-home-file-can-be-wrong.md` carries **multiple** + occurrences, two of them wrapped), **this plan and its source spec** + (`docs/plans/2026-07-24-runbook-and-standing-record-coherence.md`, + `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design.md` — planning + artifacts that quote the doctrine to specify it; **leave**) — and nowhere else unexpected + (classification record, enumerating every hit with its ruling, in the done report). The + new test lock is deliberately **not** an expected census home: lock (a) spells the phrase + `\s+`-separated by its own floor (never literal spaces), so it is invisible to the census + by construction — an empty census result over `skill-doc-contracts.test.mjs` is correct, + not a missing home. 6. New locks green and red-provable: `node --test skills/war/assets/skill-doc-contracts.test.mjs` - passes with two new construct-anchored locks — (a) the `CONTEXT.md` `**Adjudication**:` block - (extracted from the bolded term to the next bolded glossary term) matches the doctrine + passes with **three** new construct-anchored locks — (a) the `CONTEXT.md` `**Adjudication**:` + block (extracted from the bolded term to the next bolded glossary term) matches the doctrine phrase with `\s+` between tokens, case-insensitive (wrap-tolerant — Task 1.3's regex floor); (b) the `## ledger.json — run state` jsonc block of - `skills/war/references/schemas.md` names `adjudications` — and temporarily removing either - referent reds exactly its lock (red-proof recorded in the commit body, the file's house + `skills/war/references/schemas.md` names `adjudications`; **(c) a BOTH-ARMS PRESENCE key — + the `held:land-failed` Outcome-handling bullet extracted from `skills/war/SKILL.md` + (located by the 2-space token-only prefix `/^ {2}- \*\*`held:land-failed`/`, terminated at + the next SAME-INDENT `/^ {2}- \*\*/` sibling — the in-repo precedent at + `land-decision.test.mjs`) matches BOTH a primary-land-arm marker AND a + baseline-proceed-arm marker** — the committed mechanical guard backing End state 2. It is + deliberately **not** a bare `/already\s+spent/i` absence key: the spec's own sanctioned + replacement text keeps "already spent" **inside the conditional primary-land arm**, so an + absence key would red the *correct* two-path text and green the *unconditional* one only by + accident. The defect End state 2 targets is the **unconditional framing**, not the token — + so the lock asserts the two-path shape positively. Temporarily collapsing the bullet back to + the single unconditional sentence reds exactly (c); temporarily removing either other + referent reds exactly its lock (red-proofs recorded in the commit body, the file's house style). D18 and every existing row pass with their extraction constructs untouched. 7. Guard text truth: `bash hooks/validate-auditor-git.test.sh` passes with zero assertion/expectation edits — J16 still finds the literal `=-attached` in the space-form - deny stderr and every J-series `git branch` allow/deny outcome is unchanged. Absence floor: - grep `takes only =-attached` over `hooks/` returns nothing. Presence floor (the + deny stderr and every J-series `git branch` allow/deny outcome is unchanged. Absence floor + (a **string set**, not one literal — the two corrected surfaces word the same blanket claim + differently, so a deny-only grep would come back empty while the header comment's blanket + wording survives verbatim). Each key must be **non-vacuous at the base**: it MUST match + today, before any edit, or it can never discriminate a corrected surface from an untouched + one. Both keys are **case-insensitive** — the deny reword's likeliest shape is + a sentence-initial recasing (`Takes only =-attached …`), which a case-sensitive grep would + wave through. The rejected key `EVERY token must be an enumerated read flag` wraps mid-phrase + across the live header comment's two lines with a `# ` continuation marker sitting inside it + (`… With arguments, EVERY token` / `# must be an enumerated read flag with =-attached + values`), so it matches **zero** times at the base **under any line-local grep** and is + rejected as a line-local key — under the `#`-comment normalized form below it does match + once, so the phrase is not inherently unmatchable; key (2) is preferred because it is both + line-resident today and normalized-checked, discriminating under every form — the exact + wrap-blindness this plan calls out for the doctrine census + in End state 5. The two keys, over `hooks/`, both returning nothing after the edit: + (1) `grep -rin 'takes only =-attached' hooks/` — pre-change hit count **1** (the deny + string); (2) the phrase `an enumerated read flag with =-attached values` checked under the + `#`-comment normalized form + (`sed 's/^[[:space:]]*#[[:space:]]*//' hooks/validate-auditor-git.sh | tr '\n' ' ' | + tr -s ' ' | grep -ic 'an enumerated read flag with =-attached values'`) — pre-change hit + count **1**. Key (2) is normalized, **not line-local, because Task 1.5's own deliverable + rewrites this wrapped comment**: a bare line-local grep goes green on any re-wrap that moves + the split point while the blanket claim survives verbatim. Record both + pre-change counts in the done report so each floor is provably red-provable, not vacuous. + Presence floor (the discriminating proof the reword actually landed — J16's surviving-substring pin alone cannot - show it): the deny-message line greps positive for both shape classes — at least one - `=-attached` value-flag form (`--contains=`) **and** at least one enumerated bare flag - (`--list`) — with no blanket adjective covering the bare set, and the done report quotes the - new deny string and header comment verbatim. The adjacent header comment states the same - mixed-shape truth. + show it), **identical to Task 1.5 floor (iii), including its stated derivation**: the set of + flag tokens named in the new deny message **equals, exactly**, the union of the two `case` + arms' `|`-split patterns — arm side split on `|` with each token's trailing `*` stripped + (`--contains=*` → `--contains=`); message side extracted only at a start/space/`(`/`,` + boundary (so the floor-(i) literal `=-attached` yields no token), each token running from + that boundary **to the first character outside `[A-Za-z0-9=<>_-]`** (trailing `,` `;` `)` + `.` never part of the token), with **everything after a token's `=` discarded** to leave + the bare `=` (`--contains=` → `--contains=`; a concrete value like `--sort=refname` + collapses identically); **scope is the whole + one-line message** (operator-adjudicated, red-team round 5), so the deny string names no + denied token as a counter-example — denied-shape teaching lives in the header comment, never + the deny string. **Not a per-token + substring grep** (`-a` ⊂ `--all`, `-r` ⊂ `--remotes`, `-v` ⊂ `--verbose`/`-vv`: a long-forms-only + message passes a substring check while omitting three tokens — the exact partial-enumeration + defect this floor exists to catch). No blanket adjective covers the bare set, and **no + completeness adjective ("exactly", "only", "all") stands over a list shorter than the arm it + describes**. The done report quotes the new deny string, the header comment, and **both derived + token sets** verbatim. The adjacent header comment states the same mixed-shape truth. 8. Roadmap record honest: in `docs/roadmaps/2026-07-22-run-resilience-and-hardening-roadmap.md`, row 6's Files-owned cell lists `CONTEXT.md` and the shared-file contention `CONTEXT.md` row reads `2, 3, 4, 5, 6, 7`; nothing else in the file moves. 9. Zero collateral drift: `node --test 'skills/**/*.test.mjs'` and the anchored shell-test sweep over `hooks/` and `skills/` green at the tip carrying all changes; the phase's - `git diff --name-only ...` file list equals exactly the - Phase-1 `Files:` union of this plan (each worker threads its task's name-only diff into its - done report; gate-audit re-derives the union at the integrated tip). The expected-untouched - surfaces stay byte-identical unless a survey finds a straggler: `workflow-template.js`, - `land-decision.mjs`, every floor script, and `hooks/validate-auditor-git.test.sh`. + `git diff --name-only ...` file list is a **subset** of + the Phase-1 `Files:` union of this plan — **no file outside the union**, which is the + collateral-drift property this condition exists to prove — and its **only** permitted + absentee is `hooks/validate-auditor-git.test.sh`, Task 1.5's declared straggler-home slot, + which is expected byte-untouched and therefore expected NOT to appear in the diff (each + worker threads `git diff --name-only ...HEAD` into its done report — + the frozen phase base for wave-1 tasks, the rebased integration tip for `deps` tasks, so a + deps task's report shows its **own** footprint, never the union of its ancestors'; + gate-audit re-derives the + union at the integrated tip and applies exactly this subset-plus-named-absentee predicate — + where `` is the phase **integration-branch tip at the post-merge + gate-audit**, before Land and before any `docs(learnings): phase N` commit, whose + `docs/learnings/` paths sit outside every task footprint by design and would false-RED a + working-branch reading). + Equality is deliberately NOT the predicate: `hooks/validate-auditor-git.test.sh` is in the + union only so a survey-found straggler comment has an in-footprint home, so on the plan's + own expected path (no straggler) an equality check would red the phase on a non-defect. + The expected-untouched surfaces stay byte-identical: `workflow-template.js`, + `land-decision.mjs`, and every floor script **unconditionally** (outside every task + footprint — a straggler there is reported `war-followup`, never edited); + `hooks/validate-auditor-git.test.sh` unless a survey finds a straggler comment (the one + in-footprint straggler home, Task 1.5's). 10. Release lands last: all four version slots in lock-step at the next free patch above the live integration base; `version-slots.test.mjs` green. + 11. T2.9 census accuracy (campaign carry-over from plan 1): in + `skills/war/assets/provision-worktrees.test.sh`, the T2.9 block-comment census no longer + asserts a uniqueness/universality claim about exit-3 routes — the strings `only SILENT` + and `every one of which` both grep empty from that comment — while the **replacement + invariant sentence** stays **count-free** and greps **zero** for `T2\.` — + **sentence-scoped, exactly as plan 1's End state 4 scoped it**; the key is neither file-wide + (90 matching lines at base) nor census-block-wide (3, one of them the byte-pinned `(b)` + line), and either wider reading makes this End state and floor (iii) jointly unsatisfiable. + **The region is structural and worker-independent** (operator-adjudicated, red-team round + 5): the census comment lines strictly **between the block's first bare `#` + paragraph-break line and its `# (b) ` detail line** (the block has exactly one bare `#` + at base, so first = last there; "first" is immune to any break a reword adds inside the + replacement), located from the + `PAIR9="$(setup_origin_pair)"` fixture anchor Task 1.6 already names — at base that region + is exactly the invariant sentence, and after the edit it is exactly what the worker wrote + there. It is never derived from an opening literal (the sentence being replaced opens + `Exit 3 is shared by`; pinning that phrase would either forbid the reword this task exists + to perform or evaporate the moment the worker rewords) and never taken from the done + report's quote alone (a worker-chosen quote could omit part of the landed sentence — the + worker still quotes the replacement sentence verbatim in the done report as evidence, and + the auditor reconciles that quote against this structural region). The `(b)`/`(c)`/`(d)` + detail lines and all assertion code are + **byte-unchanged**, and the + replacement sentence remains truthful against `cmd_land_advance` as written (which has two + silent bare-`exit 3` arms: the push-error branch and the post-push origin-readback + mismatch). `bash skills/war/assets/provision-worktrees.test.sh` stays green. ## Build order (for /war) -1. **Phase 1 — Standing-record truth sweep + doctrine locks** (waves: 1.1 ∥ 1.4 ∥ 1.5, then - 1.2 ∥ 1.3 with `deps: [1.1]` — all five tasks file-disjoint) +1. **Phase 1 — Standing-record truth sweep + doctrine locks** (waves: 1.1 ∥ 1.4 ∥ 1.5 ∥ 1.6, + then 1.2 (`deps: [1.1]`), then 1.3 (`deps: [1.1, 1.2]`) — all six tasks file-disjoint. + 1.3 is its own third wave because its both-arms presence lock (c) asserts against the + `skills/war/SKILL.md` bullet 1.2 rewrites; run in the same wave as 1.2 it would be born RED) 2. **Phase 2 — Release** (trailing, own phase) ## Phase 1 — Standing-record truth sweep + doctrine locks @@ -122,7 +269,11 @@ Source spec: `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design - Files: `skills/war/references/schemas.md`, `skills/war-review/SKILL.md` - Plan slice: **Ledger contract (spec §4.1, one shape correction — see Notes).** In the - `## ledger.json — run state` jsonc block, add one top-level key, sibling of `phases` / + `## ledger.json — run state` jsonc block (heading **prefix** — the live heading continues + `at `.claude/teams//``; every anchor on either schemas.md heading in this plan is a + prefix match, never an exact-line match, and the same holds for `## Run manifest`, whose live + heading continues `— `.claude/war/runs/.json` (telemetry, not resume state)`), add one + top-level key, sibling of `phases` / `pr_url?`: `adjudications?` — an array carrying the rows **verbatim as threaded** in either args-contract shape (preformatted string or `{ adjudicated|value, supersedes }` object; the spec's jsonc sketch says "row strings", but its own parenthetical — "the same rows threaded as @@ -149,9 +300,19 @@ Source spec: `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design transcript-derived split values best-effort/possibly-undercounting** (the incident's own calibration: transcripts undercount tool calls ~20× against the envelope — a split presented unqualified beside envelope totals would invite exactly the false cross-sum the never-fabricate - rule exists to prevent). (2) §3 (Tally): the "total tool calls" and "total tokens" rows' Source - cells become "manifest `phases[].envelope`, else mined (transcripts)"; the split qualifier - stays mined-only. (3) §4 (Friction): add one signal class — **unfinalized phase record**: a + rule exists to prevent). (2) §3 (Tally) — **read End state 4 before editing; §3 has NO + `total tokens` row today and this edit is a row SPLIT, not two cell rewrites.** The live table + carries `| total tool calls | mined (transcripts) |` and ONE combined row whose metric label + *is* the split: `| total tokens — input / output / cache (split when available) | mined + (transcripts) |` (quote that label verbatim as your anchor). Do two things: set the **total + tool calls** row's Source cell to "manifest `phases[].envelope`, else mined (transcripts)"; + and **split the combined row into two** — `| total tokens | manifest `phases[].envelope`, else + mined (transcripts) |` and `| token split — input / output / cache | mined (transcripts), + `n/a` when unsourceable |`. Rewriting the combined row's Source cell in place instead would + claim envelope sourcing for a split the spec pins as transcript-mined-only, and End state 4's + required second row would never exist — nothing else catches it (`requiresTest:false`, no + `/war-review` prose lock, and the `manifest` keyword backstop is satisfied by the drifted row). + (3) §4 (Friction): add one signal class — **unfinalized phase record**: a phase whose record lacks `endedAt`, `tasks`, or `land` **although the run ended or a later phase started** — evidence the phase-close stamp was skipped. That conditional clause is the deliberate killed-run discriminator: a run that died mid-phase leaves run `endedAt` null and no @@ -162,8 +323,17 @@ Source spec: `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design and reconcile every claim about what the manifest carries with the widened contract; then hand-scan the §3 tally rows and the `## Scavenge` section for indirect "never carries" paraphrases the keyword grep misses, listing each straggler as a survey-derived correction in - the done report. The `land-decision.test.mjs` doc-parity rows that read `schemas.md` extract - only the `landDecision` enum line and per-value bullets — neither block is touched here. + the done report. **`land-decision.test.mjs` reads `schemas.md` at FIVE sites, not two — run + `node --test 'skills/**/*.test.mjs'` locally before you commit and do not assume this file is + out of scope.** Two are the doc-parity extractions (the `landDecision` enum line and the + per-value bullets — genuinely untouched here), but three more reach the blocks this task edits: + a second `landDecision` enum-line read in the D9 helper, a **task-status enum anchor that sits + INSIDE the `## ledger.json — run state` jsonc block you are editing**, and D9's enum-leak + detector, which is fed the **entire file**. So both edited blocks are in D9's scan scope: keep + the new `adjudications` / `envelope` prose free of equality-shaped or label-shaped examples + (`status:landed` label form, `landDecision === 'merged'` equality form) that would trip its + patterns — colon-space-quote object literals like `status: "landed"` are the test's own + documented non-trip carve-out, neither equality nor label shape. - requiresTest: false — docs-tier contract + consumer prose; the doc-contract lock guarding the new ledger key arrives with Task 1.3 (no-test route recorded here for the floor) - requiresPackaging: false @@ -178,7 +348,10 @@ Source spec: `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design "Assemble, thread, and record the run's adjudication set" step), extend "record each row in the run ledger" with the parenthetical citation to the `ledger.json` top-level `adjudications` key in `references/schemas.md`. **(b) Two-path `held:land-failed` truth (spec §4.2).** In the - `## Checkpoint` Outcome-handling list, locate the `- **`held:land-failed`**` bullet's sentence + `## Checkpoint` Outcome-handling list, locate the `held:land-failed` bullet by its + **2-space-indented, token-only prefix** `/^ {2}- \*\*`held:land-failed`/` (End state 2's + locator; the compact `` - **`held:land-failed`** `` wrap has zero occurrences in this file — + never anchor on it here) and within it the sentence beginning "A **gate-time `environment` failure that reaches this hold has already spent…" and replace it with the two-path form — primary-land arm: retry spent (the bounded fresh-env re-land came back `environment`-classified a second time — see the `gate_failed` routing bullet @@ -216,7 +389,13 @@ Source spec: `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design (`totalTokens` / `totalToolCalls` / `agentCount`) to the stamp list, sourced from the Workflow task-completion notification's envelope — the same harness-surfaced channel the **At phase launch** bullet already reads `workflowRunId`/`transcriptDir` from; unsurfaced ⇒ `null`, - `/war-review` renders `n/a`. The stamp applies on **every** phase return — `held:*` included + `/war-review` renders `n/a`. **Same edit, same section: the file's own MUST-carry mirror + sentence** — the standalone line `Field names follow spec §4.A (nesting may be refined; the + **MUST-carry** set is binding): **per phase** — …` — gains the envelope aggregates in its + per-phase list, so `skills/war/SKILL.md`'s binding-set summary and `schemas.md`'s MUST-carry + list move in lock-step (landing the schemas.md widening without this sentence would create a + fresh two-record MUST-carry divergence — the exact defect class this plan exists to remove, + and no doc-contract lock reads either MUST-carry sentence to catch it). The stamp applies on **every** phase return — `held:*` included (the record's `land` field already carries the hold; a held phase without its stamp is exactly the unfinalized record the friction signal exists to catch). The envelope field names (`totalTokens` / `totalToolCalls` / `agentCount`) are the incident run's observed @@ -231,13 +410,22 @@ Source spec: `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design "unfinalized phase record" friction row `/war-review` reports; fail-open discipline unchanged (a failed write logs one line and never blocks the advance). **(e) Doctrine framing (spec §4.5).** In step 5's **Provenance discipline** sentence, after "the Lead never - synthesizes a row to smooth over an unruled delta (spec constraint 6)", add "and never mines - one from arbitrary prose — rows come only from the two producers above". **Token sweep (floor, + synthesizes a row to smooth over an unruled delta (spec constraint 6)", add — **verbatim, the + literal phrasing is load-bearing** — "and a row is **never mined from arbitrary prose** — rows + come only from the two producers above". Do NOT paraphrase it as "never mines one from + arbitrary prose": End state 5's wrap-tolerant census, Task 1.3's classification list, and spec + §3's "same literal framing" row all key on the literal token `mined from arbitrary prose`, and + the paraphrase contains `mines one from` instead — which would make this expected census home + return **zero** hits under every form (single-line, case-insensitive, and `\s+`-tolerant + alike), silently falsifying End state 5 while every other check stayed green. **Token sweep (floor, not ceiling):** grep `already spent` across `skills/war/` and `agents/` and adjudicate every - match against the two-path truth — known matches at drafting: the target sentence (fixed + match against the two-path truth — known **grep matches** at drafting (pre-change hit count + **2**): the target sentence (fixed here); `workflow-template.js`'s ace-demotion string "the single ace attempt is already spent" - (a different retry budget — leave, and the engine file is out of footprint regardless); the - `environment` bullet's "the retry provably spent" (correct — keep). Then hand-scan the full + (a different retry budget — leave, and the engine file is out of footprint regardless). The + `environment` bullet's "the retry provably spent" is a **hand-scan keep, not a grep match** — + it does not contain the string `already spent` (correct for its exhaustion path — keep). Then + hand-scan the full Outcome-handling list and the `### Recovery relaunch` / runbook subsections for paraphrase echoes of the unconditional claim the grep cannot catch, adjudicating each and listing every straggler edited as a survey-derived correction in the done report. A straggler in a file @@ -268,7 +456,8 @@ Source spec: `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design spec-truth rows pin only the 2026-06-25 and 2026-07-12 specs — so the annotative note cannot red anything; re-confirm with a cheap grep for the spec's filename over `skills/` and `hooks/` test files before commit.) **Drift locks - (two new tests in `skill-doc-contracts.test.mjs`, house style: construct-anchored extraction, + (three new tests in `skill-doc-contracts.test.mjs` — (a), (b), and (c) below, matching End + state 6 and this task's `requiresTest` line; house style: construct-anchored extraction, root resolved from `import.meta.url` never `process.cwd()`, maintenance-rule header respected).** (a) Read `CONTEXT.md` (repo root, resolved relative to the test file the same way the existing spec-truth reads resolve `docs/specs/`), extract the `**Adjudication**:` block by @@ -280,28 +469,71 @@ Source spec: `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design loud assertion (the existing rows' could-not-locate pattern). (b) Sibling lock guarding Task 1.1's contract fix: extract the `## ledger.json — run state` jsonc block from `skills/war/references/schemas.md` - (heading to the block's closing fence) and assert it names `adjudications`. Prove both locks - red by temporarily removing each referent (red-proof recorded in the commit body, matching the - file's house style; per End state 3-style done-report threading, quote the failing output). + (locate the heading by **prefix** — the live line continues `at `.claude/teams//`` — + then extract to the block's closing fence; an exact-line match finds nothing) and assert it + names `adjudications`. Prove locks **(a) + and (b)** red by temporarily removing each referent (red-proof recorded in the commit body, + matching the file's house style; quote the failing output in the done report — the + done-report-evidence convention End states 7 and 9 use); lock **(c)** carries its own + red-proof below. Every existing row — D10, D18, the spec-truth reads — passes with extraction constructs untouched. **Token sweep (floor, not ceiling — wrap-tolerant):** census the phrase repo-wide with a whitespace-tolerant form that crosses line breaks (e.g. per file - `tr '\n' ' ' | grep -c 'mined from arbitrary prose'`, or a `\s+`-separated multiline match) — - the lesson body carries one occurrence wrapped across a line break that a single-line grep - silently drops, the misattribution-pairing defect class exactly. Classify every hit — + `tr '\n' ' ' | grep -oi 'mined from arbitrary prose' | wc -l` — case-insensitive to match + lock (a), and `-o … | wc -l` because on a single joined line `grep -c` caps at 1 and + undercounts a multi-occurrence file — or a `\s+`-separated case-insensitive multiline match) — + the lesson body `docs/learnings/spec-non-goal-citation-of-a-doctrines-home-file-can-be-wrong.md` + carries **four** occurrences of which **two** wrap across a line break, so a single-line grep + reports 2 and silently drops half — the misattribution-pairing defect class exactly. **Enumerate + and classify every hit; never match a count** (these numbers are the base-state rationale for + using a wrap-tolerant form, not a floor to assert). Classify every hit — CONTEXT.md term (standing home, this task's edit), - `skills/war/SKILL.md` step 5 (standing home, Task 1.2's edit — adjudicate fixed-in-flight by - the sibling, same phase), the 2026-07-22 spec (this task's correction-note site), the learnings - lesson (leave — lesson bodies are records), the new locks themselves (own). Then hand-scan the + `skills/war/SKILL.md` step 5 (standing home, written by Task 1.2 and **present at this task's + rebased base** via the `deps: [1.1, 1.2]` edge — leave), the 2026-07-22 spec (this task's + correction-note site), the learnings + lesson (leave — lesson bodies are records), **this plan and its source spec + (`docs/plans/2026-07-24-runbook-and-standing-record-coherence.md`, + `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design.md` — planning artifacts + quoting the doctrine to specify it; leave)**. The new locks are deliberately **not** census + homes — lock (a) writes the phrase `\s+`-separated per its own floor, invisible to the census + by construction (End state 5 says the same). Then hand-scan the CONTEXT.md `Adjudication` term body and SKILL.md step 5 for paraphrase echoes ("synthesizes a row", "ruling already made and routed") to confirm the restored clause composes with rather than duplicates them, listing any straggler adjusted as a survey-derived correction. -- requiresTest: true — the deliverable includes the two new locks; the diff touches + **(c) BOTH-ARMS PRESENCE lock backing End state 2 (the plan's only committed guard for it).** + Extract the `held:land-failed` Outcome-handling bullet from `skills/war/SKILL.md` **exactly the + way the live guard already does it** — copy the construct at `land-decision.test.mjs`: locate + with `/^ {2}- \*\*`held:land-failed`/` (a **2-space-indented, token-only prefix**; the live + header is a phrase-wrapping bold — `` - **`held:land-failed` — root-cause-branched + auto-recover, else hold.**`` — so the compact `` - **`held:land-failed`** `` form has **zero** + occurrences in `SKILL.md`; that compact wrap is *schemas.md*'s header form, never anchor on it + here) and terminate at the next **SAME-INDENT** `/^ {2}- \*\*/` sibling, never a top-level + `/^- \*\*/` one (a top-level terminator over-extends past the bullet's nested sub-bullets and + the entire `- **Escalation-completion land …**` sibling). Then assert the extracted region matches **both** + arm markers — a primary-land arm and a baseline-proceed arm — per End state 2's two-path + requirement. + **Do NOT write this as a `/already\s+spent/i` absence key.** The spec's own sanctioned §4.2 + replacement text keeps "already spent" inside the *conditional* primary-land arm (the retry + spent because the bounded fresh-env re-land came back `environment`-classified a second time), + so an absence key would go RED on the correct two-path text and could pass only by the worker + dropping sanctioned prose. The defect End state 2 targets is the **unconditional framing**, not + the token — so the lock asserts the two-path shape positively. + Red-proof: temporarily collapse the bullet back to the single unconditional sentence and + confirm exactly (c) reds. + Rationale for living here rather than in Task 1.2: `skill-doc-contracts.test.mjs` is this + task's file, and putting the lock in 1.2 would make two tasks edit one file — the same-file + collision the decomposition rule forbids (never a deps/wave dodge; this is a genuine + cross-file dependency). +- requiresTest: true — the deliverable includes the three new locks; the diff touches `skill-doc-contracts.test.mjs`, satisfying the test floor - requiresPackaging: false -- deps: [1.1] — lock (b) asserts the `adjudications` key Task 1.1 writes into `schemas.md`; the - wave edge rebases this worker onto the tip carrying it, so the lock is born green off its own - dispatch base (a conscious placement deviation — see Notes) +- deps: [1.1, 1.2] — lock (b) asserts the `adjudications` key Task 1.1 writes into `schemas.md`, + and lock (c) asserts **both arms are PRESENT** in the `held:land-failed` bullet Task 1.2 + rewrites in `skills/war/SKILL.md`; both wave edges rebase this worker onto the tip carrying + those edits, so each lock is born green off its own dispatch base (a conscious placement + deviation — see Notes). Without the 1.2 edge, lock (c) would be born RED — the pre-1.2 bullet + carries only the single unconditional sentence, so the baseline-proceed arm it requires does + not exist yet. - target repo: superproject ### Task 1.4: Campaign roadmap record — row 6 Files cell + CONTEXT.md contention row (#1053) @@ -325,19 +557,48 @@ Source spec: `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design the guard byte-untouched (no flag added to or removed from the branch arm's accepted set). In the READ-FORM branch-enforcement arm (the `if [ "$subcmd" = "branch" ]` block): **Deny message** — replace the "takes only =-attached read flags" blanket characterization with - one matching the enumeration's mixed shapes: value-carrying flags must be `=-attached` - (`--contains=`, `--merged=`, `--points-at=`) and bare read flags are enumerated - (`--list`, `-a`, `-r`, `--show-current`, `-v`); the offending token is named; space-form values - and write flags deny. Wording is worker latitude within three floors, and the floors are the + one matching the enumeration's mixed shapes. **Enumerate the accepted set as the case arm + actually admits it — the live deny string is itself partial (3 of 6 value flags, 5 of 9 bare + tokens), and reproducing that partial list is re-committing the very defect this task removes.** + Read the two `case` patterns at your base and transcribe them: value-carrying flags must be + `=-attached` — `--contains=`, `--no-contains=`, `--merged=`, `--no-merged=`, `--points-at=`, + `--sort=` (**six**) — and bare read flags are enumerated — `--list`, `--all`, `-a`, `--remotes`, + `-r`, `--show-current`, `--verbose`, `-v`, `-vv` (**nine**); the offending token is named; + space-form values and write flags deny. If the case arms at your base differ from these fifteen + tokens, the arms win — transcribe what you read and say so in the done report. Wording is worker + latitude within the numbered floors below, and the floors are the audit rubric (the plan-faithfulness seat judges against them; latitude beyond is the worker's, - never re-adjudicated as drift): the literal `=-attached` survives (it is J16's pinned - micro-teach and stays accurate for the value-carrying flags); no blanket adjective covers the - bare-flag set; and the message stays **one line, no embedded newlines** — `deny()` echoes a - single stderr line and the J-series substring greps assume line-local text (length itself is - unconstrained; a longer honest line beats a short false one). **Header comment** — correct + never re-adjudicated as drift): **(i)** the literal `=-attached` survives (it is J16's pinned + micro-teach and stays accurate for the value-carrying flags); **(ii)** no blanket adjective + covers the bare-flag set; **(iii) token-set equality — the set of flag tokens named in the new + message equals, exactly, the union of the two `case` arms' `|`-split patterns.** Derive both + sides mechanically at your base rather than trusting any list (including this plan's), under + this stated derivation (operator-adjudicated, red-team round 5): **accepted set** = the two arm + patterns split on `|`, each token's trailing `*` stripped (`--contains=*` → `--contains=` — + the arm tokens are shell globs; no honest message carries a literal `*`); **named set** = every + flag-shaped token in the message, matched only at a start-of-string/space/`(`/`,` boundary — + so the floor-(i) literal `=-attached` yields no token — where **a token runs from that + boundary to the first character outside `[A-Za-z0-9=<>_-]`** (trailing sentence punctuation — + `,` `;` `)` `.` — is never part of the token, so an honest flag abutting a `)` or `;` still + extracts clean), and **everything after a token's `=` is discarded to leave the bare `=`** + (`--contains=` → `--contains=`; a concrete illustrative value collapses identically — + `--sort=refname` → `--sort=` — placeholder or value alike); compare the two sorted sets and assert + they are **equal** — neither side may carry a token the other + lacks. **Scope is the whole one-line message**: the deny string therefore names no denied + token as a counter-example — denied-shape teaching lives in the header comment, never the deny + string. **Do not check this with a bare per-token substring grep: `-a` is a substring of `--all`, + `-r` of `--remotes`, and `-v` of both `--verbose` and `-vv`, so a message naming only the long + forms passes a substring check while omitting three tokens** — the partial-enumeration defect + sailing through the floor built to stop it. Quote both derived sets in the done report; **(iv)** + the message stays **one line, no embedded newlines** — `deny()` + echoes a single stderr line and the J-series substring greps assume line-local text (length + itself is unconstrained; a longer honest line beats a short false one). **Header comment** — + correct "EVERY token must be an enumerated read flag with =-attached values" to the mixed-shape truth (value-carrying - flags `=-attached`; bare read flags enumerated exactly). **Token sweep (floor, not ceiling):** + flags `=-attached`; bare read flags enumerated). **Do not write "exactly" (or any other + completeness adjective) unless the adjacent list is in fact the complete fifteen** — an + overclaiming completeness adjective is the same misdescription defect in a new coat. **Token sweep (floor, not ceiling):** grep `=-attached` across `hooks/` and adjudicate every match — the deny string (reworded here), the header comment (corrected here), and the test file's J7/J16 comments (verified accurate at drafting: they describe genuinely `=-attached` flags — leave). Then hand-scan the @@ -354,6 +615,72 @@ Source spec: `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design - deps: [] - target repo: superproject +### Task 1.6: T2.9 census accuracy — drop the uniqueness overclaim (campaign carry-over from plan 1) + +- Files: `skills/war/assets/provision-worktrees.test.sh` +- Plan slice: **comment text only** — every assertion, fixture, and case in the file is + byte-untouched; this task adds and removes no test. Campaign carry-over: plan 1 + (`2026-07-24-land-advance-exit-contract-truth`, Task 1.2) rewrote T2.9's block-comment census + count-free, and the mandated replacement wording asserted that the push-error branch is + "the **only** SILENT exit-3 route", with a universal lead-in that "every one of which dies + LOUDLY with route-naming text". Two auditor seats independently code-traced both claims FALSE + and dispositioned them `note` (not `absorb`) solely because the wording was plan-mandated; the + operator ratified routing the correction here. **The defect:** `cmd_land_advance` has a + **second** silent bare-`exit 3` arm — the post-push origin-readback mismatch + (`[ "$actual" = "$new_sha" ] || exit 3`, on the push-**success** path) — distinct from the + push-error branch on the push-failure path. Neither "every … dies LOUDLY" nor "the only SILENT" + is true. **The fix:** in the T2.9 census block comment (anchor: the block comment immediately + above the `PAIR9="$(setup_origin_pair)"` fixture line — anchor by that named construct, never a + line number), replace the invariant sentence with a truthful, still-count-free one. Auditor- + suggested shape, and worker latitude within the floors below: *"Exit 3 is reached by several + routes; the push-path silent ones (the push-error branch and the post-push origin-readback + mismatch) print nothing, while the rest die LOUDLY with route-naming text — so route identity + rests on (b)+(c)+(d) TOGETHER:"*. **Floors (the audit rubric — latitude beyond them is the + worker's, never re-adjudicated as drift):** (i) the **replacement invariant sentence** — + structurally delimited as the census comment lines strictly **between the block's first bare + `#` paragraph-break line and its `# (b) ` detail line** (exactly one bare `#` exists at + base, so first = last there; "first" survives any break your reword adds), both located from + the + `PAIR9="$(setup_origin_pair)"` fixture anchor above (at base that region is exactly the + invariant sentence; after your edit it is exactly what you wrote there), *excluding* the + `(b)`/`(c)`/`(d)` detail lines that floor (iii) pins byte-unchanged — greps **zero** for + `T2\.`. The region is deliberately **worker-independent** (operator-adjudicated, red-team + round 5): no opening literal is pinned (you are rewording this sentence — any phrase anchored + from the old text would either forbid the reword or vanish with it; the suggested shape below + already opens `Exit 3 is reached by`, not the current `Exit 3 is shared by`), and the scanned + region is never taken from your done-report quote (a partial quote must not narrow the scan). + **Still quote the replacement sentence verbatim in your done report as evidence** — the + auditor reconciles that quote against this structural region + (plan 1's End-state-4 count-free floor, preserved at plan 1's scope: **sentence**, never the + census block and never the file, both of which match `T2\.` at base and would report a false + RED); (ii) the claims are gone under a **case-insensitive, wrap-tolerant** check — normalize + the comment first with the continuation marker stripped **before** the lines are joined + (`sed 's/^[[:space:]]*#[[:space:]]*//' | tr '\n' ' ' | tr -s ' '`; a bare + `tr '\n' ' '` leaves the `# ` mid-phrase and a re-wrapped claim still evades it) and then + `grep -i` for **both** `only silent` **and** `every one of which`, plus a universality scan for + an `every … dies loudly`-shaped adjective that a re-phrasing could reintroduce under different + words. Both keys are line-resident at base (pre-change hit count **1** each, under every + normalization) — that is what makes them non-vacuous; the normalization buys tolerance to a + *future* re-wrap, and a line-local case-sensitive pair is still NOT sufficient, because a + sentence-initial recasing or a re-wrap would report PASS on a paragraph that still carries both + false claims; (iii) the `(b)`, `(c)`, + `(d)` detail lines and **all** assertion code are byte-unchanged, and the census block gains + **no new `# (`-prefixed detail-label line** (the floor-(i) region's end boundary keys on the + byte-pinned `# (b) ` line — a second `(x)`-shaped label would make it ambiguous); (iv) the + sentence is truthful + against `cmd_land_advance` **as written at your base** — re-read the function and confirm the + two silent arms before writing, rather than trusting this slice's summary (that is precisely the + failure mode this task exists to correct); (v) no numeric count of exit-3 routes appears + (count-free means no "two"/"three" either — the file must not re-acquire a number that rots). + Also hand-scan the rest of the T2.9 comment block for any other universality claim the two greps + in (ii) miss. Verify: `bash skills/war/assets/provision-worktrees.test.sh` green (it must be — + no assertion moved). End state 11. +- requiresTest: false — comment-only; the existing T2.x suite passing with zero assertion edits + is the check (no-test route recorded here for the floor) +- requiresPackaging: false +- deps: [] +- target repo: superproject + ## Phase 2 — Release ### Task 2.1: Version bump — all four slots @@ -388,14 +715,54 @@ Source spec: `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design ## Deferred validations (backstops) -- Integrated-tip sweep re-check — re-run the four token sweeps (`already spent` over - `skills/war/` + `agents/`; `manifest` over `skills/war-review/SKILL.md`; the **wrap-tolerant** - doctrine-phrase census repo-wide; `takes only =-attached` over `hooks/`) once on the - landed Phase-1 tip and confirm End states 2, 4, 5, 7 hold after the serial merge · why - deferred: sweep completeness is a whole-repo property spanning five parallel tasks that each - adjudicate at their own frozen base — the cross-task fixed-in-flight rulings (Task 1.3 - classifying Task 1.2's step-5 edit) are only provable on the integrated union · runner: the - Lead at Phase-1 land, before dispatching Phase 2. +- Integrated-tip sweep re-check — re-run the **five** token sweeps once on the landed Phase-1 + tip. **Every key is case-insensitive (`grep -i`), and every key over a wrapped-prose surface is + wrap-tolerant — a line-local case-sensitive grep is vacuous against a re-wrap or a + sentence-initial recasing, which is the same wrap-blindness this plan corrects in End states 5 + and 7. On a `#`-comment surface, wrap-tolerance requires stripping the continuation marker + *before* joining lines — `sed 's/^[[:space:]]*#[[:space:]]*//' | tr '\n' ' ' | tr -s ' '` + — because a bare `tr '\n' ' '` leaves the `# ` sitting mid-phrase and the joined text still + fails to match. (Key taxonomy — three kinds, not one: **(4) and (5) are absence keys** — they + must return nothing after the edit, and their stated pre-change counts prove non-vacuity; + **(3) is an enumeration/census**, whose wrap-tolerance is load-bearing **today** — two of the + lesson body's four occurrences already wrap at base, so a single-line form under-reports + before any edit; **(1) and (2) are adjudicated sweeps** that keep legitimate matches + post-change — (1) keeps the ace-demotion string; (2)'s `manifest` census pins no count (the + file says `manifest` legitimately throughout) while its never-carries absence sub-key pins + pre-change count **1**.)** The five: + (1) `already spent` over + `skills/war/` + `agents/` (pre-change hit count **2**; the `environment` bullet's "the retry + provably spent" is a hand-scan keep, not a grep match); (2) `manifest` over + `skills/war-review/SKILL.md` — an adjudicated census, plus End state 4's wrap-tolerant + `never carries` absence re-check over the same file (pre-change count **1**); (3) the + wrap-tolerant doctrine-phrase census repo-wide; (4) over `hooks/`, both absence keys + `takes only =-attached` **and** `an enumerated read flag with =-attached + values` — the latter under the `#`-comment normalized form above, because Task 1.5's own + deliverable rewrites that wrapped header comment and a line-local grep goes green on any + re-wrap that keeps the claim (**not** `EVERY token must be an enumerated read flag` — that + phrase wraps mid-sentence + across the live header comment's two lines with a `# ` continuation marker inside it, so it + any line-local form of it matches zero times even before an edit — rejected as a line-local + key, not as inherently unmatchable: the normalized form above finds it once, and key (2) + discriminates under every form); and (5) over + `skills/war/assets/provision-worktrees.test.sh`, `only silent` / `every one of which` + (each pre-change hit count **1**, line-resident) plus `T2\.` **scoped to the replacement + invariant sentence only** — never the file (90 matching lines at base) and never the whole T2.9 + census block (3, one of them the byte-pinned `(b)` detail line); a file-wide or block-wide + reading of this key reports a false RED on a correctly-executed Task 1.6. **The region is + structural, worker-independent** (operator-adjudicated, red-team round 5): the census comment + lines strictly between the block's **first** bare `#` paragraph-break line and its `# (b) ` + detail line (exactly one bare `#` at base, so first = last there; "first" survives any break + the reword adds), located from the `PAIR9="$(setup_origin_pair)"` fixture anchor — never derived + from an opening literal (the reword may legitimately have removed any old phrase) and never + taken from the done report's quote alone (reconcile Task 1.6's verbatim quote against this + region; a partial quote must not narrow the scan). Confirm + End states 2, 4, 5, 7, **11** hold after the serial merge · why + deferred: sweep completeness is a whole-repo property spanning **six Phase-1 tasks across + three waves**, each adjudicating at its own frozen dispatch base — only the integrated union + proves every task's keys simultaneously, e.g. Task 1.6's absence floors can be re-broken by + any later-landing sibling that re-enters + `provision-worktrees.test.sh` · runner: the Lead at Phase-1 land, before dispatching Phase 2. - "Unfinalized phase record" friction signal fires in practice — a future run whose Lead skips the phase-close stamp shows the new §4 row in its review · why deferred: the signal's trigger is a *future* run's manifest, unreachable from this plan's tree; the in-phase checkable floor @@ -404,10 +771,16 @@ Source spec: `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design ## Notes / conscious deviations -- **Decomposition:** five Phase-1 tasks, pairwise file-disjoint. The two `deps` edges - (1.2 ← 1.1, 1.3 ← 1.1) are real cross-file dependencies — prose citations and a test lock - naming constructs Task 1.1 writes — never a same-file dodge (the shared-file rule: all five - SKILL.md edits are one task, all schemas.md edits another). Release is its own trailing phase +- **Decomposition:** six Phase-1 tasks, pairwise file-disjoint. The **three** `deps` edges + (1.2 ← 1.1, 1.3 ← 1.1, **1.3 ← 1.2**) are real cross-file dependencies — prose citations and + test locks naming constructs Task 1.1 writes, plus the **lock-(c) ordering edge**: 1.3's + both-arms presence lock asserts against the `skills/war/SKILL.md` bullet 1.2 rewrites, so 1.3 + is its own third wave and would be born RED if run alongside 1.2 — + never a same-file dodge (the shared-file rule: all five + SKILL.md edits are one task, all schemas.md edits another). Task **1.6** is the plan-1 campaign + carry-over; it is `deps: []` and owns `skills/war/assets/provision-worktrees.test.sh`, a file no + other task in this plan touches, so it rides the first wave alongside 1.1 ∥ 1.4 ∥ 1.5 with no + rebase-conflict surface. Release is its own trailing phase per the rule. Tasks are carved on **file boundaries, not issue boundaries** — deliberately: issue-per-task would put five issues' edits in the same two files and guarantee serial-merge rebase conflicts. Issue → task closure map (so the Lead's Checkpoint duty and the campaign @@ -415,7 +788,25 @@ Source spec: `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design close-each-task-issue rule; the seven **source** issues close against this map when the campaign PR lands): #1016 → 1.1 + 1.2 (+ 1.3's lock (b)); #1039 → 1.2; #1053 → 1.4; #1078 parts 2–3 → 1.1 + 1.2 (part 1 overtaken, no task — closes on the spec's - overtaken-by-v0.14.49 rationale); #1084 → 1.2; #1085 → 1.5; #1087 → 1.2 + 1.3. + overtaken-by-v0.14.49 rationale); #1084 → 1.2; **#1085 → 1.5 PARTIAL — DO NOT CLOSE on this + plan's PR**; #1087 → 1.2 + 1.3. + **#1085 partial-closure note (red-team finding, adjudicated — corrected in round 2).** The same + blanket "`=`-attached read flags only" characterization is live on **three** surfaces, and Task + 1.5 corrects only one: `hooks/validate-auditor-git.sh` (this plan). The other two — + `agents/war-auditor.md`'s standing `` **`branch` takes `=`-attached read flags only** `` bullet + and the mirrored `READ-ONLY GIT GUARD CONTRACT` clause in `workflow-template.js`'s dispatched + auditor prompt — are **out of footprint here** (spec constraint 1 pins `workflow-template.js` + byte-untouched). **They are NOT picked up by campaign plan 4 either.** Plan 4 + (`docs/plans/2026-07-24-drift-guard-and-floor-diagnostic-hardening.md`) explicitly disclaims + them: its design row 7 is spec-ratified that the hook deny string and both mirrors must move + together, it owns neither the hook nor that sentence family, and its End state 5 pins **no** + `workflow-template.js` edit. Its named vehicle is a **Lead-filed `war-followup` issue at its + Phase-1 close**, naming `agents/war-auditor.md` and the `workflow-template.js` dispatched + clause. So **#1085 closes on NEITHER plan's PR** — it stays open behind that follow-up. (The + two mirrors travel together in one commit per the standing split rule: a change to auditor + behavior updates the standing `agents/*.md` doc and the string-built dispatched prompt in the + same commit, or they drift silently.) This plan's PR body cites #1085 as partially addressed + and must **not** use a closing keyword for it. - **Ledger row-shape correction (conscious deviation from the spec's jsonc sketch):** spec §4.1 sketches the `adjudications` key as "preformatted row strings", but the args contract it cites admits string **or** `{ adjudicated|value, supersedes }` object rows, and the spec's own @@ -424,10 +815,13 @@ Source spec: `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design reappearing one layer down. Resolved toward the checkable pair (the args contract + the re-thread promise): the ledger key documents rows verbatim-as-threaded in either shape. - **No `/war-review` prose lock (self-decided, grill):** the §2/§3/§4 edits stay grep-checked - (End state 4) rather than test-locked — the spec's design tree deliberately chose exactly two - new locks, a third would add contention on `skill-doc-contracts.test.mjs` (which a sibling - campaign spec also edits) for a low-blast-radius prose surface no test has ever pinned, and - the integrated-tip backstop re-checks the greps at land. + (End state 4) rather than test-locked. The spec's design tree chose two new locks; **this plan + carries three** — red-team round 1 added lock (c) (the End state 2 both-arms presence guard) to + close an End state that otherwise had no committed guard, and Task 1.3 owns all three. A + **fourth**, on `/war-review` prose, is where the line is drawn: it would add further contention + on `skill-doc-contracts.test.mjs` (which a sibling campaign spec also edits) for a + low-blast-radius prose surface no test has ever pinned, and the integrated-tip backstop + re-checks the greps at land. - **Split stays transcript-mined but caveated (self-decided, grill):** spec constraint 3 pins the split as transcript-mined-or-`n/a`; the plan adds the best-effort/undercount label to the rewritten §2 sentence (from #1078's own 13-vs-265 calibration) so the split is never read as @@ -438,11 +832,30 @@ Source spec: `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design `skill-doc-contracts.test.mjs` is a single file already owned by 1.3 (same-file → same-task beats guard-travels-with-fact here). The `deps: [1.1]` wave edge closes the gap: the lock is authored against a base already carrying the key, and both land in the same phase. -- **Campaign stacking (plan 2 of 6, ADR 0011 stack-and-plow):** Phase-1 footprint is fully - disjoint from plan 1's Phase-1 footprint (`docs/learnings/land-advance-push-first-cas-rejected-token.md`, - `docs/seed/seed-manifest.json`, `docs/seed/seed.tar.gz`, - `skills/war/assets/provision-worktrees.test.sh`, `docs/adr/0023-land-asserts-git-ground-truth.md`). - The only overlap is the trailing release phase's four slots (`plugin.json`, +- **Campaign stacking (plan 2 of 6, ADR 0011 stack-and-plow):** Phase-1 footprints intersect + **plan 1's** on exactly one file, by design: `skills/war/assets/provision-worktrees.test.sh`, + which plan 1's Task 1.2 edited and this plan's **Task 1.6** re-enters to correct the T2.9 census + wording plan 1 landed there. **That same file is also declared in plan 5's + (`gate-evidence-and-release-integrity`) footprint**, so the campaign-wide contention set for it + is **1, 2, 5** — three plans, all strictly sequential in stack order, different case families + (1: T2.5d + T2.9 census; 2: T2.9 census wording; 5: a new P-family refusal case), no content + dependency. **Known stale record:** the campaign roadmap + (`docs/roadmaps/2026-07-24-standing-record-and-guard-hardening-roadmap.md`) still records this + file against **1, 5** only — its row-2 Files cell, its `1 → 5` dependency line, and its + shared-file contention row all predate the Task 1.6 fold. Correcting the campaign roadmap is a + **campaign-Lead bookkeeping action, not a task in this plan** (the roadmap is a campaign + artifact, outside every task's declared footprint); it is recorded here so a gate-audit seat + re-deriving the footprint union from the roadmap reads the intersection as declared, not as + collateral drift, and so plan 5's provisioning does not inherit a stale contention set. The + intersection is **sequential, never concurrent** — plan 1 is fully landed at + this plan's base (phase merges `09e4969` and `9cd713f`, release `441855c`, Gate-2 `f9fc4a4`), so + Task 1.6 dispatches off a tree that already carries the strings its floors delete. That ordering + is load-bearing: dispatched off any base predating plan 1's land, Task 1.6's floor (ii) + (`only SILENT` / `every one of which` grep empty) would be **vacuously true** and the task would + land having corrected nothing. The rest of plan 1's Phase-1 footprint + (`docs/learnings/land-advance-push-first-cas-rejected-token.md`, `docs/seed/seed-manifest.json`, + `docs/seed/seed.tar.gz`, `docs/adr/0023-land-asserts-git-ground-truth.md`) is untouched here. + The other overlap is the trailing release phase's four slots (`plugin.json`, `marketplace.json` ×2, `README.md ## Status`) — expected and fine: both plans use the directive form, so each resolves its patch from the slots as they stand at its own land time. - **Cross-plan contention for the roadmap table:** this plan claims **stack position 2** (after @@ -490,5 +903,6 @@ Source spec: `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design ## Open decisions None — the spec's design tree resolved every decision; remaining latitude (4.2's exact two-arm -phrasing, 4.7's exact deny wording, the correction note's sentence shape) is the worker's within +phrasing, 4.7's exact deny wording, Task 1.6's replacement invariant sentence, the correction +note's sentence shape) is the worker's within the named-element floors stated per task. diff --git a/docs/red-team/2026-07-24-runbook-and-standing-record-coherence.md b/docs/red-team/2026-07-24-runbook-and-standing-record-coherence.md new file mode 100644 index 00000000..086415fe --- /dev/null +++ b/docs/red-team/2026-07-24-runbook-and-standing-record-coherence.md @@ -0,0 +1,133 @@ +# Red Team — docs/plans/2026-07-24-runbook-and-standing-record-coherence.md (2026-07-24) +**Verdict:** CLEARED-WITH-NOTES — seven rounds; final micro-round 0 blockers / 0 needsDecision / 3 Minors, all auto-fixed in the plan before this report. + +## Attack surface +Spine: claims-vs-reality, executable-proof, coverage-vs-source, consistency-placeholders, +dependency-feasibility, intent-vs-plan — every round. Bespoke across rounds 1–7: ff-topology +(mandatory — End state 9 anchors a three-dot merge-topology base; never skipped), anchor-existence, +t29-census-truth, task16-integration, doctrine-census-wrap, doc-contract-lock-redprovable, +guard-deny-string-truth, default-flip-old-absent (drift-guard spine), already-spent-keeps, +manifest-envelope-feasibility, absence-floor-nonvacuity, lock-c-shape, task11-row-split, +plan-self-consistency, task15-enumeration-completeness, t2-scope-satisfiability, +normalization-recipe, campaign-footprint-record, t2-region-declaration, token-set-equality-floor, +t2-structural-region, token-set-equality-final, patch-self-consistency, final-spec-execution, +round6-patch-consistency. Executed in sandbox: every `technique: executed` probe (throwaway +`cp -R` copies / synthetic git repos; the live worktree never written). artifactKind: `impl-plan`. +Fallback: none — all analyzed probes dispatched on `Explore`. + +Rounds: r1 `wf_e338e52c-01b` (16 probes, BLOCKED — 16 blockers / 12 root defects) · r2 +`wf_6aa5d373-637` (BLOCKED — 9 defects, all in the r1 patches) · r3 `wf_4856642e-cba` (BLOCKED — +7 Majors, **3 refuted by direct measurement**, 4 real) · r4 `wf_9dc44788-d07` (BLOCKED — 4 root +defects, all in the r3 patches) · r5 `wf_04c127ad-87d` (BLOCKED — blocker surface reduced to the +two r4 constructs; operator grilled interactively) · r6 `wf_e574711b-790` (BLOCKED — 1 new Major ++ A2 completion; first zero-finding spine passes) · r7 `wf_02123020-0ef` (micro-round, +**CLEARED-WITH-NOTES**). Plan patch commits: `629f90d` (r1–2), `65bcbdf` (r3), `7e6a585` (r4), +`30e8edd` (r5, operator-adjudicated), `fe36ddd` (r6), + the r7 Minor sweep (this commit). + +## Executed proof +- Base suites green at every round's base: `node --test 'skills/**/*.test.mjs'` 926/926; + `bash hooks/validate-auditor-git.test.sh` 87/87; + `bash skills/war/assets/provision-worktrees.test.sh` 380/380. +- Final equality-floor spec (r7): implemented verbatim from Task 1.5 floor (iii) → GREEN on a + correct full-enumeration message (flags abutting `)`/`;`, concrete `--sort=refname`); RED on + long-forms-only (omitting `-a`/`-r`/`-v`); RED on the live partial message; J-series 87/87 with + the correct message swapped in. +- MUST-carry lock-step (r7): simulated Task 1.2(d) passes End state 4's checks; leaving + `skills/war/SKILL.md`'s mirror sentence unedited fails them. +- Structural region (r7): first-bare-`#` locator selects exactly the invariant sentence at base + (lines 1278–1281) and catches the two-paragraph evasion round 6 proved against the last-bare-`#` + form; floor (iii) holds simultaneously. +- ff-topology (r4–r6): End state 9's three-dot subset-plus-named-absentee predicate sound under + both fast-forward and `--no-ff` topologies; equality would false-RED; subset still catches a + genuine collateral file. +- Envelope feasibility (r1): `totalTokens`/`totalToolCalls`/`agentCount` attested from this + campaign's own task-completion envelopes (18/1898609/409, 7/554519/159, 27/2108110/508) — the + plan carries the attestation because nothing in the tree evidences the envelope shape. + +## Findings +### Major (all resolved in place; the defining ones) +- [Major r1] End state 9 demanded diff-equality while expecting `hooks/validate-auditor-git.test.sh` + byte-untouched → unsatisfiable on the happy path. → subset + one named permitted absentee. +- [Major r1] Task 1.2(e) mandated the paraphrase `mines one from arbitrary prose` → End state 5's + census home would return zero under every form. → literal `mined from arbitrary prose` mandated, + do-not-paraphrase warning. +- [Major r1] End state 2 had no committed guard → lock (c) added (Task 1.3, third wave, + `deps: [1.1, 1.2]`). +- [Major r3, REFUTED ×3] probe claimed the absence floors match zero / are vacuous → direct + measurement: every key matches exactly 1 under raw/`tr`-only/`sed`+`tr` alike. Downgraded to one + Minor (the `tr`-only recipe truly does not defend a *future* re-wrap: proven 0/0/1 on a re-wrap + fixture) and fixed with the `sed 's/^[[:space:]]*#[[:space:]]*//'` strip. +- [Major r3] Task 1.5's replacement text named 3/6 value flags + 5/9 bare tokens and claimed + "enumerated exactly" — re-committing the misdescription defect the task removes. → full 15-token + transcription; evolved r4→r6 into derived token-set equality (see Adjudications). +- [Major r3] `T2\.` key jointly unsatisfiable with floor (iii) (90 file-wide / 3 in-block hits, one + byte-pinned). → sentence-scoped; evolved r4→r6 into the structural region (see Adjudications). +- [Major r4] r3's region anchor was the defective sentence's own opener (`Exit 3 is shared by`) + while the plan's suggested replacement opens `Exit 3 is reached by` — locator evaporates on a + compliant edit. → worker-declared region (r4), itself proven unauditable (r5): a partial quote + hid a `T2.` in the unquoted remainder. → structural region (r5, operator-adjudicated). +- [Major r4] the `15/15` presence floor was a per-token substring grep; `-a` ⊂ `--all`, `-r` ⊂ + `--remotes`, `-v` ⊂ `--verbose`/`-vv` — a long-forms-only message scored 15/15 while omitting + three tokens. → token-set equality derived from the case arms (r5, operator-adjudicated), + terminator + collapse completed r6. +- [Major r6, new] Task 1.2(d) widened schemas.md's MUST-carry list but not `skills/war/SKILL.md`'s + own binding MUST-carry mirror sentence (SKILL.md:68) → would land a fresh two-record divergence. + → 1.2(d) extends the mirror in the same edit; End state 4 checks the pair in lock-step. + +### Minor (r7 notes, auto-fixed in the plan) +- [Minor] The two schemas.md heading anchors are **prefixes** of the live headings + (`## ledger.json — run state at …`, `## Run manifest — …`), not exact lines → prefix-match noted + at Task 1.1 and lock (b). +- [Minor] The "vacuous key" rejection rationale was overstated: the rejected phrase matches once + under the plan's own normalized form — it is rejected as a *line-local* key, not as inherently + unmatchable → rationale corrected at End state 7 and backstop key (4). +- [Minor] Backstop taxonomy said key (2) pins no pre-change count while key (2)'s own text pins 1 + for its never-carries sub-key → clause scoped to the `manifest` census half. + +## Resolutions applied (grill decisions) +- r5 blocker "worker-declared region unauditable" → operator: **structural region** → End state 11, + Task 1.6 floor (i), backstop key (5). +- r5 needsDecision "equality scope: whole-message vs enumeration-region" → operator: + **whole-message** → Task 1.5 floor (iii), End state 7. +- r5 "what closes the loop" → operator: **narrow round 6**, then (after r6's near-green) operator: + **micro-round 7** → this verdict. +- r6 needsDecision "token terminator" / "collapse scope" → Lead-completed under `--afk` as forced + choices (the rejected reading of each REDs correct deliverables): charset terminator + `[A-Za-z0-9=<>_-]`; collapse everything after `=`, placeholder or concrete value alike. + +## Adjudications + +- Task 1.6 scanned region = the census comment lines strictly between the block's **first** bare + `#` paragraph-break line and its `# (b) ` detail line, located from the + `PAIR9="$(setup_origin_pair)"` fixture anchor; the done-report quote is evidence only, + reconciled against this region — supersedes the worker-declared-region, opening-literal + (`Exit 3 is shared by`), and file-wide readings — End state 11 / Task 1.6 floor (i) / backstop + key (5); operator-adjudicated 2026-07-24 (red-team round 5, hardened round 6). +- Task 1.5 floor (iii) = **whole-message** token-set equality, derivation: arm side `|`-split with + trailing `*` stripped; message side boundary-anchored (start/space/`(`/`,`), token running to + the first character outside `[A-Za-z0-9=<>_-]`, everything after `=` discarded (placeholder or + concrete value alike), the floor-(i) literal `=-attached` yielding no token; consequence: the + deny string names no denied token — supersedes the `15/15` substring floor and the + "enumerated exactly" partial list — Task 1.5 / End state 7; operator-adjudicated 2026-07-24 + (round 5), Lead-completed terminator + collapse under `--afk` (round 6, forced choices). +- `skills/war/assets/provision-worktrees.test.sh` campaign contention set = plans **1, 2, 5** + (sequential, distinct case families) — supersedes the roadmap's recorded `1, 5` (row-2 Files + cell, `1 → 5` dependency line, contention row all predate the Task 1.6 fold); the roadmap + correction is campaign-Lead bookkeeping at campaign close, deliberately not a task in this plan. +- #1085 closes on **neither** this plan's PR **nor** plan 4's — plan 4's vehicle is a Lead-filed + `war-followup` naming `agents/war-auditor.md` + the `workflow-template.js` dispatched clause; + this plan's PR cites #1085 as partially addressed, no closing keyword. + +## Residual risk +- The plan is prose-floor heavy by design (docs-truth sweep): its floors are greps and derivations + the refiner and gate-audit seats run, not committed tests — except lock (a)/(b)/(c), which are + committed. The Deferred-validations integrated-tip sweep is the union-level backstop; runner: + the Lead at Phase-1 land. +- `hooks/validate-auditor-git.test.sh` rides Task 1.5's Files as a straggler-comment home and is + expected byte-untouched — End state 9's named-absentee predicate covers the expected path. +- Envelope field names rest on this campaign's observed task-completion envelopes; the + null-tolerated posture covers a future harness rename. +- Six of seven rounds' new defects were introduced by the patch loop itself (rounds 2–6); the + terminal mechanism that converged was: reproduce-before-patch (r3), remove-literals-instead-of- + adding-them (r4), a mechanical pre-verify self-consistency sweep (r4 onward), and interactive + operator adjudication of construct-shape decisions (r5–r7). diff --git a/docs/roadmaps/2026-07-22-run-resilience-and-hardening-roadmap.md b/docs/roadmaps/2026-07-22-run-resilience-and-hardening-roadmap.md index 4090f0a3..473d3563 100644 --- a/docs/roadmaps/2026-07-22-run-resilience-and-hardening-roadmap.md +++ b/docs/roadmaps/2026-07-22-run-resilience-and-hardening-roadmap.md @@ -18,7 +18,7 @@ grill findings reconciled into each plan; self-adjudications recorded in each pl | 3 | [servitor-wrapup-landed-tip](../plans/2026-07-22-servitor-wrapup-landed-tip.md) | `skills/war/assets/workflow-template.js`, `skills/war/assets/workflow-template.test.mjs`, `agents/war-servitor.md`, `docs/adr/0029-capture-grounds-on-committed-tip.md`, `docs/learnings/servitor-verify-on-write-worktree-can-lag-just-landed-phase.md`, `CONTEXT.md` | next free | 2 (shared `workflow-template.js`/`.test.mjs`; D3 registry row after 1's) | | 4 | [merge-land-resilience](../plans/2026-07-22-merge-land-resilience.md) | `skills/war/assets/workflow-template.js`, `skills/war/assets/workflow-template.test.mjs`, `agents/war-refiner.md`, `skills/war/assets/provision-worktrees.sh`, `skills/war/assets/provision-worktrees.test.sh`, `skills/war/SKILL.md`, `skills/war/references/schemas.md`, `skills/war/assets/skill-doc-contracts.test.mjs`, `CONTEXT.md`, `docs/adr/0023-land-asserts-git-ground-truth.md`, new ADR (number at land) | next free | 3 (D3 registry row/floor after the 1→3 chain) | | 5 | [test-floor-target-repo](../plans/2026-07-22-test-floor-target-repo.md) | `skills/war/assets/assert-test-in-diff.sh`, `skills/war/assets/assert-test-in-diff.test.sh`, `skills/war/assets/workflow-template.js`, `skills/war/assets/workflow-template.test.mjs`, `agents/war-refiner.md`, `skills/war/SKILL.md`, `skills/war/references/schemas.md`, `skills/war/assets/war-config.mjs`, `skills/war-room/SKILL.md`, `skills/war/assets/assert-packaging-in-diff.sh`, `CONTEXT.md` | next free | 4 (manifest edge; shared engine + refiner + doc surfaces; its 4-site count assumes 4's env-proceed prompt landed) | -| 6 | [war-memory-hardening](../plans/2026-07-22-war-memory-hardening.md) | `skills/_shared/war-memory.mjs`, `skills/_shared/war-memory.test.mjs`, `skills/lessons-learned/SKILL.md`, `skills/lessons-learned/lessons-learned-doc-contract.test.mjs`, two learnings | next free | — (stack order only) | +| 6 | [war-memory-hardening](../plans/2026-07-22-war-memory-hardening.md) | `skills/_shared/war-memory.mjs`, `skills/_shared/war-memory.test.mjs`, `skills/lessons-learned/SKILL.md`, `skills/lessons-learned/lessons-learned-doc-contract.test.mjs`, two learnings, `CONTEXT.md` | next free | — (stack order only) | | 7 | [aftermath-class1-postdelete-verify](../plans/2026-07-22-aftermath-class1-postdelete-verify.md) | `skills/aftermath/SKILL.md`, `skills/war-machine/war-pipeline-structure.test.sh`, `docs/learnings/aftermath-remote-stranded-differs-from-local-tip-reachability.md`, `CONTEXT.md` | next free | — (stack order only) | | 8 | [cli-main-guard-normalization](../plans/2026-07-22-cli-main-guard-normalization.md) | `skills/war/assets/stage-workflow.mjs`, `skills/war/assets/stage-workflow.test.mjs`, `skills/war/assets/war-config.mjs`, `skills/war/assets/war-config.test.mjs`, `skills/war-campaign/assets/campaign-ledger.mjs`, `skills/war-campaign/assets/campaign-ledger.test.mjs`, `docs/learnings/cli-main-guard-equality-check-silently-noops-under-relative-invocation.md` | next free | 5 (shared `war-config.mjs`, construct-disjoint: 5 comment-only, 8 main guard) | | 9 | [war-strategy-structure-lock](../plans/2026-07-22-war-strategy-structure-lock.md) | `skills/war-strategy/war-strategy-structure.test.sh`, `docs/learnings/structure-test-check-f-locks-presence-anywhere-not-intended-location.md` | — (no release phase; rides the next sibling release) | — (stack order only) | @@ -65,7 +65,7 @@ row/floor collision), recorded in plan 4's Notes. |------|-------|------| | Release slots (`.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json` ×2, `README.md` `## Status`) | 1–8 | By design — each trailing release phase resolves the next free patch from the live slots at land time (stacked-release lesson; `version-slots.test.mjs` arbiter). Plan 9 ships no release phase (logged deviation) — its changes ride the next sibling release | | `skills/war/assets/workflow-template.js` + `.test.mjs` | 1, 2, 3, 4, 5 | Real ordering chain — disjoint named constructs per plan (1: `auditPrompt()` teach, entry parse, top-level catch; 2: `adjudicationClause` + three gate-audit blocks; 3: Wrap-up block + `tipSha` hoist; 4: merge/land env-proceed arms; 5: floor-retry sub-loop + `MERGE_RESULT.floor_diagnostic`); the `.test.mjs` D3 registry/floor edits by 1, 3, 4 are the hard rebase-contact points | -| `CONTEXT.md` | 2, 3, 4, 5, 7 | Serial appends/edits at distinct named anchors — each plan's Notes carries the rebase-by-named-anchor rule | +| `CONTEXT.md` | 2, 3, 4, 5, 6, 7 | Serial appends/edits at distinct named anchors — each plan's Notes carries the rebase-by-named-anchor rule | | `skills/war/SKILL.md` | 2, 4, 5 | Distinct sections (2: decompose-gate step insert + renumber 5–7→6–8; 4: env-proceed/land prose; 5: per-phase re-adoption prose). Plan 2's renumber lands first; 4 and 5 rebase over it | | `skills/war/references/schemas.md` | 2, 4, 5 | Distinct blocks (2: args-contract `adjudications`; 4: `gate_failure_class` environment rewrite; 5: `floor_diagnostic` + per-phase testPattern) | | `agents/war-auditor.md` | 1, 2 | Distinct sections (1: guard-contract teach; 2: adjudication-match bullet) | diff --git a/docs/specs/2026-07-22-audit-adjudication-threading-design.md b/docs/specs/2026-07-22-audit-adjudication-threading-design.md index 9f66c08b..88e7be9c 100644 --- a/docs/specs/2026-07-22-audit-adjudication-threading-design.md +++ b/docs/specs/2026-07-22-audit-adjudication-threading-design.md @@ -231,6 +231,17 @@ uncorrected per convention. - **No auto-mining of adjudications from prose** — rows are written at the moment of ruling by the Lead or the red-team grill loop, never extracted from plan/report prose after the fact (matches the existing `lenses.md` "never mined from arbitrary prose" doctrine). + **[Correction 2026-07-24 — annotative; the ratified sentence above stands unchanged.]** That + `lenses.md` citation was **false**. `skills/red-team/references/lenses.md` never carried + "never mined from arbitrary prose"; a `git log -G` over the phrase (code-verified at this spec's + own execution worktree) showed the `CONTEXT.md` `**Adjudication**:` term's `_Avoid_` line was the + doctrine's **sole** live home repo-wide. This spec's §6 verbatim rewrite of that term was + plan-faithful and therefore **orphaned** the doctrine — zero operative anchors after landing, with + nothing downstream positioned to catch it. The anchor is restored in the `CONTEXT.md` term (and + given a second standing home in `skills/war/SKILL.md` step 5) and drift-locked in + `skills/war/assets/skill-doc-contracts.test.mjs`, per + `docs/specs/2026-07-24-runbook-and-standing-record-coherence-design.md` / #1087. Provenance and + the general rule: `docs/learnings/spec-non-goal-citation-of-a-doctrines-home-file-can-be-wrong.md`. - **No worker-visible adjudications** — workers receive adjudicated values via task instructions, the top of the precedence order. - **No precedence-vocabulary rename** (D2) and **no D3 registry row** (D6). diff --git a/hooks/validate-auditor-git.sh b/hooks/validate-auditor-git.sh index 6b5e93aa..8aa9cc85 100755 --- a/hooks/validate-auditor-git.sh +++ b/hooks/validate-auditor-git.sh @@ -163,7 +163,8 @@ esac # --------------------------------------------------------------------------- # READ-FORM branch enforcement (D4): `branch` is admitted only in read shapes. # A bare `git branch` (empty rest) lists — allow. With arguments, EVERY token -# must be an enumerated read flag with =-attached values; the first token that +# must match one of the two read arms below — value-carrying flags =-attached, +# bare read flags enumerated; the first token that # is a bare name (flagless creation), a write flag (-d/-D/-m/-M/-c/-f/-u/--track/ # --set-upstream-to/…), a combined short cluster (-av), or a space-form value # (--contains ) denies — WITHOUT enumerating the write flags (default-deny @@ -179,7 +180,7 @@ if [ "$subcmd" = "branch" ]; then --contains=*|--no-contains=*|--merged=*|--no-merged=*|--points-at=*|--sort=*) ;; --list|--all|-a|--remotes|-r|--show-current|--verbose|-v|-vv) ;; *) - deny "git branch takes only =-attached read flags (--contains=, --merged=, --points-at=, --list, -a, -r, --show-current, -v); '$tok' is not one — space-form values and write flags deny" ;; + deny "git branch admits read forms only: value-carrying flags =-attached (--contains=, --no-contains=, --merged=, --no-merged=, --points-at=, --sort=), bare read flags (--list, --all, -a, --remotes, -r, --show-current, --verbose, -v, -vv); '$tok' is not one — space-form values and write flags deny" ;; esac done fi diff --git a/skills/war-review/SKILL.md b/skills/war-review/SKILL.md index 0f5ec38d..fb0d03c8 100644 --- a/skills/war-review/SKILL.md +++ b/skills/war-review/SKILL.md @@ -21,8 +21,9 @@ run from it (ADR 0008 ordering is git > issues > ledger, untouched here). absent, deleted, or unparseable renders **`n/a`** — never an estimate, never a guess. Every mined metric degrades to `n/a` independently. - **Numbers are best-effort harness reads, not billing truth.** Token and tool-call counts come from - Claude Code's transcript files, whose formats are harness-internal and may change; state this in - the report. This is not an invoice. + the harness — the manifest `envelope` aggregates where present, otherwise Claude Code's transcript + files — whose formats are harness-internal and may change; state this in the report. This is not + an invoice. ## Run @@ -65,10 +66,14 @@ JSON** — read them **defensively**: - Sum **token usage** (input / output / cache-read / cache-creation) from whatever usage-shaped fields the records carry; count **tool-call** events. If a phase's `transcriptDir` is `null`, missing on disk, expired, or carries no usage-shaped fields → that phase's mined metrics are - **`n/a`** (do not fall back to the manifest for token counts — the manifest never carries them). + **`n/a`**. Prefer the manifest's per-phase `envelope` aggregates for **totals** when present — the + authoritative non-transcript source; the input/output/cache **split** stays transcript-mined and + renders `n/a` when unsourceable, and a mined split value is always **best-effort and possibly + undercounting** (transcripts undercount tool calls roughly 20× against the envelope), never + cross-summable against an envelope total. - The **manifest** — not the transcripts — supplies dispatch counts by role, task terminal - statuses, per-phase and run timestamps, `land`, `lessonsWritten`, and `issuesFiled`. These stand - even when a transcript is gone. + statuses, per-phase and run timestamps, `land`, `lessonsWritten`, `issuesFiled`, and — when + present — the `envelope` token/tool-call totals. These stand even when a transcript is gone. Do not hardcode a rigid transcript schema. If a future harness renames the usage fields, the defensive read degrades to `n/a` instead of crashing — that is the intended failure mode. @@ -81,8 +86,9 @@ Render **per phase and as run totals** (the full End-state-2 set; `n/a` for any |---|---| | workflows run (= phase count) | manifest `phases[]` | | sub-agents by role — workers / auditors / fix-rounds / refiner dispatches / servitor | manifest `phases[].dispatches` | -| total tool calls | mined (transcripts) | -| total tokens — input / output / cache (split when available) | mined (transcripts) | +| total tool calls | manifest `phases[].envelope`, else mined (transcripts) | +| total tokens | manifest `phases[].envelope`, else mined (transcripts) | +| token split — input / output / cache | mined (transcripts), `n/a` when unsourceable | | wall-clock — total and per phase | manifest `startedAt`/`endedAt` (run) + `phases[].startedAt`/`endedAt` | | audit rounds used vs limit | manifest `phases[].dispatches.fixRounds` vs `run.roundLimit` (from `$MAIN/.claude/war/config.json`; `n/a` if absent) | | findings by severity and disposition | manifest / handoff if present, else `n/a` | @@ -112,6 +118,12 @@ string, its phase, and its task (where task-scoped): - **guard denials** — a hook denial observed in an `agent-*.jsonl` transcript (a scope/git/servitor guard that fired). - **phase-close sweep failures** — a coherence/absorb sweep that failed at phase close. +- **unfinalized phase record** — a phase record missing `endedAt`, `tasks`, or `land` **although + the run ended or a later phase started** (evidence the phase-close stamp was skipped). This is a + deliberate killed-run discriminator: a run that died mid-phase leaves the run's own `endedAt` + null and starts no later phase, so the signal stays silent there — that death already surfaces + through the `held:*` / dropped-return signal classes above; this one fires only when a Lead + demonstrably outlived the phase and still skipped the close stamp. Close with the **verdict line**: **clean** (no signals) or **friction found (N signals)**. diff --git a/skills/war/SKILL.md b/skills/war/SKILL.md index d57f7fa0..d0280070 100644 --- a/skills/war/SKILL.md +++ b/skills/war/SKILL.md @@ -38,7 +38,7 @@ Example: `/war docs/implement/implementation_plan_A.md --working dev/planA --lan - Also set **`requiresPackaging`** per task (default `true`; the refiner reads `r.task.requiresPackaging !== false`, independent of `requiresTest`). Set `false` only for a task that cannot add an image-shipped source file — pure metadata/docs edits, or a repo with no Dockerfile at all. When `false`, the refiner's packaging-floor check (`assert-packaging-in-diff.sh`) is skipped for that task; like the `requiresTest:false` skip it is **logged, never silent**. - Thread each task's plan **`Files:`** list into the per-phase Workflow as **`tasks[].files`** (the plan's declared file paths, **not** the worker's reported diff — the diff does not exist at dispatch time). The template's first-pass worker dispatch reads it: an **all-`*.md`** task (non-empty list, every entry ends `.md`) runs its worker on the **docs** tier (`agents.worker.docs`, sonnet by default); any non-`.md` entry — or an **absent/empty** list (the fail-safe: an undefined list never vacuously reads as docs-only) — keeps the base worker tier. Fix-round and `--ace` follow-up dispatch on the `agents.worker.fix` tier when configured, else the base worker. 4. **Backstop extraction — the Lead is the single normalization point (spec §4.4).** Parse the plan's `## Deferred validations (backstops)` section (or its AI-declared variant) into `{ check, why, runner, source: 'plan' }` entries — an entry line that does not parse into the three fields is **carried whole as `check`, never dropped**. Merge in any Setup auto-recorded entries (§ step 3 docker-gate, `source: 'auto'`). Mark `aiDeclared: true` on the plan entries **iff** the heading was the AI-declared variant (`## Deferred validations (backstops — AI-declared)`, ADR 0014 provenance — an AI-declared waiver is never later surfaced as operator-ratified). Thread the merged result as **`args.backstops`** (array|null of `{ check, why, runner, source, aiDeclared? }`) into every per-phase Workflow; the Workflow passes it through to `handoff.backstops[]` untouched **save one exception** — it appends a deduped `source: 'auto'` entry for any **baseline gate debt** it classifies mid-phase (spec §8; see the Checkpoint's `gate_failed` routing). Lead-normalized `source: 'plan'`/`source: 'auto'` entries are never rewritten. A legacy plan with **no** backstop section → `args.backstops = null` **and** the phase report notes "no backstop section (pre-ratified-backstop plan)". Interactive runs **ask at the approval gate** to confirm the extracted backstops; `--afk` takes them as parsed. -5. **Assemble, thread, and record the run's adjudication set (spec §4.A, D3).** Assemble the run's adjudication set from (1) the plan's red-team report `## Adjudications` rows (`docs/red-team/.md`, machine-readable block) — the existing producer, now instructed on the /war side too — and (2) **every scope finding you adjudicate at this gate** — especially a known spec delta routed to a follow-up issue — recorded **at the moment of adjudication** as a preformatted string row naming the delta, the ruling, the route (`#` where filed), and the adjudication moment. Thread the assembled set as **`args.adjudications`** (array|null) into **every** per-phase Workflow of the run; record each row in the run ledger. Interactive runs present the rows at the approval gate alongside the DAG (next step); `--afk` self-adjudicates and records without waiting (the standing afk posture). No adjudications ⇒ omit the arg — every prompt stays byte-identical to today. **Provenance discipline:** a row records a real ruling made at a named moment — the Lead never synthesizes a row to smooth over an unruled delta (spec constraint 6); a row re-scores a known, already-routed delta and never waives a gate, floor, or backstop (ADR 0017). +5. **Assemble, thread, and record the run's adjudication set (spec §4.A, D3).** Assemble the run's adjudication set from (1) the plan's red-team report `## Adjudications` rows (`docs/red-team/.md`, machine-readable block) — the existing producer, now instructed on the /war side too — and (2) **every scope finding you adjudicate at this gate** — especially a known spec delta routed to a follow-up issue — recorded **at the moment of adjudication** as a preformatted string row naming the delta, the ruling, the route (`#` where filed), and the adjudication moment. Thread the assembled set as **`args.adjudications`** (array|null) into **every** per-phase Workflow of the run; record each row in the run ledger (the `ledger.json` top-level `adjudications` key — [references/schemas.md](references/schemas.md)). Interactive runs present the rows at the approval gate alongside the DAG (next step); `--afk` self-adjudicates and records without waiting (the standing afk posture). No adjudications ⇒ omit the arg — every prompt stays byte-identical to today. **Provenance discipline:** a row records a real ruling made at a named moment — the Lead never synthesizes a row to smooth over an unruled delta (spec constraint 6) and a row is **never mined from arbitrary prose** — rows come only from the two producers above; a row re-scores a known, already-routed delta and never waives a gate, floor, or backstop (ADR 0017). 6. Present the DAG to the user as an issues preview **and any per-phase Workflow patches** you intend to inject. **Wait for approval/edits.** 7. On approval, run `bash ${CLAUDE_PLUGIN_ROOT}/skills/_shared/gh-preflight.sh ""` (the gh-account preflight — asserts the active `gh` account equals `overrides.ghUser`, switching + re-verifying on drift, failing loud on an unrecoverable mismatch; empty/unset `ghUser` ⇒ no-op exit 0) **before the file-epics batch**, then file **all phase epics up front** (labels `phase:N`, `status:todo`) so the full scope exists before any teammate launches. Run the same preflight again **before each per-phase sub-issue batch** and break that phase into **task sub-issues just-in-time** at its start (so later phases absorb learnings/drift). Record everything in the ledger. 8. **Submodule router (Increment 2).** After extracting the DAG, call `submodulePaths(repoDir)` (from `skills/_shared/provision.mjs`) to get the declared submodule paths from `.gitmodules`. For each task whose target overlaps a submodule path, **propose** to the user: @@ -62,10 +62,10 @@ MAIN=$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)") **When — at phase boundaries.** Initialize at run start with the top-level fields (`runId`, `planPath`, `configProfile`, run `startedAt`). Then, per phase: - **At phase launch** — stamp the phase's `startedAt`, and capture `workflowRunId` + `transcriptDir` **from the `Workflow` tool's own launch envelope** (harness-surfaced): launching a per-phase Workflow yields a `Run ID: ` line **and** a `Transcript dir: …/subagents/workflows/` line, and the task-completion notification's `` repeats the same `…/subagents/workflows//journal.jsonl` path. The workflow **return object** (`{ landed, escalated, auditLog, landDecision, servitorResult, handoff?, … }`) carries **neither** — do **not** read them off the return. This is the same `transcriptDir` `/war-review`'s later mining reads. If a future harness ever omits these lines, both degrade to `null` and `/war-review` renders `n/a` — never a fabricated value. -- **On phase return** — stamp the phase's `endedAt` plus the per-phase record: **dispatch counts by role** (`worker` / `auditor` / `fixRounds` / `refiner` / `servitor`, derived from the decompose + the returned `auditLog` / fix rounds / `servitorResult`), **task terminal statuses**, `landDecision`, `lessonsWritten`, `issuesFiled`. +- **On phase return** — stamp the phase's `endedAt` plus the per-phase record: **dispatch counts by role** (`worker` / `auditor` / `fixRounds` / `refiner` / `servitor`, derived from the decompose + the returned `auditLog` / fix rounds / `servitorResult`), **task terminal statuses**, `landDecision`, `lessonsWritten`, `issuesFiled`, and the **envelope aggregates** (`totalTokens` / `totalToolCalls` / `agentCount`) sourced from the Workflow task-completion notification's envelope — the same harness-surfaced channel the **At phase launch** bullet above reads `workflowRunId`/`transcriptDir` from; unsurfaced ⇒ `null`, `/war-review` renders `n/a`. - **At run end** — stamp the run's `endedAt`. -Field names follow spec §4.A (nesting may be refined; the **MUST-carry** set is binding): **per phase** — `transcriptDir`, `workflowRunId`, ISO-8601 timestamps, dispatch counts by role, and task terminal statuses; **top level** — `runId`, `planPath`, `configProfile`, run `startedAt`/`endedAt`. +Field names follow spec §4.A (nesting may be refined; the **MUST-carry** set is binding): **per phase** — `transcriptDir`, `workflowRunId`, ISO-8601 timestamps, dispatch counts by role, task terminal statuses, and the **envelope aggregates** (`totalTokens` / `totalToolCalls` / `agentCount`, binding-to-attempt, null-tolerated — the `workflowRunId` posture); **top level** — `runId`, `planPath`, `configProfile`, run `startedAt`/`endedAt`. **Fail-open.** Every manifest write is **best-effort** — a failed write logs **one** line and the run proceeds unaffected. Bookkeeping **never** blocks a run, and the manifest is **never** resume input (the resume ordering git > issue labels > `ledger.json`, [ADR 0008](../../docs/adr/0008-git-is-the-resume-source-of-truth.md), is untouched). @@ -130,6 +130,7 @@ Before reading the ledger or open issues and continuing, run the **reconciliatio ## Checkpoint (between phases) Post a phase report — *landed (task→issue#, SHA) · the rendered **handoff block** (tipSha · polish merged/discarded/skipped · absorbed (sha → finding titles) · follow-ups filed (issue + why-not-absorbable) · notes · End-state condition statuses · intentPresent) · **Unexecuted backstops** (mandatory line rendering `handoff.backstops[]` — every deferred validation this phase did not run: each `check · why deferred · runner`, `source: 'auto'` entries flagged as Setup-recorded, and an **AI-declared** entry rendering its marker so it is never shown as operator-ratified; literal `None`/`— none —` when the array is empty, and the "no backstop section (pre-ratified-backstop plan)" note when it is `null`) · learnings captured · escalations needing your decision (with options) · deferred/blocked · gate result + working pushed@SHA · next phase?* — and **wait for the user's go**. Under `--afk`: post + `PushNotification`, then proceed. **Hard escalations (audit-blocked, unresolvable conflict, plan-contradiction) always halt regardless of mode.** Promote any ADR-worthy deviation to a real `docs/adr/` entry. +- **Manifest stamp (telemetry, fail-open).** Before posting the phase report, complete the Run-manifest section's on-phase-return stamp (`endedAt`, dispatch counts, task terminal statuses, `land`, envelope aggregates); a skipped stamp is the "unfinalized phase record" friction row `/war-review` reports; fail-open discipline unchanged (a failed write logs one line and never blocks the advance). - **Issue-lifecycle floor — gate the DAG advance (§4.1).** Before advancing the DAG past this phase, run `bash ${CLAUDE_PLUGIN_ROOT}/skills/war/assets/assert-issues-filed.sh assert ` (it runs `gh-preflight.sh` itself first, so an account flip cannot fake its exit). It confirms the ledger's `epic_issue` and every `tasks[].issue` exist on live `gh`, and — on a phase being **landed** — that the epic is `state:CLOSED` **and** carries `status:done`. **Route by exit code:** `0` ⇒ verified, advance; `1` ⇒ blocks the advance, surfacing the named route (`issues-missing` naming the missing/null issue, or `done-but-open`) — treat as an escalation to resolve before proceeding; `2` ⇒ a gh/network/ledger-parse **tooling error**, escalate as such, **never a silent pass** (exit 2 never collapses into 1). On a **landed** phase, close the epic via the floor's atomic mode — `bash ${CLAUDE_PLUGIN_ROOT}/skills/war/assets/assert-issues-filed.sh --close-epic --sha ` (one call does `+status:done` / `−status:in-progress` **and** `gh issue close --reason completed` with the landed-SHA comment) — **instead of** separate label/close commands, so `status:done` can never outlive an open epic. - **Never-waive rule (§4.4 — Lead prose).** A validation that is in **none** of: the resolved gate, a deterministic floor (test / packaging / submodule), or the plan's backstop section (a plan-declared or Setup-auto-recorded deferred validation) **may not be waived in prose mid-run**. Do not narrate it away as "skipped" or "not applicable" in a phase report — **escalate to the operator** instead (interactively wait; under `--afk` this is a hard escalation, since only the operator can ratify a new waiver). This is the behavioral fix for the silent-prose-waiver failure the whole feature exists to prevent — a deferred validation is legitimate only once it is a ratified backstop, never as an ad-hoc mid-run sentence. - **Fail-closed phase classification (§4.2 — apply on every phase notification, before any land/hold/advance decision).** On each phase Workflow notification, derive `landDecision` as follows: @@ -142,7 +143,7 @@ Post a phase report — *landed (task→issue#, SHA) · the rendered **handoff b - **`held:phase-incomplete` — retryable.** Under `--afk`, auto-resume via `Workflow({ scriptPath: , resumeFromRunId })` — always resume the same run, **never** a fresh re-run (the staged per-phase copy persists for the run's life — never in a reapable worktree — so its bytes are stable across Lead restarts and the replay dispatches the identical script). Count against `run.roundLimit` total attempts (all attempts across all resume rounds for the phase). When the attempt budget is exhausted, HARD-halt as `held:phase-incomplete (retries-exhausted)`, regardless of `--afk`. Interactively (no `--afk`), surface the option to resume and wait for the user's go. If `resumeFromRunId` is unavailable (e.g. Lead process was killed between runs), degrade immediately to terminal HARD-halt. - **Dead phase invariants.** A dead phase (`held:phase-incomplete` or `held:workflow-error`) **never advances the DAG** — the next phase is not started. Its git state is **preserved (no teardown)** so the phase can be inspected and, if retryable, resumed. - **`held:submodule-pr` — interactive hold (2B path only).** The phase has pushed the submodule branch and opened a PR on the submodule remote; the superproject pin has not yet been bumped. This hold **only arises under non-`--afk` mode** (an un-owned submodule under `--afk` is refused at launch — see submodule router above). The phase **never advances the DAG** while held; git state is **preserved**. The hold is cleared by the Resume procedure (the `held:submodule-pr` sub-procedure in `## Resume`) — not by retry, not automatically. - - **`held:land-failed` — root-cause-branched auto-recover, else hold.** The refiner's push-first CAS land could not advance the working branch, or the land dispatch **died / returned an unrouted result** (root cause (c) below). **Three independent root causes auto-recover**; each ends in a single `land-advance` on a green gate — except (c)'s step-0 already-landed case, which **records** a completed land and lands nothing new — and all obey identical `--afk`-auto / interactive-offer discipline. A **gate-time `environment` failure that reaches this hold has already spent its in-workflow `environment-proceed` retry** (the bounded fresh-env re-land came back `environment`-classified a second time — see the `gate_failed` routing bullet below), so the manual re-run below is the **second** line of defense, not the first attempt: expect a genuinely persistent environment, and inspect it before re-running. **Precheck (§4.4).** Every `land-advance` in the recipes below is invoked from the worktree holding ``: `land-advance` **refuses** with a self-explaining die (`EX_WRONG_BRANCH`, exit **6** — deliberately never exit 2) when invoked from a worktree whose HEAD is not ``, naming both SHAs and the expected cwd, and pushes nothing (local **and** origin refs untouched) — so a `[rejected]` **exit 2** from it now always means a **real concurrent advance**, never a wrong cwd ([ADR 0023](../../docs/adr/0023-land-asserts-git-ground-truth.md) amendment). + - **`held:land-failed` — root-cause-branched auto-recover, else hold.** The refiner's push-first CAS land could not advance the working branch, or the land dispatch **died / returned an unrouted result** (root cause (c) below). **Three independent root causes auto-recover**; each ends in a single `land-advance` on a green gate — except (c)'s step-0 already-landed case, which **records** a completed land and lands nothing new — and all obey identical `--afk`-auto / interactive-offer discipline. A **gate-time `environment` failure that reaches this hold** arrives by one of two arms, and only one has already spent a retry: on the **primary-land arm**, the land gate failure was itself classified `environment`, a bounded fresh-env `environment-proceed` re-land was dispatched, and that re-land came back `environment`-classified a second time (see the `gate_failed` routing bullet below) — the retry is **already spent**, so the manual re-run below is the **second** line of defense, not the first attempt: expect a genuinely persistent environment, and inspect it before re-running. On the **baseline-proceed arm**, the land gate failure was classified `baseline` and a `baseline-proceed` re-land was dispatched instead; when that re-land's own failure comes back `environment`-classified it routes straight here with **no** `environment-proceed` retry ever dispatched for it — the two `*-proceed` flavors deliberately never chain — so the manual re-run below is genuinely the **first** fresh attempt. **Precheck (§4.4).** Every `land-advance` in the recipes below is invoked from the worktree holding ``: `land-advance` **refuses** with a self-explaining die (`EX_WRONG_BRANCH`, exit **6** — deliberately never exit 2) when invoked from a worktree whose HEAD is not ``, naming both SHAs and the expected cwd, and pushes nothing (local **and** origin refs untouched) — so a `[rejected]` **exit 2** from it now always means a **real concurrent advance**, never a wrong cwd ([ADR 0023](../../docs/adr/0023-land-asserts-git-ground-truth.md) amendment). - **(a) Checkout collision** (existing) — the working branch is **checked out in the Lead worktree** (the launch-worktree collision) **AND** `git merge-base --is-ancestor ` holds (the integration branch is a clean fast-forward superset — no divergent commits on the working branch). Recover in the Lead worktree: `git merge integration/ --no-ff` into the working branch, run the **resolved gate**, and only on a green gate call `bash …/provision-worktrees.sh land-advance ` — the **land primitive** performs the CAS push, the follower sync, and the phantom-land guard (this **replaces the raw `git push`**) — then close with `bash …/provision-worktrees.sh sync-follower `, a one-command assertion (**not** a load-bearing sync): `land-advance` already ran `cmd_land_advance`'s follower CAS, so this exits 0 when the recipe was followed and fires only to catch a deviation (a raw `git push` in place of `land-advance`, which would leave the follower stale). Safe under origin anchoring precisely because the merge already advanced the checked-out follower to ``: a local-tip guard would false-phantom that, but the guard reads the **pre-push origin tip** ([ADR 0023](../../docs/adr/0023-land-asserts-git-ground-truth.md)). - **(b) Absent origin baseline** — `git ls-remote origin refs/heads/` returns **empty** (reachable post-Setup only when the operator deleted the remote working branch mid-run — `git push origin :`, an out-of-band reset/prune — after Setup's `ensure-origin`; Setup never re-runs mid-run, so only this Checkpoint can heal it). **Safety predicate first:** `git merge-base --is-ancestor ` must hold — the same trustworthiness check as (a), proving the local working ref carries nothing outside the integration lineage before it is enshrined as origin's new baseline. On pass: `bash …/provision-worktrees.sh ensure-origin `, then the standard **detached-`_refinery`** merge `integration/ --no-ff` + resolved gate + `land-advance ` (no checkout collision exists on this branch, so the one-primitive topology applies directly), then the same closing `bash …/provision-worktrees.sh sync-follower ` assertion (trivially exit-0 after `land-advance`'s `cmd_land_advance` follower CAS; it fires only on a raw-`git push` deviation). On predicate **failure, stay held** — never bootstrap origin from an untrusted local ref. - **(c) Dead land agent** (new) — the land dispatch produced **no routable land result**: the escalated `phase--land` entry carries a **null / evidence-free `detail`** (the dispatch died producing no `MergeResult` — observed class: a transient API 529 returning 0 tokens, so `land-advance` never ran) **or** a `reason` that is **no `MergeResult` status** (an unrouted / contract-breaking return — same ownership either way: no trustworthy landing evidence). Diagnose (c) from the **escalation payload before** probing (a)/(b)'s git predicates. **Recovery — step 0, already-landed probe** (the dead-*after*-push case): `git -C <_refinery> fetch origin ` then `git merge-base --is-ancestor origin/` — when it **holds**, the dispatch died *after* its CAS push succeeded, so the land already happened: **record it** (ledger / epic close per the manual-land bookkeeping), run the closing `bash …/provision-worktrees.sh sync-follower ` assertion, and proceed to the **"Capture learnings on every landed phase"** manual-servitor step (`servitorResult` is absent — the Lead spawns the servitor itself); **never re-merge** — a second `--no-ff` merge would mint an empty phantom phase commit. When the probe **fails**, run the **same one-primitive land every land uses** — the **Escalation-completion land** recipe below (fetch, detach `_refinery` at `origin/`, `git merge --no-ff `, resolved gate, a **single** `land-advance`, closing `sync-follower` assertion; the detached-`_refinery` topology is collision-safe, so no (a)-interaction) — or the full [Recovery relaunch](#recovery-relaunch) when the operator prefers a fresh run; **same green-gate guard and `--afk`-auto / interactive-offer discipline as (a)/(b)**. **Anti-`resumeFromRunId` warning:** the Workflow tool's printed `resumeFromRunId` hint is **generic harness text** — following it after a land failure re-dispatches the already-merged tasks' `merge:*` agents **live** (the journal replay re-runs the gate and the push-first CAS, design.md §6) and is **exactly wrong** during the transient-API window when the integration tip is already complete and green; `resumeFromRunId` stays `held:phase-incomplete`-only, **never** for a land failure. @@ -168,6 +169,7 @@ The sanctioned retry for a held/escalated task or a dead phase — **not** a new - **Owned-file continuity** — set `args.ownedFile` to the prior run's owned-refs ledger (`.claude/teams//owned-refs`), **or** adopt the `integration//phase-` branch into the new run's owned-file with `bash …/provision-worktrees.sh record-as-owned --owned-file ` — the tooled, proof-carrying form of the append: it proves the branch strictly descends from the frozen ``, prints the `..` ahead-commits (feeding the recovery runbook's step-2 mapping), and appends the ref while **moving no ref**. Either way `cmd_ensure_integration` **reuses the owned integration branch** (already carrying the landed sibling merges) instead of dying foreign (exit 3). The branch derivation reproduces each task branch (`war//p-`) and the fresh run's phase-scoped worktree path checks the existing branch out as-is. - **Prior commits are kept, never rewritten** — a relaunched task branch may carry audit-rejected commits; **keep them as WIP**. Seed the retried task's `planSlice` with the recorded audit findings / escalation detail so the relaunch worker fixes **forward** per those findings. **No `reset`, no `--force`** on the shared refs. - **Normal land path** — nothing special: the reused integration branch lands via the ordinary push-first CAS. +- **Adjudication continuity** — re-thread the full accumulated `args.adjudications` set from the ledger record (the `ledger.json` top-level `adjudications` key — [references/schemas.md](references/schemas.md)), alongside `args.recovery`, the same duty as the held-partial-phase runbook's step 4 — so neither entry point ever relaunches a seat adjudication-blind. **Warnings.** Letter-suffixed phase ids (e.g. `4b`) are **rejected by design** — the numeric-`` guard in `cmd_ensure_integration`/`cmd_teardown_phase` stays; a relaunch reuses the **same numeric `phase.id`**, never an improvised suffix. Worktrees left by **pre-change runs** at the old `/` paths are never collided with (new runs derive the `p-` shape) but are also **never reaped** by a dead run's teardown — same manual-cleanup posture as today's leftovers (see the backstops line). diff --git a/skills/war/assets/provision-worktrees.test.sh b/skills/war/assets/provision-worktrees.test.sh index 7767c552..46947f4f 100755 --- a/skills/war/assets/provision-worktrees.test.sh +++ b/skills/war/assets/provision-worktrees.test.sh @@ -1275,10 +1275,10 @@ expect "T2.8b: already-landed, follower absent -> follower CREATED at " # STOPPED exercising when it was reframed to prove the ls-remote rc-guard # short-circuit (test-reframe-can-strand-adjacent-branch-coverage). # -# Exit 3 is shared by multiple routes, every one of which dies LOUDLY with -# route-naming text — except this one: the push-error branch is the only SILENT -# exit-3 route (land-advance captures the push output internally and prints -# nothing), so route identity rests on (b)+(c)+(d) TOGETHER: +# Exit 3 is reached by several routes; the push-path silent ones (the +# push-error branch and the post-push origin-readback mismatch) print nothing, +# while the rest die LOUDLY with route-naming text — so route identity rests +# on (b)+(c)+(d) TOGETHER: # (b) ls-remote SUCCEEDS pre-call — closes the T2.3 rc-guard route by # construction (pre-receive is push-side; ls-remote is fetch-side); # (c) the token-distinctness fact asserted BY NAME (remote rejected present, diff --git a/skills/war/assets/skill-doc-contracts.test.mjs b/skills/war/assets/skill-doc-contracts.test.mjs index e4b666e8..c5ddee52 100644 --- a/skills/war/assets/skill-doc-contracts.test.mjs +++ b/skills/war/assets/skill-doc-contracts.test.mjs @@ -27,6 +27,11 @@ const specProseDrift = readFileSync( join(HERE, '..', '..', '..', 'docs', 'specs', '2026-07-12-prose-drift-corrections-design.md'), 'utf8', ) +// Glossary + contract reads (D19/D20) — same construct-anchored style as the rows above. +// CONTEXT.md is the repo-root ubiquitous-language glossary; schemas.md is the war skill's contract +// sheet (skills/war/references/), so the two roots differ — both resolved from HERE, never cwd. +const contextMd = readFileSync(join(HERE, '..', '..', '..', 'CONTEXT.md'), 'utf8') +const schemasMd = readFileSync(join(HERE, '..', 'references', 'schemas.md'), 'utf8') // (D10) The Checkpoint classification ladder's routing predicates are the source of truth; each // class's inline example list must map to its predicate. The recorded regression @@ -305,3 +310,114 @@ test('D18 — SKILL.md gate_failed environment arm documents bounded environment assert.ok(new RegExp(anchor, 'i').test(b), why) } }) + +// ---- Task 1.3 locks (a)/(b)/(c) — plan 2026-07-24-runbook-and-standing-record-coherence ---- + +// (D19) The CONTEXT.md `**Adjudication**:` glossary term must keep the provenance-discipline clause +// on its `_Avoid_` line — the doctrine that a row comes only from the two named producers and is +// never sourced from surrounding prose (#1087). Recorded regression: the 2026-07-22 +// audit-adjudication-threading spec justified overwriting that clause by citing a duplicate home at +// `skills/red-team/references/lenses.md` which never carried it, so the plan-faithful rewrite left +// the doctrine with ZERO operative anchors repo-wide +// ([[spec-non-goal-citation-of-a-doctrines-home-file-can-be-wrong]]; that spec now carries a dated +// correction note at the citing non-goal). This row is the committed guard against a second orphaning. +// +// Extraction is BY CONSTRUCT — the bolded term to the next bolded glossary term — never a +// whole-file scan: the same doctrine has a second standing home in SKILL.md step 5, and a +// repo-wide key could not tell the two anchors apart (deleting this one would still pass). +// The key spells its inner spaces `\s+` on purpose, D18's first absence key being the in-file +// precedent: CONTEXT.md wraps near 100 columns, so the restored `_Avoid_` line may legitimately wrap +// mid-phrase, and per the two-line-pairing lesson the `\s+` form strictly widens the match across a +// wrap. It also keeps the contiguous literal out of this file, which the plan's wrap-tolerant +// repo-wide doctrine census expects to return zero hits here — a guard must never trip the floor it backs. +test('D19 — CONTEXT.md **Adjudication** term keeps its provenance-discipline doctrine clause (#1087)', () => { + const block = contextMd.match(/^\*\*Adjudication\*\*:[\s\S]*?(?=\n\*\*[^\n*]+\*\*:)/m) + assert.ok( + block, + 'could not locate the `**Adjudication**:` glossary term in CONTEXT.md (bolded term → next ' + + 'bolded glossary term) — the extraction construct rotted', + ) + assert.match( + block[0], + /never\s+mined\s+from\s+arbitrary\s+prose/i, + "the CONTEXT.md **Adjudication** term's `_Avoid_` line must keep the provenance-discipline " + + 'doctrine clause — it is the doctrine\'s original and standing anchor (#1087); correct this ' + + 'row to a sanctioned rewording, never drop the clause to make a reword pass', + ) +}) + +// (D20) `skills/war/references/schemas.md`'s ledger.json contract block must declare the top-level +// `adjudications` key (#1016). SKILL.md step 5 ("record each row in the run ledger") and this same +// file's args-contract paragraph both cite that key as the record a recovery relaunch re-threads +// `args.adjudications` from — a ledger block that never declares it leaves both citations dangling, +// which is the #1016 gap itself. Heading located by PREFIX (the live line continues +// ``at `.claude/teams//```), region terminated at the block's closing fence — an exact-line +// heading match finds nothing, and a whole-file key would pass on the args-contract paragraph alone. +test('D20 — schemas.md ledger.json block declares the top-level adjudications key (#1016)', () => { + const block = schemasMd.match(/^## ledger\.json — run state[^\n]*\n```jsonc\n([\s\S]*?)\n```/m) + assert.ok( + block, + 'could not locate the `## ledger.json — run state` jsonc block in references/schemas.md ' + + '(heading prefix → closing fence) — the extraction construct rotted', + ) + assert.match( + block[1], + /adjudications/, + 'the ledger.json contract block must declare the top-level `adjudications` key — SKILL.md ' + + "step 5 and this file's args contract both cite it as the re-thread source (#1016)", + ) +}) + +// (D21) SKILL.md's `held:land-failed` Outcome-handling bullet must carry BOTH arms by which a +// gate-time `environment` failure reaches the hold (#1039). The retired sentence was unconditional — +// the retry was declared spent for every such entry — which is false on the baseline-proceed arm, +// where a `baseline-proceed` re-land's `environment`-classified failure routes straight to the hold +// with NO `environment-proceed` retry ever dispatched (the two `*-proceed` flavors never chain), so +// the operator's first manual re-run genuinely is the first fresh attempt. +// +// This is a PRESENCE key on the two-path shape and deliberately NOT an `/already\s+spent/i` absence +// key: the sanctioned replacement keeps that token inside the CONDITIONAL primary-land arm, so an +// absence key would red the correct text and green the unconditional one only by accident. The +// defect is the unconditional framing, not the token. +// +// Extraction copies the live construct at `land-decision.test.mjs` (same bullet, same file): locate +// the REAL 2-space-indented ``- **`held:land-failed``` header — a TOKEN-ONLY prefix, trailing bullet +// text variable; the compact ``- **`held:land-failed`**`` wrap is *schemas.md*'s header form and has +// zero occurrences in SKILL.md — and terminate at the next SAME-INDENT 2-space `- **` sibling, never +// a top-level `- **` one (that truncates at the nested ` - **(a)` sub-bullet, or, read the other +// way, over-extends past the whole `- **Escalation-completion land …**` sibling and would let arm +// vocabulary from unrelated prose green the lock). Arm markers are markup-tolerant (D18's idiom), so +// a bold/backtick reshuffle inside an arm name does not false-red. +test('D21 — SKILL.md held:land-failed bullet names both environment arms, never one unconditional retry-spent claim (#1039)', () => { + const lines = skillMd.split('\n') + const headerIdx = lines.findIndex((l) => /^ {2}- \*\*`held:land-failed`/.test(l)) + assert.ok( + headerIdx >= 0, + 'could not locate the 2-space ``- **`held:land-failed``` bullet header in SKILL.md — anchor ' + + 'rotted (non-vacuous guard)', + ) + let endIdx = lines.length + for (let i = headerIdx + 1; i < lines.length; i++) { + if (/^ {2}- \*\*/.test(lines[i])) { endIdx = i; break } // next SAME-INDENT sibling, not a nested ` - **` + } + const region = lines.slice(headerIdx, endIdx).join('\n') + // Non-vacuous: the region must reach root cause (c) — proves the extraction did not truncate early + // at a nested sub-bullet (the exact failure the same-indent terminator avoids). + assert.match( + region, + /dead land agent/i, + 'the extracted held:land-failed region must span through root cause (c) "dead land agent" — ' + + 'extraction truncated too early', + ) + for (const [re, arm, why] of [ + [/primary-land[\s*`]{0,6}arm/i, 'primary-land', 'the land gate failure was itself classified `environment`, a bounded environment-proceed re-land was dispatched, and it came back `environment` a second time — the retry IS spent there'], + [/baseline-proceed[\s*`]{0,6}arm/i, 'baseline-proceed', 'a `baseline-proceed` re-land failed `environment`-classified with no environment-proceed retry ever dispatched — the manual re-run is the first fresh attempt'], + ]) { + assert.match( + region, + re, + `the held:land-failed bullet must name the ${arm} arm (${why}) — a single unconditional ` + + 'retry-spent sentence is false on one of the two paths (#1039)', + ) + } +}) diff --git a/skills/war/references/schemas.md b/skills/war/references/schemas.md index cdaaaf88..dfc23df7 100644 --- a/skills/war/references/schemas.md +++ b/skills/war/references/schemas.md @@ -108,6 +108,7 @@ A task reaches the refiner with exactly one terminal **outcome**. Two are produc pr_remote?, // (submodule 2B phases) submodule remote (e.g. "owner/repo") the PR was opened against submodule_merge_sha? // (submodule phases) SHA of the submodule commit that was merged (written on resume after mergeCommit.oid is confirmed) } ], + adjudications?, // array — the run-long accumulated adjudication set (spec D8); rows carried verbatim as threaded in either args-contract row shape (preformatted string, or `{ adjudicated|value, supersedes }`); absent ⇒ none recorded — the recovery relaunch re-threads `args.adjudications` from this key pr_url? } ``` - **`merge_sha` is advisory** — authoritative only when reachable on the branch (the reconciliation pre-flight's invariant; git is monotonic, so a recorded `merge_sha` is real iff its commit is reachable). The ledger is a lagging view; git branch state is the authority ([ADR-0008](../../../docs/adr/0008-git-is-the-resume-source-of-truth.md)). @@ -132,12 +133,13 @@ Fail-open per-run **telemetry** the `/war` Lead accumulates at phase boundaries scriptPath: "… | null", // workflow script path (unsurfaced ⇒ null) transcriptDir: "… | null", // MUST — harness transcript dir /war-review mines (unsurfaced ⇒ null) dispatches: { worker: 4, auditor: 9, fixRounds: 1, refiner: 6, servitor: 1 }, // MUST — dispatch counts by role + envelope: { totalTokens, totalToolCalls, agentCount } | null, // MUST-carry (binding-to-attempt, null-tolerated — the `workflowRunId` posture) — Workflow task-completion envelope aggregates, stamped at phase return; any field, or the whole object, the Lead cannot source is `null`, never transcript-derived tasks: { t1: "merged", t2: "escalated" }, // MUST — per-task terminal status land: "landed", // the phase landDecision lessonsWritten: 3, issuesFiled: 7 } ] } ``` -- **MUST-carry** (field names are the spec's contract; nesting may be refined, the list is binding) — per-phase `transcriptDir`, `workflowRunId`, ISO-8601 timestamps (top-level run + per-phase), dispatch counts by role, and task terminal statuses; top-level `runId`, `planPath`, `configProfile`, and run `startedAt`/`endedAt`. These are what `/war-review` consumes; anything the Lead cannot source is `null`/omitted and the review renders `n/a` (**never** fabricated). +- **MUST-carry** (field names are the spec's contract; nesting may be refined, the list is binding) — per-phase `transcriptDir`, `workflowRunId`, ISO-8601 timestamps (top-level run + per-phase), dispatch counts by role, task terminal statuses, and the `envelope` aggregates (binding-to-attempt, null-tolerated — the `workflowRunId` posture); top-level `runId`, `planPath`, `configProfile`, and run `startedAt`/`endedAt`. These are what `/war-review` consumes; anything the Lead cannot source is `null`/omitted and the review renders `n/a` (**never** fabricated). - **`tasks` vs `land`** — `tasks` maps each task id to its **task** terminal status (the `ledger.json` task-status enum above: `merged`|`escalated`|`blocked`, or `env-blocked`); `land` is the phase `landDecision` (`landed`|`held:*`, the Workflow per-phase return enum below). Both are copied from the Workflow per-phase return / handoff, not re-derived. - **The review saves a sibling** — `/war-review` renders the manifest + its transcripts into `.claude/war/runs/-review.md` beside the manifest (always written; a `/war-review` output, not a `/war` write). - **Fail-open, never resume input** — every manifest write is best-effort: a failed write logs one line and the run proceeds unaffected. The manifest is telemetry only; the resume source-of-truth ordering (git > issues > ledger, [ADR-0008](../../../docs/adr/0008-git-is-the-resume-source-of-truth.md)) is untouched. @@ -256,7 +258,7 @@ Optional `intent` (string|null, ADR 0013) — the plan's `## Commander's Intent` Optional `memory` (spec §4.5) — the Lead's per-phase prior-lesson prefetch, shaped `{ byTask: { : { worker, seats: { : block } } }, servitor }`, where each `block` is the `war-memory query` CLI's ready-to-inject text. Threaded like `intent`: the template concatenates a `memoryClause` at the worker, auditor, fix-worker, add-test, and servitor spawn sites (ace / gate-audit / polish-sweep get none). An empty/absent map ⇒ every prompt is **byte-identical** to a memory-less run. Retrieval fails open — a missing `memory` is never an error. -Optional `adjudications` (array|null) — preformatted strings or `{ adjudicated|value, supersedes }` objects (row shapes unchanged by this widening). **Two producers** feed the set: the plan's red-team report `## Adjudications` block (`docs/red-team/.md`) and the Lead's own decompose-gate / escalation-time scope adjudications, assembled and recorded per [SKILL.md](../SKILL.md). The set **accumulates run-long** and is **re-threaded in full** — from the run-ledger record, alongside `args.recovery` — on a sanctioned recovery relaunch, so a relaunched seat is never adjudication-blind. Threaded like `intent`: the template concatenates an `adjudicationClause` at the roster-seat `auditPrompt` and the three gate-audit-family seats (post-merge, integrated-tip, end-state-only). Empty/absent ⇒ every prompt is **byte-identical** to an adjudication-less run. +Optional `adjudications` (array|null) — preformatted strings or `{ adjudicated|value, supersedes }` objects (row shapes unchanged by this widening). **Two producers** feed the set: the plan's red-team report `## Adjudications` block (`docs/red-team/.md`) and the Lead's own decompose-gate / escalation-time scope adjudications, assembled and recorded per [SKILL.md](../SKILL.md). The set **accumulates run-long** and is **re-threaded in full** — from the run-ledger record (the `ledger.json` top-level `adjudications` key above), alongside `args.recovery` — on a sanctioned recovery relaunch, so a relaunched seat is never adjudication-blind. Threaded like `intent`: the template concatenates an `adjudicationClause` at the roster-seat `auditPrompt` and the three gate-audit-family seats (post-merge, integrated-tip, end-state-only). Empty/absent ⇒ every prompt is **byte-identical** to an adjudication-less run. Auditors receive the **absolute `task.worktree` path** so they can `Read` candidate files directly in the task's isolated checkout rather than the main repo tree.