Skip to content

Plan 3: recovery re-merge dispatch coherence — submodule scoping on every retry, ADR 0019/0040 cross-reference, rot-proof capture comment - #1117

Merged
Ljferrer merged 10 commits into
masterfrom
dev/2026-07-24-recovery-re-merge-dispatch-coherence
Jul 26, 2026
Merged

Plan 3: recovery re-merge dispatch coherence — submodule scoping on every retry, ADR 0019/0040 cross-reference, rot-proof capture comment#1117
Ljferrer merged 10 commits into
masterfrom
dev/2026-07-24-recovery-re-merge-dispatch-coherence

Conversation

@Ljferrer

Copy link
Copy Markdown
Owner

Plan 3 of the 2026-07-24-standing-record-and-guard-hardening campaign (stack-and-plow, ADR 0011).
Stacked on plan 2 — base is dev/2026-07-24-runbook-and-standing-record-coherence (PR #1109), not master.

What this closes

After plan 4 rebuilt merge-time gate-failure recovery, three coherence gaps remained — one per layer
of the record. All three are closed here.

  1. Retry re-merges dispatched submodule tasks unscoped. The per-task SUBMODULE TASK: note —
    the paragraph rooting a submodule task's rebase and gate in its own checkout via
    targetRepo/targetBase — rode only the initial merge-task prompt. A taskType:'submodule'
    task whose first merge tripped a recoverable route was re-dispatched pointing at the
    superproject. The note now rides all three recovery re-merges in
    skills/war/assets/workflow-template.jsmerge:<taskId>:floor-retry:r<round>,
    merge:<taskId>:environment-proceed, merge:<taskId>:baseline-proceed. It is the empty string
    off the submodule path, so every non-submodule prompt stays byte-identical.
  2. gateCaptureClause's header comment had rotted to 2 of its 3 consumer sites. The
    enumeration is replaced by a count-free evidence-contract statement naming the captureUses
    drift guard in workflow-template.test.mjs as the arbiter of the site list — the enumeration is
    exactly what rotted, so the fix is to stop enumerating.
  3. ADR 0019 still taught the retired 0-fix-round env-blocked route that ADR 0040 replaced, and
    neither record pointed at the other. Repaired in both directions without retro-editing:
    0019 gains a dated ## Amendment (2026-07-24) (original Decision prose byte-intact); 0040's
    ## Relationship to prior ADRs gains one Supersedes one row of ADR 0019 bullet.

Proof is by dispatch capture, not source-occurrence counting: three new sibling tests drive a
taskType:'submodule' fixture through each recovery route and assert the prompt located by
dispatch label
carries both the SUBMODULE TASK: marker and the fixture's targetRepo.

Phases

phase tasks result
1 — Retry-dispatch scoping + record coherence 1.1 (#1112), 1.2 (#1113) landed 43a6dc4; 2/2 merged, unanimous approve, 0 fix rounds, 0 escalations; phase-close polish merged 48f93e3
2 — Release 2.1 (#1116) landed a84b42d4; release 0.14.60, four slots in lock-step

Epics #1110 (phase 1) and #1111 (phase 2), both closed status:done; issue-lifecycle floor assert
rc 0 on both. Red-team: CLEARED-WITH-NOTES in one round (docs/red-team/2026-07-24-recovery-re-merge-dispatch-coherence.md),
3 minor roots auto-fixed pre-execution.

Unexecuted backstops

None. The plan declared one deferred validation and it was EXECUTED — PASS @ 43a6dc4:

Integrated-tip sweep re-check — the three §4.5 greps on the landed Phase-1 tip. Deferred because
the two Phase-1 tasks adjudicate at their own frozen bases, while the coherence claims (End states
1, 7) are properties of the integrated tip after the serial merge queue. Runner: the Lead at
Phase-1 land, before dispatching Phase 2.

Result: submodMergeNote = 5; env-blocked (case-insensitive) = 6 in ADR 0019 / 5 in ADR 0040, every
hit adjudicated; token hunt = 10 in the js (down from 11 — exactly the removed enumeration sentence)
/ 67 in the test, every js hit adjudicated a keep.

End state 9 was reported SOFT cannot-confirm by the gate-audit (no gate-log artifact threaded to
the end-state pass; an auditor cannot execute the gate) and was closed by the Lead executing
version-slots.test.mjs at the landed tip — 3/3 green, four slots all 0.14.60.

Follow-ups filed

Source issues

#1032, #1033, #1034 close at the campaign PR per the plan's ## Notes map (#1032→1.1,
#1033→1.2, #1034→1.1) — deliberately not closed here.

One note recorded, not held: the release blurb's colon-scoped No behavior change: label reads
as a headline contradiction against the paragraph's own opening sentence. Strictly read it is true
(every enumerated surface is verifiably untouched) and it is the anti-overclaim device the plan
mandated; the nit is cross-release readability.

⚠️ Merge order is bottom-up. This PR sits above #1097 (plan 1) and #1109 (plan 2) and must not
be merged before them.

🤖 Generated with Claude Code

WAR Test and others added 10 commits July 25, 2026 00:36
…ence r1 (3 minor roots auto-fixed: real Scope-paragraph anchors, case-insensitive sweeps, both-routes amendment body)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… rot-proof the gateCaptureClause comment (#1112)

A taskType:'submodule' task whose first merge trips a recoverable route was
re-dispatched UNSCOPED: the SUBMODULE TASK targetRepo/targetBase paragraph rode
only the initial merge-task prompt. The floor-retry re-merge
(merge:<taskId>:floor-retry:r<round>), the environment-proceed re-merge and the
baseline-proceed re-merge all omitted it, so the refiner would rebase and gate in
the SUPERPROJECT on any retry.

Fix: append the already-in-scope `submodMergeNote` const as the final prompt
segment at each of the three retry dispatches, mirroring the initial merge-task
prompt's trailing placement (after the requiresPackaging ternary). No helper, no
re-derivation, no dispatch-option, enum, schema or routing change. The const is
'' for non-submodule tasks, so every existing non-submodule prompt is
byte-identical — no existing prompt-content test moved (932/932 green).

No agents/war-refiner.md mirror edit — deliberate, and this is where the auditor
adjudicates it. The standing refiner doc's submodule-phase scope sentence already
makes the generic claim this change brings the dispatched surface up to:

  "All merge-task and land-phase steps below run with `<taskWorktree>` and
   `<_refinery>` rooted in the submodule checkout."

"All merge-task ... steps" already covers the retries; the dispatched prompts were
the surface that diverged. There is no same-commit standing mirror edit to miss
(plan Notes / spec §2).

Comment rot-proofing: the gateCaptureClause (D5) header comment enumerated 2 of
its 3 consumer sites. Its site list is now count-free — the clause rides the
dispatched merge-task prompts whose evidence contract REQUIRES the captured
fully-green gate for the post-merge gate-audit (ADR 0024) — and names the
captureUses drift guard in workflow-template.test.mjs as the arbiter. Deliberately
NOT universally quantified over merge-task prompts: the baseline-proceed re-merge
(proceed-over contract) and the class-exempt phase-close polish merge (fail-open
discard, feeds no gate-audit seat) legitimately carry no clause, so a universal
wording would be false at birth — the same rot class as the enumeration it
replaces. No call site added or removed; captureUses stays green at exactly 3.

Regression proof: three sibling dispatch-capture tests (never an extension of the
T4 #297 threading test, whose header enumeration stays byte-untouched), each
driving one recovery route through the harness that already stubs it and reading
the captured prompt BY DISPATCH LABEL — asserting both the SUBMODULE TASK marker
and the fixture's targetRepo. Per-route RED probe run and restored: removing one
route's append REDs exactly that route's test (1 fail / 337 pass, each time).

Sub-issue #1112. Plan: docs/plans/2026-07-24-recovery-re-merge-dispatch-coherence.md

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…t class-route record points both ways (#1113)

ADR 0019: append `## Amendment (2026-07-24): the environment class-route is
superseded by ADR 0040` after the existing References section. It supersedes
exactly the environment-classified 0-fix-round env-blocked route — the
class-routes-doctrine row in ## Decision and the **Scope.** paragraph's
"routes as env-blocked" restatement, both under the single ## Decision
heading (ADR 0019 carries no ## Scope heading, per red-team round-1
adjudication). States the live routing completely: one bounded
environment-proceed re-run per gate site that must come back fully green
(never a proceed-over, ADR 0040 SS C); exhaustion at the merge site
HARD-escalates via 'escalate'; exhaustion at the land site falls back to
held:land-failed (reason env-blocked) with the retry provably spent. States
explicitly that the original Decision text is not retro-edited and that the
introduced/baseline routes and the target-derived execution values stand
unchanged. Original prose above the amendment is byte-intact.

ADR 0040: add one "Supersedes one row of ADR 0019" bullet to
## Relationship to prior ADRs, appended after the existing five (0005, 0025,
0017, 0023, 0024), which are untouched.

Same-scope survey: grep -in "env-blocked" over both ADR files adjudicated —
every ADR-0019 hit sits either inside byte-intact original text now covered
by the amendment, or inside the amendment itself; ADR 0040's own
env-blocked mentions are pre-existing and accurate, no edit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…1113 absorb polish)

[Minor] Amendment's "Live routing" omits ADR 0040 §(B)'s no-chaining bound — the one
live case where 0019's superseded row still governs.

Rationale: the amendment stated unconditionally that an environment-classified gate
failure "now earns exactly one bounded environment-proceed re-run per gate site", but
ADR 0040 §(B) is explicit that a baseline-proceed re-merge/re-land that then fails
environment keeps today's routing (soft env-blocked / held:land-failed) and does NOT
earn an environment-proceed — so ADR 0019's zero-fix-round env-blocked row survives as
the chained-case fallback at both gate sites. A reader landing on 0019 was told a retry
is always earned, which is false on that path (the same defect class the landed D21
guard in skill-doc-contracts.test.mjs #1039 polices). Fix: one additive clause appended
to the Live routing paragraph naming the never-a-second / never-chained bound and the
chained-case fallback. Single file, additive, no anchor change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ub-loop is EITHER-status, requiresTest:true is prompt-shape only

Phase-close coherence sweep, WAR phase 1 "Retry-dispatch scoping + record coherence".
Fixes the one queued finding:

  [Nit] New fixture comment overclaims: the floor-retry sub-loop is entered on
  EITHER floor status, and requiresTest:true is inert in this stub harness
  (task 1.1, skills/war/assets/workflow-template.test.mjs:5019)

Both halves of the old rationale sentence were false against the live engine:
workflow-template.js declares `const FLOOR_STATUSES = ['no-test', 'unpackaged']`
and enters the sub-loop on either (`if (mr && FLOOR_STATUSES.includes(mr.status))`,
whose own comment above reads "on EITHER floor status (no-test OR unpackaged)");
and runNoTestLoop's first Refine call returns `{ mode:'merge-task', status:'no-test' }`
unconditionally, so requiresTest:true changes only the dispatched prompt's
requiresTest ternary here, not route reachability.

Comment-only. The fixture value stays — plan-mandated (docs/plans/2026-07-24-recovery-re-merge-dispatch-coherence.md,
Task 1.1 floor-retry sequencing) and correct; no assertion touched, no test moves
either way. Same comment-overclaim class as the recorded lesson
entry-validation-unconditional-phase-field-check-comment-overclaims-runtime-path.

Gate green at this commit: node --test 'skills/**/*.test.mjs' => 932/932 pass;
26 *.test.sh suites all pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…se-1 into dev/2026-07-24-recovery-re-merge-dispatch-coherence
…40 coherence (#1116)

Bump all four release slots in lock-step: .claude-plugin/plugin.json version,
.claude-plugin/marketplace.json metadata.version AND plugins[0].version, and the
README.md ## Status paragraph (replace-in-place, no badge).

Version resolution (End state 9): resolved from the four slots AS THEY STAND at
this task's frozen base, never from a plan literal. All four read 0.14.59 at base
(landed by this campaign's plan 2, `chore(release): 0.14.59 — standing-record
coherence (#1108)`, which is in this branch's history), so the next free patch is
0.14.60. No ref in the repo carries 0.14.60. version-slots.test.mjs is the
lock-step arbiter and is green.

Release blurb describes the Phase-1 diff as landed, not as planned:
- submodMergeNote appended as the final prompt segment at all three retry merge
  dispatches (merge:<taskId>:floor-retry:r<round>, :environment-proceed,
  :baseline-proceed); '' off the submodule path, so non-submodule prompts are
  byte-identical
- three new sibling dispatch-capture tests asserting the captured prompt BY
  DISPATCH LABEL carries the SUBMODULE TASK: marker and the fixture targetRepo
- gateCaptureClause (D5) header comment: site enumeration dropped, invariant
  stated count-free as an evidence-contract property, captureUses named as the
  arbiter of the site list (still exactly 3 sites)
- ADR 0019 dated Amendment (2026-07-24) + ADR 0040 "Supersedes one row of ADR
  0019" bullet — the record now points both ways at routing ADR 0040 already
  shipped

The blurb makes NO claim that any recovery route, status enum, or escalation
reason changed: land-decision.mjs, every MergeResult status,
HARD_ESCALATION_REASONS / KNOWN_LAND_DECISIONS, every dispatch label, and every
merge-path floor are untouched by this plan. Its explicit "No behavior change"
sentence enumerates exactly those surfaces.

Gate run locally and fully green before commit: node --test 'skills/**/*.test.mjs'
932/932 pass, 0 fail; every *.test.sh suite exits 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…se-2 into dev/2026-07-24-recovery-re-merge-dispatch-coherence
Two type:project lessons from the Release phase of
2026-07-24-recovery-re-merge-dispatch-coherence (landed a84b42d, release 0.14.60):

- release-blurb-overstates-guard-semantics — recurrence 6 in the release-blurb
  precision chain: a colon-scoped **No behavior change:** label is technically
  true but reads as a headline contradiction when the paragraph's own opening
  sentence describes a real shipped change. Adds the sharper sub-rule (prefer a
  scoped label over the fully-generic one).
- header-comment-delegates-site-count-to-drift-guard-test-instead-of-enumerating
  — new pattern: point a shared helper's header comment at the counting drift
  guard rather than hand-enumerating consumer sites, which is what rotted.

Redaction lint clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant