* 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:
5
.changeset/plucky-moles-romp.md
Normal file
5
.changeset/plucky-moles-romp.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Fixed
|
||||
pr: 3138
|
||||
---
|
||||
**Spec-phase edge resolution vocabulary realigned to the code's `Status` enum** — the workflow prose in spec-phase.md, plan-phase.md, and ui-phase.md used the retired `covered`/`backstop`-as-status vocabulary that `validateResolution` rejects. Now uses `resolved` + `verification: explicit|backstop`. (#3132)
|
||||
@@ -119,16 +119,17 @@ if [ "$APPLICABLE" = "0" ]; then
|
||||
fi
|
||||
```
|
||||
|
||||
**Edge `--auto` resolution rules (reuse spec-phase 5.5 verbatim, D-06):** auto-`covered` where a
|
||||
defensible acceptance criterion can be written (→ a plain `must_haves.truths` string); else
|
||||
auto-`backstop` → author it as a **structured flat-scalar marker** `{ statement: <the check>,
|
||||
**Edge `--auto` resolution rules (reuse spec-phase 5.5 verbatim, D-06):** auto-`resolved`
|
||||
(verification: explicit) where a defensible acceptance criterion can be written (→ a plain
|
||||
`must_haves.truths` string); else auto-`resolved` (verification: backstop) → author it as a
|
||||
**structured flat-scalar marker** `{ statement: <the check>,
|
||||
verification: backstop }` in `must_haves.truths`, NOT a prose note (the verifier branches
|
||||
deterministically on the `verification: backstop` field; a parenthetical is unparseable — the #1110
|
||||
fragility; flat scalar `verification:` key, never a nested object, ADR-550 #1278). A `backstop` truth
|
||||
the verifier cannot confirm with explicit evidence abstains → `human_needed` (reason
|
||||
`insufficient_spec`), never a silent pass (#1154; `references/honest-verifier.md`). **Never
|
||||
auto-dismiss** (a wrong dismissal is the exact silent failure this eliminates). An `unclassified` row
|
||||
stays **`unresolved`** (#1110) — never auto-`backstop`ped — and is surfaced to the planner as a flagged
|
||||
stays **`unresolved`** (#1110) — never auto-resolved with backstop — and is surfaced to the planner as a flagged
|
||||
assumption. Pass `$COVERAGE` (+ the gate's `$SPECLESS_FALLBACK_DISABLED` note) into the gsd-planner
|
||||
prompt (Step 8). When `EDGE_ABSENT=0`, `$COVERAGE` is empty and this does not run.
|
||||
|
||||
@@ -159,8 +160,8 @@ assumption — never a silent drop.
|
||||
Author the fallback report into `must_haves` with the SAME lift the SPEC path uses — only the source
|
||||
changes (the fallback report, not the SPEC):
|
||||
|
||||
- **Edges →** every `covered` edge's acceptance criterion → `must_haves.truths` as a plain string;
|
||||
every `backstop` edge → `must_haves.truths` as a structured `{ statement, verification: backstop }`
|
||||
- **Edges →** every resolved (verification: explicit) edge's acceptance criterion → `must_haves.truths` as a plain string;
|
||||
every resolved (verification: backstop) edge → `must_haves.truths` as a structured `{ statement, verification: backstop }`
|
||||
marker (NOT prose; #1110/#1278), which abstains → `human_needed` at verify time when unconfirmed
|
||||
(#1154); every `unresolved`/`unclassified` row → an explicit flagged assumption (never a silent drop).
|
||||
- **Prohibitions →** every kept prohibition → the `must_haves.prohibitions:` sibling block (NOT
|
||||
|
||||
@@ -561,8 +561,8 @@ PATTERNS_PATH=$(_gsd_field "$INIT" patterns_path)
|
||||
SPIKE_FINDINGS_PATH=$(ls ./.claude/skills/spike-findings-*/SKILL.md 2>/dev/null | head -1 || true)
|
||||
SKETCH_FINDINGS_PATH=$(ls ./.claude/skills/sketch-findings-*/SKILL.md 2>/dev/null | head -1 || true)
|
||||
|
||||
# Resolve the phase SPEC (carries the ## Edge Coverage section the planner lifts covered/
|
||||
# backstop edges from). UNCONDITIONAL — must NOT live in §4.5 Check AI-SPEC, which is skipped
|
||||
# Resolve the phase SPEC (carries the ## Edge Coverage section the planner lifts resolved
|
||||
# edges from). UNCONDITIONAL — must NOT live in §4.5 Check AI-SPEC, which is skipped
|
||||
# on non-AI phases; gating it there silently starves the planner of the SPEC (#550 review).
|
||||
# Glob the plain phase SPEC, excluding the -AI-SPEC.md / -UI-SPEC.md variants.
|
||||
PHASE_DIR_FOR_SPEC=$(_gsd_field "$INIT" phase_dir)
|
||||
@@ -703,7 +703,7 @@ Planner prompt:
|
||||
- {reviews_path} (Cross-AI Review Feedback - if --reviews; actionable findings must be incorporated or explicitly deferred/rejected in PLAN.md)
|
||||
- {AI_SPEC_PATH} (AI Design Contract — framework and evaluation strategy, if exists)
|
||||
- {UI_SPEC_PATH} (UI Design Contract — visual/interaction specs, if exists)
|
||||
- {SPEC_PATH} (Phase SPEC — carries the ## Edge Coverage section to lift covered/backstop edges from, if exists)
|
||||
- {SPEC_PATH} (Phase SPEC — carries the ## Edge Coverage section to lift resolved edges from, if exists)
|
||||
- {SPIKE_FINDINGS_PATH} (Spike Findings — validated patterns, constraints, landmines from experiments, if exists)
|
||||
- {SKETCH_FINDINGS_PATH} (Sketch Findings — validated design decisions, CSS patterns, visual direction, if exists)
|
||||
- {API_SURFACE_PATH} (API Surface — HINT ONLY, when intel capability is active; see <intel_surface_hint> below)
|
||||
@@ -775,9 +775,9 @@ Output consumed by /gsd:execute-phase. Plans need:
|
||||
- Tasks in XML format with read_first and acceptance_criteria fields (MANDATORY on every task)
|
||||
- Verification criteria
|
||||
- must_haves for goal-backward verification
|
||||
- If the SPEC has an `## Edge Coverage` section, lift every `covered` edge's acceptance criterion into `must_haves.truths` as a plain string, and every `backstop` edge **as a structured flat-scalar marker** — an object item `{ statement: <the check>, verification: backstop }`, NOT a prose note (the verifier branches deterministically on the `verification: backstop` field; a parenthetical is unparseable — the #1110 fragility). Use a flat scalar `verification:` continuation key, never a nested object (ADR-550 #1278). At verify time a `backstop` truth the verifier cannot confirm with explicit evidence abstains → `human_needed` (reason `insufficient_spec`), never a silent pass (#1154; see `references/honest-verifier.md`). `unresolved` edges are explicit assumptions — surface them in the plan, do not silently drop them. **Otherwise** (`EDGE_ABSENT`): apply the SAME lift to the fallback report `{COVERAGE}` (per §C of `references/specless-probe-fallback.md`); a SPEC-supplied section is never re-run.
|
||||
- If the SPEC has an `## Edge Coverage` section, lift every resolved (verification: explicit) edge's acceptance criterion into `must_haves.truths` as a plain string, and every resolved (verification: backstop) edge **as a structured flat-scalar marker** — an object item `{ statement: <the check>, verification: backstop }`, NOT a prose note (the verifier branches deterministically on the `verification: backstop` field; a parenthetical is unparseable — the #1110 fragility). Use a flat scalar `verification:` continuation key, never a nested object (ADR-550 #1278). At verify time a `backstop` truth the verifier cannot confirm with explicit evidence abstains → `human_needed` (reason `insufficient_spec`), never a silent pass (#1154; see `references/honest-verifier.md`). `unresolved` edges are explicit assumptions — surface them in the plan, do not silently drop them. **Otherwise** (`EDGE_ABSENT`): apply the SAME lift to the fallback report `{COVERAGE}` (per §C of `references/specless-probe-fallback.md`); a SPEC-supplied section is never re-run.
|
||||
- If the SPEC has a `## Prohibitions` section, lift every resolved prohibition into the `must_haves.prohibitions:` sibling block (NOT `truths` — ADR-550 D3) with `statement`+`status`+`verification`, via the single `projectProhibitions` serializer (Hyrum — no second serializer); unresolved -> flagged assumptions, don't drop; never put a must-NOT under `truths`. **Otherwise** (`PROHIB_ABSENT`), author the recalled prohibitions into the SAME block via the SAME `projectProhibitions` contract but **descriptor-less** (no `check_*`) so each disposes flagged-unverified; never auto-dismiss. Section-level precedence + no-silent-drop equality apply (§C).
|
||||
- If a `-UI-SPEC.md` exists (resolved above as `UI_SPEC_PATH`) with a `## UI Considerations` section, lift it by the **identical rule** as `## Edge Coverage` above — `covered` → `must_haves.truths` string, `backstop` → flat scalar `{ statement, verification: backstop }`, `unresolved` → explicit planner assumption (no new verb — ADR-550 #1278/#1154; #1867). Read it from `UI_SPEC_PATH` (the SPEC glob excludes `-UI-SPEC.md`).
|
||||
- If a `-UI-SPEC.md` exists (resolved above as `UI_SPEC_PATH`) with a `## UI Considerations` section, lift it by the **identical rule** as `## Edge Coverage` above — resolved (explicit) → `must_haves.truths` string, resolved (backstop) → flat scalar `{ statement, verification: backstop }`, `unresolved` → explicit planner assumption (no new verb — ADR-550 #1278/#1154; #1867). Read it from `UI_SPEC_PATH` (the SPEC glob excludes `-UI-SPEC.md`).
|
||||
- **"Artifacts this phase produces" section (MANDATORY)** — list every symbol this phase creates: decorators, classes, functions, CLI flags, struct/dataclass fields, new file paths. The plan-review-convergence source-grounding pass reads this section to exclude newly-created symbols from drift verification; omitting it causes new symbols to be flagged for acknowledgement.
|
||||
</downstream_consumer>
|
||||
|
||||
@@ -823,8 +823,8 @@ Every task MUST include these fields — they are NOT optional:
|
||||
- [ ] Waves assigned for parallel execution
|
||||
- [ ] must_haves derived from phase goal
|
||||
- [ ] Every PLAN.md includes an "Artifacts this phase produces" section listing symbols created by this phase (decorators, classes, functions, CLI flags, struct/dataclass fields, new file paths)
|
||||
- [ ] Every SPEC ## Edge Coverage covered/backstop edge is represented in a plan's must_haves (no silent drops)
|
||||
- [ ] Every UI-SPEC ## UI Considerations covered/backstop consideration is represented in a plan's must_haves (no silent drops)
|
||||
- [ ] Every SPEC ## Edge Coverage resolved edge is represented in a plan's must_haves (no silent drops)
|
||||
- [ ] Every UI-SPEC ## UI Considerations resolved consideration is represented in a plan's must_haves (no silent drops)
|
||||
- [ ] Every SPEC ## Prohibitions resolved item is represented in a plan's must_haves.prohibitions (no silent drops)
|
||||
</quality_gate>
|
||||
```
|
||||
|
||||
@@ -251,7 +251,8 @@ if ! node -e 'const a=require(process.argv[1]);if(!Array.isArray(a)||a.length===
|
||||
exit 1
|
||||
fi
|
||||
# Invoke the compiled engine and CAPTURE its report — it computes which categories apply per
|
||||
# requirement. The covered/backstop/dismissed/unresolved rows in $COVERAGE drive the
|
||||
# requirement. The resolved/dismissed/unresolved rows in $COVERAGE (resolved items carry
|
||||
# verification: explicit|backstop) drive the
|
||||
# resolution loop below (canonical taxonomy compute, NOT LLM re-derivation from prose).
|
||||
# The engine FAILS CLOSED (exit 2) on an invalid authored shape or bad input — so the capture
|
||||
# MUST be exit-checked. A bare `COVERAGE=$(node …)` swallows that exit code, leaves $COVERAGE
|
||||
@@ -288,14 +289,15 @@ an empty `## Edge Coverage` section after explicit confirmation.
|
||||
For each Requirement gathered so far:
|
||||
1. Classify its shape and raise only applicable edge categories (relevance filter — see
|
||||
the taxonomy in the reference). Reuse any edges the Round-4 Failure Analyst already
|
||||
surfaced as pre-`covered`.
|
||||
surfaced as pre-resolved.
|
||||
2. For each raised category, propose a CONCRETE candidate edge (not "consider
|
||||
boundaries" — e.g. "R2 merges intervals; what about `[[1,2],[2,3]]` that only touch?").
|
||||
3. Resolve each with the user (AskUserQuestion; text mode → numbered list):
|
||||
- **Specify it** → write a new pass/fail line into Acceptance Criteria AND mark the
|
||||
edge `covered`.
|
||||
edge `resolved` with `verification: explicit`.
|
||||
- **Dismiss (reason)** → mark `dismissed` with a required non-empty reason.
|
||||
- **Backstop with a test** → mark `backstop`; note "held-out edge test" for plan-phase.
|
||||
- **Backstop with a test** → mark `resolved` with `verification: backstop`; note
|
||||
"held-out edge test" for plan-phase.
|
||||
- **Defer** → leave `unresolved`.
|
||||
- An `unclassified` row (probe `unclassified — review manually`) means the requirement's
|
||||
prose matched no shape cue (#1110) — treat it like any other candidate (**Specify**,
|
||||
@@ -311,13 +313,14 @@ For each Requirement gathered so far:
|
||||
- On "anyway": write SPEC.md with those rows marked `⚠ Edge unresolved — planner must
|
||||
treat as assumption`.
|
||||
|
||||
**`--auto` mode:** auto-`covered` where a defensible acceptance criterion can be written;
|
||||
otherwise auto-`backstop` (never auto-dismiss — a wrong dismissal is the exact silent
|
||||
failure being eliminated). Log: `[auto] edge coverage: C covered, B backstop, U unresolved`.
|
||||
**`--auto` mode:** auto-`resolved` (verification: explicit) where a defensible acceptance
|
||||
criterion can be written; otherwise auto-`resolved` (verification: backstop) (never
|
||||
auto-dismiss — a wrong dismissal is the exact silent failure being eliminated). Log:
|
||||
`[auto] edge coverage: E explicit, B backstop, U unresolved`.
|
||||
|
||||
**`unclassified` exception (#1110):** `--auto` leaves an `unclassified` candidate
|
||||
**`unresolved`** (the soft gate surfaces it as a flagged planner assumption) — it never
|
||||
auto-`backstop`s it. A missing shape is not evidence an edge exists, so minting a held-out
|
||||
auto-resolves it with `verification: backstop`. A missing shape is not evidence an edge exists, so minting a held-out
|
||||
edge obligation on a requirement that may be genuinely edge-free would be a false claim and
|
||||
risks a vacuous edge test. Leaving it `unresolved` keeps the zero-cue requirement visible
|
||||
(never a silent drop) without fabricating an edge — which is exactly #1110's purpose: surface
|
||||
@@ -425,7 +428,7 @@ downstream rather than blocking authoring.
|
||||
|
||||
Use the SPEC.md template from @~/.claude/gsd-core/templates/spec.md.
|
||||
|
||||
- Populate the **Edge Coverage** section from Step 5.5 (covered/dismissed/backstop/unresolved rows).
|
||||
- Populate the **Edge Coverage** section from Step 5.5 (resolved/dismissed/unresolved rows; resolved items carry `verification: explicit|backstop`).
|
||||
- Populate the **Prohibitions** section from Step 5.6 (resolved/dismissed/unresolved rows with the test|judgment tier).
|
||||
|
||||
**Requirements for every requirement entry:**
|
||||
|
||||
@@ -388,8 +388,8 @@ SIGNAL, not proof the element is only that kind — the confirm step, not the he
|
||||
coverage sound.
|
||||
|
||||
**Resolution loop** (mirror spec-phase 5.5): resolve each applicable consideration via
|
||||
AskUserQuestion — **Specify** (→ `covered`, write a concrete truth) / **Dismiss (reason required)** /
|
||||
**Backstop** (a held-out/visual UI-state test) / **Defer** (→ `unresolved`). An `unclassified` row is
|
||||
AskUserQuestion — **Specify** (→ `resolved`, verification: explicit; write a concrete truth) / **Dismiss (reason required)** /
|
||||
**Backstop** (→ `resolved`, verification: backstop; a held-out/visual UI-state test) / **Defer** (→ `unresolved`). An `unclassified` row is
|
||||
a manual-review nudge, not a hard block. Text mode (`workflow.text_mode` / `--text`) → numbered lists.
|
||||
|
||||
**Kind-confirmation under `--auto`.** The propose-then-confirm step above is an AskUserQuestion, so
|
||||
@@ -402,17 +402,18 @@ recover a kind that was never surfaced, so recall is fixed HERE, at kind-confirm
|
||||
resolution runs.
|
||||
|
||||
**`--auto` mode (two layers).** The adapter's `autoResolve` is the CODE floor: every applicable
|
||||
consideration auto-`backstop`s (carrying the taxonomy question as its resolution) and an
|
||||
`unclassified` candidate stays `unresolved` — it NEVER auto-`dismiss`es and never auto-backstops an
|
||||
unclassified item (#1110). On top of that floor the workflow MAY upgrade an item to `covered` when a
|
||||
consideration auto-resolves with `verification: backstop` (carrying the taxonomy question as its
|
||||
resolution) and an `unclassified` candidate stays `unresolved` — it NEVER auto-`dismiss`es and never
|
||||
auto-resolves an unclassified item with backstop (#1110). On top of that floor the workflow MAY
|
||||
upgrade an item to `resolved` (verification: explicit) when a
|
||||
defensible acceptance criterion can be written (the same judgment spec-phase 5.5 applies in prose).
|
||||
An auto `--auto` run therefore leaves un-upgraded backstops as `backstop`: at verify time each one
|
||||
An auto `--auto` run therefore leaves un-upgraded items as `resolved` (verification: backstop): at verify time each one
|
||||
with no wired evidence routes to `insufficient_spec → human_needed` — never a silent pass (#1154).
|
||||
That surfacing is the intended honest-verifier behavior, not over-flagging.
|
||||
|
||||
**Write-back.** Populate a `## UI Considerations` section in the UI-SPEC from the resolved
|
||||
considerations, in the format the shipped plan-phase `## UI Considerations` lift rule reads:
|
||||
`covered` → a truth string; `backstop` → a flat scalar `{ statement, verification: backstop }`;
|
||||
`resolved` (explicit) → a truth string; `resolved` (backstop) → a flat scalar `{ statement, verification: backstop }`;
|
||||
`unresolved` → an explicit `⚠ unresolved — planner must treat as assumption` row. Empty-state and
|
||||
error-state COPY stays in `## Copywriting Contract` — the considerations section covers shape-rooted
|
||||
STATE coverage and REFERENCES those rows rather than restating the copy (de-dup). IDEMPOTENT: if a
|
||||
|
||||
@@ -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'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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"');
|
||||
});
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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').",
|
||||
|
||||
6
tests/emitted-drift-acks/3132-retired-status-vocab.json
Normal file
6
tests/emitted-drift-acks/3132-retired-status-vocab.json
Normal 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."
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user