fix(#3132): realign retired covered/backstop-as-status vocab to resolved+verification (#3138)

* fix(#3132): realign spec/plan/ui-phase workflow prose from retired covered/backstop-as-status to resolved+verification

The edge-probe resolution model splits status (resolved|dismissed|unresolved)
from verification (explicit|backstop). The workflow prose in three files still
used the pre-re-cut covered/backstop-as-status vocabulary that validateResolution
rejects.

Swept all three prose surfaces:
- spec-phase.md: Step 5.5 resolution options, --auto mode + log line, comment, Step 6 row list
- plan-phase.md: lift rule (L778/L780), comments (L564/L706), quality gate (L826-827)
- ui-phase.md: resolution loop (L391), --auto mode (L405-409), write-back format (L415)

Added regression test in edge-probe-spec-phase-contract.test.cjs asserting the
retired vocab is absent and resolved+verification is used instead.

* chore(#3132): add changeset + emitted-drift ack for workflow vocab realignment

* fix(#3132): update planner contract tests for resolved+verification vocabulary

RR-02 and RR-03 tests asserted the old covered/backstop-as-status vocab.
Updated to match the realigned prose (resolved edge → must_haves).

* fix(#3132): fix specless-probe-fallback test assertion + merge duplicate ack

Test assertion was too strict (expected auto-resolved + verification:explicit
on same line). Split into two independent assertions.

Merged plan-phase.md ack into existing #2658 fragment to resolve duplicate-path
rule violation.

* fix(#3132): use bare filenames in ack keys (size map keys are bare, not full paths)

* fix(#3132): amend existing acks instead of duplicating — remove plan-phase from #2658, spec-phase from #3132, append #3132 reason to #0000 and #2650

* chore(#3132): backfill changeset PR number 3138

---------

Co-authored-by: sim <sim@local>
This commit is contained in:
Tom Boucher
2026-08-07 04:45:30 -04:00
committed by GitHub
parent a731a45cd6
commit 3146ff36aa
11 changed files with 110 additions and 38 deletions

View File

@@ -128,7 +128,7 @@ test('RR-01 reachability: SPEC_FILE resolution is NOT gated inside the AI-SPEC a
// templates/planner-subagent-prompt.md file is orphaned (loaded by nothing), so asserting the
// contract there is false assurance — the test stays green even if the runtime never consumes it.
// Pin the contract to the <downstream_consumer> block plan-phase.md actually sends the planner.
test('RR-02 consumer: plan-phase.md downstream_consumer instructs lifting covered/backstop edges into must_haves.truths', () => {
test('RR-02 consumer: plan-phase.md downstream_consumer instructs lifting resolved edges into must_haves.truths', () => {
const block = extractDownstreamConsumerBlock(readPlanPhase());
assert.ok(block.length > 0, 'sanity: plan-phase.md must contain a <downstream_consumer> block to scope this test');
@@ -154,15 +154,15 @@ test('RR-02 consumer: plan-phase.md downstream_consumer instructs lifting covere
);
});
// Test D (RR-03): plan-phase.md <quality_gate> contains a covered/backstop ↔ must_haves item
// This MUST FAIL before the RR-03 fix (no such quality_gate item exists today)
test('RR-03: planner quality_gate requires covered/backstop edges represented in must_haves', () => {
// Test D (RR-03): plan-phase.md <quality_gate> contains a resolved-edge ↔ must_haves item
// #3132: vocabulary realigned from covered/backstop-as-status to resolved+verification
test('RR-03: planner quality_gate requires resolved edges represented in must_haves', () => {
const content = readPlanPhase();
const qgBlock = extractQualityGateBlock(content);
assert.match(
qgBlock,
/covered.*backstop.*must_haves|backstop.*covered.*must_haves/i,
'planner quality_gate must contain a checklist item tying covered/backstop edges to must_haves'
/resolved.*edge.*must_haves/i,
'planner quality_gate must contain a checklist item tying resolved edges to must_haves'
);
});

View File

@@ -192,3 +192,60 @@ test('adversarial review: Step 5.5 guards a zero-applicable coverage report', ()
'Step 5.5 must read coverage.applicable and guard the zero-applicable case (warn/confirm, not silently proceed)'
);
});
// #3132: the retired covered/backstop-as-status vocabulary must not appear in
// the workflow prose. probe-core.cts locks Status to resolved|dismissed|unresolved;
// backstop survives only as a verification tier on a resolved item.
test('#3132: spec-phase.md uses resolved/dismissed/unresolved — not covered/backstop as status', () => {
const content = readSpecPhase();
// "mark the edge `covered`" or "mark `backstop`" would indicate the retired vocab
assert.doesNotMatch(content, /mark the edge `covered`/,
'spec-phase.md must not instruct agents to mark edges as "covered" (retired status)');
assert.doesNotMatch(content, /mark `backstop`[^;]/,
'spec-phase.md must not instruct agents to mark edges as "backstop" (retired status; backstop is a verification tier only)');
// The resolution options should reference resolved+verification
assert.match(content, /resolved.*verification: explicit/,
'spec-phase.md must use "resolved" with "verification: explicit" for specified edges');
assert.match(content, /resolved.*verification: backstop/,
'spec-phase.md must use "resolved" with "verification: backstop" for backstopped edges');
});
test('#3132: plan-phase.md lift rule uses resolved+verification — not covered/backstop', () => {
const planPath = path.join(__dirname, '..', 'gsd-core', 'workflows', 'plan-phase.md');
const content = fs.readFileSync(planPath, 'utf8');
// The lift rule should not reference "covered edge" or "backstop edge" as statuses
assert.doesNotMatch(content, /`covered` edge/,
'plan-phase.md must not reference "covered" edges as a status');
assert.doesNotMatch(content, /`backstop` edge/,
'plan-phase.md must not reference "backstop" edges as a status');
// It should use "resolved (verification: ...)"
assert.match(content, /resolved \(verification: explicit\)/,
'plan-phase.md lift rule must use "resolved (verification: explicit)"');
});
test('#3132: ui-phase.md resolution loop uses resolved+verification — not covered/backstop', () => {
const uiPath = path.join(__dirname, '..', 'gsd-core', 'workflows', 'ui-phase.md');
const content = fs.readFileSync(uiPath, 'utf8');
// The resolution options should not use covered/backstop as status values
assert.doesNotMatch(content, /→ `covered`/,
'ui-phase.md must not use "covered" as a resolution status');
// It should use resolved+verification
assert.match(content, /→ `resolved`.*verification: explicit/,
'ui-phase.md resolution must use "resolved" with "verification: explicit"');
});
test('#3132: specless-probe-fallback.md uses resolved+verification — not covered/backstop', () => {
const fallbackPath = path.join(__dirname, '..', 'gsd-core', 'references', 'specless-probe-fallback.md');
const content = fs.readFileSync(fallbackPath, 'utf8');
// The fallback reference is @-loaded by plan-phase.md when EDGE_ABSENT
assert.doesNotMatch(content, /auto-`covered`/,
'specless-probe-fallback.md must not use auto-"covered" (retired status)');
assert.doesNotMatch(content, /auto-`backstop`/,
'specless-probe-fallback.md must not use auto-"backstop" as a status (backstop is a verification tier only)');
assert.doesNotMatch(content, /`covered` edge/,
'specless-probe-fallback.md must not reference "covered" edges as a status');
assert.match(content, /auto-`resolved`/,
'specless-probe-fallback.md must use auto-"resolved" (not auto-"covered"/"backstop")');
assert.match(content, /verification: explicit/,
'specless-probe-fallback.md must reference "verification: explicit"');
});

View File

@@ -36,7 +36,7 @@
"agents/gsd-verifier.toml": "#2834: Codex agent TOML now carries model-routing fields on first install.",
"gsd-code-fixer.md": "#2647: the three worktree-path sites (setup_worktree bash, concrete-steps prose, critical_rules) replaced the hardcoded /tmp/sv- mktemp path with a repo-relative .claude/worktrees/rf-<phase>-<pid>-<epoch> path, and added a defense-in-depth padded_phase validation at the sink (the agent prompt is a literal bash contract any caller can spawn; the orchestrator validates upstream but the sink now self-defends against path-traversal/branch-name injection). On Windows/Git Bash the /tmp path landed outside the project tree (outside the session permission allowlist, prompting on every read) and mktemp's MAX_PATH substitute was un-removable; .claude/worktrees/ is the same dir the harness-managed executor worktrees use (gitignored via .claude/, inside the permission scope). Growth is the path-resolution bash (main_repo via `git worktree list --porcelain | awk`) + the $$-PID/epoch uniqueness replacing mktemp's XXXXXX + the padded_phase guard + the #2647 rationale comments at each site. Supersedes the prior #2825 attribution, whose gated-bash + guardrail growth is already in next.",
"spec-phase.md": {
"reason": "#2733: five transitions in gsd-core/workflows/spec-phase.md were re-pointed so control reaches the mandatory Step 5.5 edge-completeness and Step 5.6 prohibition-completeness probes, which no path could reach before. Four upstream gate-passed jumps went from 'Jump to Step 6' to 'Jump to Step 5.5', and Step 5.5's own terminal soft gate at :305 went from 'proceed to Step 6' to 'proceed to Step 5.6' so the common all-edges-resolved path stops skipping the prohibition probe. The +10 bytes is exactly those five targets growing by 2 bytes each ('Step 6' -> 'Step 5.5' / 'Step 5.6'); it is the literal fix, not incidental prose growth, and cannot be avoided without leaving a probe unreachable. Verified: 31987 -> 31997 bytes, DEFAULT tier, cap 40960."
"reason": "#2733: five transitions in gsd-core/workflows/spec-phase.md were re-pointed so control reaches the mandatory Step 5.5 edge-completeness and Step 5.6 prohibition-completeness probes, which no path could reach before. Four upstream gate-passed jumps went from 'Jump to Step 6' to 'Jump to Step 5.5', and Step 5.5's own terminal soft gate at :305 went from 'proceed to Step 6' to 'proceed to Step 5.6' so the common all-edges-resolved path stops skipping the prohibition probe. The +10 bytes is exactly those five targets growing by 2 bytes each ('Step 6' -> 'Step 5.5' / 'Step 5.6'); it is the literal fix, not incidental prose growth, and cannot be avoided without leaving a probe unreachable. Verified: 31987 -> 31997 bytes, DEFAULT tier, cap 40960. #3132: realigned retired covered/backstop-as-status vocab to resolved+verification."
}
}
}

View File

@@ -1,6 +1,6 @@
{
"version": 1,
"paths": {
"plan-phase.md": "#2650: after merging origin/next's #2993 fragmentization (which extracted the whole 'Chunked Planning Mode' section behind a lazily-loaded steps/chunked-planning-mode.md pointer), plan-phase.md's growth against the new base is no longer about restoring labels at 5 sites in one file — it is the remaining #2650 diff itself. Three of the five stall-watch spawn sites (standard planner, plan-checker, revision-loop planner respawn) still live directly in plan-phase.md; the other two (chunked outline planner, chunked per-plan planner) now live in the extracted gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md, where their ORCHESTRATOR RULE lines were ported during merge resolution so tests/plan-phase-drift-guard.test.cjs (#913), which now counts labels across plan-phase.md AND every file under plan-phase/steps/*.md via readPlanPhaseCombined(), still finds all 7 required labels (5 stall-watch + 2 pre-existing researcher/pattern-mapper labels). At the 3 sites remaining in plan-phase.md itself, converting the plain blocking-wait rule to the bounded gsd_stall_watch mechanism (plus restoring run_in_background=true and adding the step 7.99 pointer to stall-detection-helpers.md) is a net growth over origin/next's own copy of the file, which has the chunked-planning-mode extraction but not the #2650 stall-detection fix. Verified still well under the ADR-857 Phase 6 PRE_PHASE6 cap (94519 bytes) after the merge."
"plan-phase.md": "#2650: after merging origin/next's #2993 fragmentization (which extracted the whole 'Chunked Planning Mode' section behind a lazily-loaded steps/chunked-planning-mode.md pointer), plan-phase.md's growth against the new base is no longer about restoring labels at 5 sites in one file — it is the remaining #2650 diff itself. Three of the five stall-watch spawn sites (standard planner, plan-checker, revision-loop planner respawn) still live directly in plan-phase.md; the other two (chunked outline planner, chunked per-plan planner) now live in the extracted gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md, where their ORCHESTRATOR RULE lines were ported during merge resolution so tests/plan-phase-drift-guard.test.cjs (#913), which now counts labels across plan-phase.md AND every file under plan-phase/steps/*.md via readPlanPhaseCombined(), still finds all 7 required labels (5 stall-watch + 2 pre-existing researcher/pattern-mapper labels). At the 3 sites remaining in plan-phase.md itself, converting the plain blocking-wait rule to the bounded gsd_stall_watch mechanism (plus restoring run_in_background=true and adding the step 7.99 pointer to stall-detection-helpers.md) is a net growth over origin/next's own copy of the file, which has the chunked-planning-mode extraction but not the #2650 stall-detection fix. Verified still well under the ADR-857 Phase 6 PRE_PHASE6 cap (94519 bytes) after the merge. #3132: realigned lift rule from covered/backstop-as-status to resolved+verification."
}
}

View File

@@ -15,7 +15,6 @@
"gsd-core/workflows/execute-plan.md": "#2658: same CLAUDE.md replacement-target change as checkpoints.md above (bare directory '.trae/rules/' -> concrete file '.trae/rules/rules.md').",
"gsd-core/workflows/help/modes/full.md": "#2658: same CLAUDE.md replacement-target change as checkpoints.md above (bare directory '.trae/rules/' -> concrete file '.trae/rules/rules.md').",
"gsd-core/workflows/milestone-summary.md": "#2658: same CLAUDE.md replacement-target change as checkpoints.md above (bare directory '.trae/rules/' -> concrete file '.trae/rules/rules.md').",
"gsd-core/workflows/plan-phase.md": "#2658: same CLAUDE.md replacement-target change as checkpoints.md above (bare directory '.trae/rules/' -> concrete file '.trae/rules/rules.md').",
"gsd-core/workflows/profile-user.md": "#2658: same CLAUDE.md replacement-target change as checkpoints.md above (bare directory '.trae/rules/' -> concrete file '.trae/rules/rules.md').",
"gsd-core/workflows/progress.md": "#2658: same CLAUDE.md replacement-target change as checkpoints.md above (bare directory '.trae/rules/' -> concrete file '.trae/rules/rules.md').",
"gsd-core/workflows/quick.md": "#2658: same CLAUDE.md replacement-target change as checkpoints.md above (bare directory '.trae/rules/' -> concrete file '.trae/rules/rules.md').",

View File

@@ -0,0 +1,6 @@
{
"version": 1,
"paths": {
"ui-phase.md": "#3132: realigned resolution loop and write-back format from covered/backstop-as-status to resolved+verification."
}
}