Gate evidence and release integrity — lint rides the discovered gate, End-state ownership spans deps siblings, version guard gains a monotonic floor (0.14.62) - #1139
Merged
Ljferrer merged 16 commits intoJul 27, 2026
Conversation
…ds, escalating)
Terminal BLOCKED. The Lead stopped patching after round 2 and is escalating five
residual open questions to the operator.
Round 1 found 4 blockers over 3 roots; two were resolved and verified:
- ${plan.file} in endStateBlock was a phase-level crash. `plan` is destructured
with no default and never entry-validated (workflow-template.js:231, which is
why :484 reads `if (plan) plan.gate = ...`), endStateBlock is built top-level
and unconditionally (:1497), and pt throws on undefined by contract (:191). A
plan-less zero-task phase claiming End-state conditions would have gone from a
clean route to held:workflow-error phase-wide. Now the guarded form at all
three mandate sites.
- The monotonic mechanic silently depended on git's IMPLIED
--diff-merges=first-parent: all 50 slot-touching first-parent commits are
merges, and --diff-merges=off yields zero versions. version-slots.test.mjs
runs in every refiner gate, so a zero-parse would have bricked the gate
campaign-wide. The flag is now pinned explicitly.
Round 2 returned 5 blockers, mostly defects the round-1 patch introduced: a
mis-recorded measurement (a -n 6 sample written as a full-window figure; the
real number is 49 from 50), a false causal claim (the Lead wrote that the path
filter makes rev-list return non-merges; three probes measured otherwise and the
Lead confirmed --first-parent is the discriminator), a floor contradiction, and
an overstated test citation.
Two wrong Lead explanations in a row on one paragraph is what the
two-rounds-per-blocker rule exists for. The wrong text is WITHDRAWN rather than
replaced with a third theory, and that paragraph now carries only measured facts
plus a do-not-implement marker.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Plan 5 (gate-evidence-and-release-integrity) was BLOCKED after 2 red-team rounds with 5 residual questions escalated to the operator. All five ruled: 1. rev-list + git show alternative DROPPED — the proven `git log --first-parent --diff-merges=first-parent` form is the sole mechanic 2. §4.4 sweeps re-anchored: leader-strip -> whitespace-normalize -> grep -io, full clauses kept, each paired with a named must-fire control 3. orphaned §4.4 disposition 5 (ADR 0024) assigned to Task 1.1; Notes bullet now maps all six dispositions to owners 4. two-surface drift guard stays per-surface bounded ordered pins; the D3 registry row rejected as strictly weaker than End state 3 5. End state 4 enumerates the plan-less claims-bearing regression case; the :6692 citation re-anchored by construct name Round 3 (wf_89c5f40a-e37, 10 probes, 15 agents, 10/10 on-target) returned 3 Major blockers. One was real — whitespace normalization alone does NOT survive a comment-leader reflow (`# ` survives `tr`, so the clause still misses). Fixed by prepending a comment-leader strip; proven at the real base: 0 hits raw, 0 tr-only, 1 with the strip, control firing on the same run, all four sweeps green. The other two blockers were refuted as stale-base self-confounds — the probes measured this Lead worktree (v0.14.57) instead of the campaign base (master, v0.14.61), where the plan's README claim is true. One Lead statement introduced this session was likewise stale-base and is withdrawn in the report: the cited plan-less test sits at :6692 exactly as originally cited; only the coverage half (no endState key) stands. Verdict: CLEARED. /war may execute plan 5. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rapper (#1130) Closes the #1081 gap: `war-memory.mjs lint docs/learnings/` ran only in post-push CI, so a plan End state citing it was a structurally SOFT cannot-confirm at every gate-audit — no captured artifact could carry the evidence. - NEW skills/_shared/war-memory-lint.test.sh — a gate-DISCOVERED `*.test.sh` beside the CLI. The gate's own `-name '*.test.sh'` sweep picks it up with ZERO engine change: no `resolveGate`/composer edit (both hand-mirrored copies byte-untouched), no floor-script vehicle. bash-3.2-safe, cwd-independent (repo root resolved two dirs up from $0). Target is `$1` when given (fixture override for the meta-tests), else the repo's docs/learnings/ — explicit, because a bare `lint` falls back to the LOCAL memory root. Existence guard (anti-vacuity, conscious deviation from spec §4.1): cmdLint's `catch { continue }` prints "lint: clean" exit 0 on an absent target, so the wrapper fails LOUD with exit 2 naming the path instead. On an existing target the CLI exit code propagates untouched. - war-memory.test.mjs: three meta-tests spawn the wrapper against a violating fixture (exit 1 + offending file + `home-path` on stdout), a clean fixture (exit 0 + "lint: clean"), and a nonexistent target (guard exit 2 naming the path). Fixtures live under mkdtemp, never docs/learnings/; the home-path shape is assembled at runtime, never committed. Temp-break proven: deleting the `exec node … lint` line reds the first two, deleting the `-d` guard reds the third — and only the third. - memory-audit.yml: header comment only (name/on/permissions/steps byte-identical) — CI is now the POST-PUSH backstop, the discovered suite is the PRE-MERGE enforcement. - gate-artifact-never-includes-war-memory-lint lesson: `MITIGATED (#1081):` description prefix (compressed 221B -> 188B), a lead MITIGATED note naming the wrapper, tense-shifted historical record, and forward advice that no longer sends a gate-auditor straight to SOFT-note on a post-wrapper tip. Gate green (935 JS tests, all discovered shell suites); the captured gate log carries `== gate(bash): ./skills/_shared/war-memory-lint.test.sh ==` followed by `lint: clean`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… fix, default-target pin (#1130) Advisory absorb-disposition fixes, one commit, no behavior change beyond the widened guard and one additive test: 1. [Nit] Existence guard covers absent-but-not-unreadable, while its own header comment claims both (war-memory-lint.test.sh:34). cmdLint wraps statSync AND readdirSync in one catch{continue}, so an existing-but-unreadable target (EACCES) still yielded a vacuous "lint: clean" exit 0 — the code was narrower than the comment it ships with. Fix: `[ ! -d ] || [ ! -r ]`, message updated to match ("does not exist or is not readable"). 2. [Nit] memory-audit.yml: 'this is not plugin code' dangled after two inserted sentences about war-memory-lint.test.sh — a file that IS plugin code (skills/ ships as the plugin), so 'this' could bind to the wrapper and read as the opposite of the truth. Fix names the referent: "This WORKFLOW is WAR-repo-only — it is not plugin code." Comment-only; name/on/permissions/ steps byte-identical. 3. [Nit] No meta-test exercised the wrapper's DEFAULT target — all three cases override $1, so a default silently repointed at an existing wrong dir (e.g. docs/, which exists and holds no top-level .md) would pass every meta-test AND print "lint: clean" exit 0 in the gate forever. Fix: fourth case spawns the wrapper no-arg from tmpdir(), pinning the default target AND the cwd-independence the plan mandates. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…prompt surfaces (#1131) Sub-issue #1131 (#1082). A condition owned by a not-yet-landed deps-chained sibling task read as provably-UNMET-HARD against an upstream task that fully met its own slice — the recorded incident dodged the false hold only through auditor judgment. Extends the EXISTING case (3) ownership exemption to name both owners (never a case (4)): the dispatched `endStateBlock` in workflow-template.js and the standing `execution-evidence` checklist in agents/war-auditor.md, in one commit. Reuses the `out-of-scope` finding-title token, so the handoff `endState` status derivation and every downstream route are unchanged. Case (3) now also interpolates the plan path so the seat can map conditions to owning slices without guessing — GUARDED as `${(plan && plan.file) ?? '<unset>'}`: `plan` is never entry-validated and `endStateBlock` renders at top-level scope, so a bare read would throw phase-wide on a plan-less claims-bearing phase. Tests (workflow-template.test.mjs): - criterion-11 case-3 pin rewritten as ONE ordered CASE-BOUNDED regex (anchored on the literal `(3)` marker, lazy spans, terminating on case 3's own `NEVER a hold`) - same-shape bounded pin for the new war-auditor.md bullet - behavioral: a sibling-owned out-of-scope Nit → status out-of-scope, landed - NEGATIVE: a Critical finding merely mentioning out-of-scope → unmet + held (pins the derivation's arm order) - regression: plan-less + tasks:[] + non-empty phase.endState still dispatches, never held:workflow-error (sole coverage of the guarded interpolation) Temp-break proofs: dropping the sibling clause from either surface alone reds exactly that surface's pin; the bare `${plan.file}` reds the regression case to held:workflow-error while the adjacent plan-less/zero-task test stays green; swapping the derivation arms reds only the new negative case. Records/comments swept: both workflow-template.js header comments and the schemas.md `intent` parenthetical gain the sibling-task clause; the origin lesson carries a RESOLVED (#1082) description prefix + a body note naming both surfaces. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… incident is above (#1131) [Nit] Learnings RESOLVED note says "The incident below" but the incident narrative is above it: the appended RESOLVED paragraph sits AFTER the What-happened / The-rule / Why-it-matters narrative and immediately before ## Related, so its closing sentence 'The incident below is what the carve-out encodes' pointed at nothing but the Related section. One-word fix: below -> above. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ckwards (#1132) Lock-step is not monotonic: a coherent all-four-slots DOWNGRADE moves every slot together, so the lock-step test passes it. That is exactly how a Gate-2 `docs(learnings)` commit staged from a stale publication worktree silently reverted a landed release (#1083). New fourth test in `version-slots.test.mjs` (the existing three are byte- untouched — the file diff is pure insertion, 83 add / 0 delete): ONE bounded git spawn from the suite's existing `repoRoot` git log --first-parent --diff-merges=first-parent -n 50 -p -- \ .claude-plugin/plugin.json parsed for `+`-side `"version"` values; `readSlots()`'s canonical `plugin.json#version` must be >= the window max under a local three-component NUMERIC semver compare. No unbounded history walk, no per-commit spawn, no dependency. Measured at this implementation base (never trusted from plan text): - window yields 50 version values, max 0.14.61; working tree 0.14.61 -> green - all 50 slot-touching first-parent commits are MERGES, so the entire signal comes from merge diffs. `--diff-merges=first-parent` is therefore passed EXPLICITLY: suppressing the implication (`--diff-merges=off`) yields a NON-EMPTY 17,408-byte log with ZERO version values. Since this suite runs in every refiner-dispatched gate, that is a campaign-wide brick — the non-vacuity assertion makes it loud instead of silent. PROOFS (uncommitted scratch-fixture probe evidence; a resulting gate-audit cannot-confirm is SOFT by recorded doctrine): RED proof (End state 5) — scratch git fixture, commit 0.99.9 then a coherent all-four-slots downgrade to 0.99.8: pass 3 / fail 1 — lock-step, extraction and README-prose tests all PASS; the monotonic test FAILS naming BOTH versions: "plugin.json#version (0.99.8) is BELOW the highest version in the last 50 slot-touching first-parent commits (0.99.9) — the release tip moved BACKWARDS." Fail-open boundary, proven all three arms: (a) OPEN — non-git directory copy of the tree: 4/4 pass, note "fail-open — unusable git context, `git log` exited 128: fatal: not a git repository" (b) OPEN — git repo with zero slot-touching commits in the window (0-byte log): 4/4 pass, note "fail-open — empty log, no slot-touching commits" (c) CLOSED — merge-only fixture (the merge is the ONLY slot-touching first-parent commit, the live campaign shape) with the pin suppressed: non-empty 152-byte log, zero parses -> RED, "the PARSE is broken …, not an empty window; a broken parse never passes vacuously". The same fixture with the pin PRESENT is 4/4 green, so the `--diff-merges=first-parent` pin is proven load-bearing. The assertion message names the incident class, the remedy, and the remedy OWNER: the Lead/operator restores all four slots in ONE dedicated commit on the affected branch tip — never a parallel task-branch edit, slot files are release-class shared files — and the test stays red until that restore lands. CLAUDE.md: the Releasing paragraph's `version-slots.test.mjs` sentence now also names the monotonic floor and its fail-open boundary, keeping the standing doc truthful. Sweep (plan §4.4, this task's share; leader-strip -> whitespace-normalize -> case-insensitive grep for "lock-step"): - CLAUDE.md — 2 hits, both in the amended Releasing paragraph (CONTROL fired) - skills/war/assets/version-slots.test.mjs — 9 hits (CONTROL fired); the pre-existing "Lock-step: each slot is asserted …" comment describes that test's own mechanism and stays true without the floor - README.md — ZERO hits: disposition MOOT at this base, exactly as the plan's red-team round 1 measured (the `## Status` blurb rotated to plan 4's 0.14.61 release prose, which says nothing about version slots) - docs/learnings/gate2-…-revert-a-release-bump.md — 8 hits, Task 1.4's file: REPORTED, not edited. Its description ("the test checks lock-step, not monotonic increase") and body lines "locks the four release slots in lock-step … Lock-step ≠ monotonic" are now stale as of this commit — the `MITIGATED (#1083):` prefix and body note are Task 1.4's End state 8. Gate green at this base: node --test 'skills/**/*.test.mjs' 933/933, shell suite exit 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…touching commits, not raw first-parent commits (#1132) Absorb three converging Nit findings on the same string (the empty-log fail-open diagnostic in the monotonic-floor test): 1. [Nit] Empty-log fail-open diagnostic mis-describes the window bound (-n bounds OUTPUT commits, not traversal) and contradicts the assertion message — with the plugin.json pathspec, -n 50 limits commits OUTPUT after history simplification, so an empty log means ZERO slot-touching commits in the entire reachable first-parent history, not "the last 50 commits happened not to touch the slot"; the assertion message already used the correct phrasing, so the two messages contradicted each other. 2. [Nit] Empty-log fail-open diagnostic misdescribes the window: -n 50 counts slot-touching commits, not raw first-parent commits — verified empirically (the 50 returned commits span far more than 50 raw first-parent commits); only this one diagnostic drifted from the header comment and the RED assertion, sending a reader to the wrong place. 3. [Nit] Fail-open empty-log diagnostic mis-describes the window bound ("the last 50 first-parent commits") — git walks as far back as needed to collect 50 MATCHING commits; a single lagging string, not a mechanic defect. Fix: reword the diagnostic to "git found no slot-touching first-parent commits at all (window: the last ${WINDOW} slot-touching commits; shallow clone, fresh history, or a moved plugin.json path)". Message-only; no behavior change, no assertion weakened. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…reuse refusal (#1133) Closes the procedural and provisioning layers of #1083 — a Gate-2 docs(learnings) commit staged from a stale publication worktree silently reverted a landed release while the lock-step version guard stayed green. Three edits, one hazard: - skills/war/SKILL.md — the Gate-2 promotion flow now lists the docs commit's staged file set (`git show --name-only --format= HEAD`) between the `docs(learnings): phase N` commit step and the `ensure-origin` push, and refuses to push when any path falls outside the resolved promotion destination or `CLAUDE.md`. This is the ROOT-CAUSE probe, one level above the version slots: the incident mechanism is a bulk stage of stale tracked files, so it catches a stale skill or hook the same way it catches a stale slot. - skill-doc-contracts.test.mjs — new D22 row (next free number resolved from the file at implementation time) locking that duty with ONE ordered match: commit step -> `--name-only` probe -> do-not-push clause -> `ensure-origin`. One regex pins the pairing AND the position. Never two presence checks. RED proof: removing the probe token alone, and removing the do-not-push clause alone, each fail D22 and only D22 (11 pass / 1 fail, both ways). - provision-worktrees.sh — `cmd_ensure_publication_worktree` behavior (b) (registered + present + HEAD already on the working branch) now runs the same `status --porcelain -uno` probe behaviors (c)/(d) use and refuses a dirty reuse via the EXISTING catalogued `$EX_WRONG_BRANCH`, naming the stale-staging hazard and the remove-then-re-provision remedy. Only ever refuses — never reset, never cleaned, never switched away. No new exit constant, no bare numeric literal, so the E-family census is untouched. Deliberate deviations from spec §4.3, both plan-sanctioned: - the probe is the staged FILE LIST, not the spec's version-slot re-read, which detects only the slot instance of the class; - behavior (b) exits `$EX_WRONG_BRANCH` rather than the spec's default generic exit — the catalogue's own text classifies "a dirty tree we refuse to switch/remove" there, and (d) plus `remove-publication-worktree` already exit 6 for the same class. Header comments amended in the same edit: the byte-for-byte mirror sentence and the "staleness is the CAS retry's job" claim now record the divergence — REF staleness stays the CAS's job, WORKING-TREE staleness is this subcommand's refusal, publication verb only (the refinery counterpart is a recorded non-goal). The behavior-list (b) line and the test file's block header follow. New test case P.8 pins the SPECIFIC exit code 6, never merely non-zero (a crashed script also exits non-zero), plus the stderr hazard + remedy content and the survival of the tracked modification. Delete-and-trace: removing the guard drops P.8 to exit 0 and reds three assertions. P.2 keeps its UNTRACKED sentinel at exit 0, so `-uno` stays proven. The origin lesson gains a `MITIGATED (#1083):` description prefix (net shorter than before — budget-safe) and a body section naming all three layers, including the monotonic floor produced by Task 1.3 in this same phase. Two claims that went stale with that floor ("does not assert the version increased", "version-slots.test.mjs will not warn") are corrected in place. Gate green: 933/933 JS tests, all discovered shell suites, redaction lint clean. §4.4 sweep re-run post-change with its control firing — all three doctrine tokens read accurately; SKILL.md still carries none, as the plan predicted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…fore remove (#1133) [Minor] Behavior-(b) refusal names a remedy its sibling verb deterministically refuses (remove-publication-worktree is dirty-guarded on the identical probe). The die at cmd_ensure_publication_worktree behavior (b) fires exactly when `git -C <wt> status --porcelain -uno` is non-empty, yet instructed "Run remove-publication-worktree '<path>', then re-provision" — and cmd_remove_publication_worktree guards on the byte-identical probe, so step 1 of the named remedy is guaranteed to exit 6 in the very state that produced the message. SKILL.md's Gate-2 entry heal scan and ADR 0022 (D) both reserve remove-publication-worktree for the clean arm. Fix: insert the intermediate commit-or-discard-by-hand step into the remedy sentence, keeping both P.8 grep tokens (remove-publication-worktree, re-provision) and the stale-staging hazard naming intact. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…d-state exemption (#1131, #1133) Phase-close coherence sweep, phase 1 "Evidence wiring + ownership carve-out + release floor". Two queued findings, both doc-only; no runtime surface, no version slot, no test pin touched. [Minor] Evict flow now hard-reds every gate in this repo — migration.md's "fails open once the dir is absent" is only true of CI (task 1.1, skills/_shared/war-memory-lint.test.sh:34) The wrapper's default target IS docs/learnings/, so after an eviction PR merges a fresh clone has no such dir, the existence guard exits 2, and the gate's `bash "$f" || exit 1` aborts every refiner-dispatched gate. Recorded in the wrapper's own header comment block (the finding's primary remedy): an eviction must delete this wrapper in the same PR, and migration.md §Evict step 6's CI fail-open note is true of CI only. Chose the wrapper header over amending migration.md because that playbook ships to plugin consumers, who never discover this file (the gate's `find .` runs in the target repo tree) — a repo-specific "delete this wrapper" instruction there would be wrong for every consumer. [Minor] design.md §18 still enumerates the single-owner End-state exemption ("later-phase → out-of-scope") — doc-cascade straggler outside the swept scope (task 1.2, skills/war/references/design.md:133) The spec of record enumerated the gate-audit End-state cases with one owner where the dispatched `endStateBlock`, agents/war-auditor.md, and schemas.md now name two. Extended the enumeration in place with the deps-chained-sibling clause, matching schemas.md's landed wording. Straggler outside the plan's file-anchored §4.4 sweep set, so a survey-derived correction, not a regression. Gate green: `node --test 'skills/**/*.test.mjs'` 942/942 pass; the discovered shell loop runs 27 suites (including ./skills/_shared/war-memory-lint.test.sh, which prints `lint: clean`), full gate exit 0.
…4-gate-evidence-and-release-integrity
Five project-typed lessons captured by the servitor after plan 5 phase 1 landed (tip c663340): the three origin lessons updated with MITIGATED/RESOLVED status, plus two new ones — the *.test.sh vehicle's interaction with the merge-path test floor, and the leader-strip-before-normalize-before-grep doc-sweep ordering. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…1137) Bump the four release slots in lock-step to 0.14.62, the next free patch above the live integration base: all four read 0.14.61 at this task's base (phase-1 tip 73334aa), and the monotonic floor's 50-commit first-parent window over .claude-plugin/plugin.json maxes at 0.14.61 as well. Resolved from the slots as they stand, never from a plan literal (stacked campaign releases lag cumulatively; predecessors landed .58/.59/.60/.61). Slots: plugin.json version; marketplace.json metadata.version AND plugins[0].version; README ## Status (replace-in-place, no badge). The README Status blurb describes phase 1 precisely: the redaction lint now rides the gate's existing *.test.sh self-discovery as a thin wrapper (pre-merge, captured in the gate log, exit 2 on an absent target); the gate-audit End-state ownership exemption names deps-chained sibling tasks on both prompt surfaces via the existing out-of-scope token; and the version guard gains a monotonic floor plus a Gate-2 pre-push staged-file-list check and a publication-worktree dirty-reuse refusal through the existing $EX_WRONG_BRANCH. It makes no claim that resolveGate, any enum, any floor exit code, or CI behavior changed. Gate green: 942/942 JS tests, full shell loop exit 0 — including the monotonic floor, which this bump is the first to clear (End state 11). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… claimed a vacuous GREEN (task 2.1, README.md:340)
Queued phase-close finding [Minor]: "README blurb's unpinned-`--diff-merges`
counterfactual claims a vacuous GREEN — the landed non-vacuity assert makes it
a loud RED, and the blurb contradicts itself two clauses later (task 2.1,
README.md:340)".
Both halves were wrong against the landed tree, and both are re-measured here:
(a) Unpinned does NOT yield zero versions today. `git log --first-parent -n 50
-p -- .claude-plugin/plugin.json` with no `--diff-merges` yields 50 version
hits at this base — exactly what the source comment in
`skills/war/assets/version-slots.test.mjs` says ("which modern git shows
only because `--first-parent` IMPLIES that flag"). The zero-version result
needs an explicit `--diff-merges=off`, and it is base-scoped: at the
implementation base 73334aa the window is 50/50 merges and
`--diff-merges=off` yields ZERO, but at this polish base the window is
49 merges + this phase's own direct release commit, so the same command
yields 1. An unqualified present-tense "yields ZERO" would ship a fresh
false claim, so the blurb now mirrors the source comment's own attributed
measurement ("50 of 50 at the implementation base") instead of asserting a
counterfactual number.
(b) "green the suite vacuously" was contradicted by the landed non-vacuity
assert AND by the blurb's own next sentence. With `--diff-merges=off` the
log still emits commit headers, so `log.trim() !== ''` and
`assert.ok(seen.length > 0, ...)` fires RED.
The clause after "**pinned explicitly** — " now states the load-bearing reason
(the whole signal comes from merge diffs, which git shows today only because
`--first-parent` implies that flag; the explicit pin keeps the window from
collapsing if that display default ever shifts) and leaves the RED claim to the
existing next sentence, which already carries it. Prose after the em dash only:
the `**0.14.62**` slot literal is byte-identical, no other file is touched.
Gate green: node --test 'skills/**/*.test.mjs' 942/942; the discovered shell
loop exits 0 over 27 suites.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…4-gate-evidence-and-release-integrity
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Plan 5 of 6 — campaign
2026-07-24-standing-record-and-guard-hardening. Plan:docs/plans/2026-07-24-gate-evidence-and-release-integrity.md· Red-team CLEARED (3 rounds) · Closes #1081 #1082 #1083.What this closes
Three verified holes that let a false "all green" survive the exact checkpoint built to catch it.
#1081 — the redaction lint was structurally invisible to gate evidence. It ran only in post-push CI, so any plan End state citing it was a permanent SOFT cannot-confirm at every gate-audit. A thin
skills/_shared/war-memory-lint.test.shwrapper now rides the gate's existing*.test.shself-discovery — zero engine change:resolveGateand both hand-mirrored copies are byte-untouched, and CI keeps its triggers, steps and permissions (header comment only). The wrapper fails loud on a missing target, so a moveddocs/learnings/can never printlint: cleanforever.#1082 — an in-phase sibling's condition read as provably-UNMET-HARD. The gate-audit's only ownership exemption was "a LATER phase", so a condition owned by a not-yet-landed
deps-chained sibling held the land against a task that fully met its own slice. Case (3) now names both owners on both prompt surfaces in one commit (dispatchedendStateBlock+ standingagents/war-auditor.md), reusing theout-of-scopetitle token so every downstream route is unchanged.#1083 — lock-step ≠ monotonic. A coherent all-four-slots revert passed every guard — which is how a Gate-2 commit staged from a stale worktree once silently reverted a landed release. Three layers land: a monotonic-floor test (bounded first-parent window; fail-open only on an unusable git context or a genuinely empty window — a non-empty log parsing to zero versions is RED, never a vacuous pass), a Gate-2 pre-push staged-file-list check, and a publication-worktree dirty-reuse refusal that only ever refuses loudly (never resets, never destroys work).
Phases
c66334023f853c64 agents, 0 errors. 11 findings absorbed across 6 ace/polish commits — durable record is the commit bodies, not issues.
Release 0.14.62 resolved from the live slots at land time (all four read 0.14.61; predecessors ran 0.14.58 → 0.14.61), never from a plan literal. Verified at the landed tip:
version-slots.test.mjs4/4 green, including the new monotonic floor on its first real bump.Unexecuted backstops
None. All three plan-declared backstops were executed at Phase-1 land:
== gate(bash): ./skills/_shared/war-memory-lint.test.sh ==+lint: cleanpresent in all five captured.war/gate-*.logartifacts — End state 1 confirmed from a real refiner artifact, not the in-task proxy.memory-audit.yml's retired claim is gone; the lesson's hits are past-tense under itsMITIGATED (#1081)prefix; ADR 0024 = 0 hits; README = 0lock-stephits.render-indexREFUSED (projection 24,893 B vs the 24,400 B hard cap) because the memory-tighten archives were still unmerged, leaving the on-disk repo root at 104 hot lessons while this phase promoted 2 more. docs(learnings): tighten — archive 13 cold lessons #1127 has since merged to master, which clears the cause. Lessons were never at risk — they stayed on disk and queryable; only the projection index was stale.Learnings
5 project-typed lessons promoted through Gate 2 (lint clean, pushed
73334aa): the three origin lessons updated withMITIGATED/RESOLVEDstatus, plus two new — the*.test.shvehicle's interaction with the merge-path test floor, and the leader-strip → whitespace-normalize → grep doc-sweep ordering. The Gate-2 pre-push duty that landed in this very PR was applied to its own promotion commit.Follow-ups filed
docs/learnings/archive/; ~104 committed lessons unlinted by both CI and the new wrapper (auditor-stated: needs a decision, not a mechanical edit)Not touched, deliberately
resolveGateand both mirrors,war-config.mjs, every status enum,HARD_ESCALATION_REASONS,KNOWN_LAND_DECISIONS, every floor exit code, and CI behavior.🤖 Generated with Claude Code