From 93e5d2dd84e3ea710b49f13415ba6c3e5e300216 Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Tue, 30 Jun 2026 21:29:38 -0400 Subject: [PATCH] fix(#1525): skip deferred phases on autonomous reruns (#1846) * fix(#1525): skip deferred phases on autonomous reruns * chore(#1525): add changeset fragment * chore(#1525): fix changeset body * test(#1525): refresh install parity fixtures * test(#1525): shrink autonomous workflow * test(#1525): refresh autonomous baselines * test(#1525): tolerate Windows temp cleanup flake --- .changeset/gallant-cats-hum.md | 5 +++ docs/how-to/run-phases-autonomously.md | 7 +++ gsd-core/workflows/autonomous.md | 44 +++++++++---------- tests/autonomous-converge.test.cjs | 14 ++++++ .../golden-install-parity/antigravity.json | 2 +- .../golden-install-parity/augment.json | 2 +- .../golden-install-parity/claude.json | 2 +- .../fixtures/golden-install-parity/cline.json | 2 +- .../golden-install-parity/codebuddy.json | 2 +- .../fixtures/golden-install-parity/codex.json | 2 +- .../golden-install-parity/copilot.json | 2 +- .../golden-install-parity/cursor.json | 2 +- .../golden-install-parity/gemini.json | 2 +- .../golden-install-parity/hermes.json | 2 +- .../fixtures/golden-install-parity/kilo.json | 2 +- .../fixtures/golden-install-parity/kimi.json | 2 +- .../golden-install-parity/opencode.json | 2 +- .../fixtures/golden-install-parity/qwen.json | 2 +- .../fixtures/golden-install-parity/trae.json | 2 +- .../golden-install-parity/windsurf.json | 2 +- tests/helpers.cjs | 14 +++++- tests/workflow-size-baseline.json | 2 +- 22 files changed, 76 insertions(+), 42 deletions(-) create mode 100644 .changeset/gallant-cats-hum.md diff --git a/.changeset/gallant-cats-hum.md b/.changeset/gallant-cats-hum.md new file mode 100644 index 000000000..e795dba0b --- /dev/null +++ b/.changeset/gallant-cats-hum.md @@ -0,0 +1,5 @@ +--- +type: Fixed +pr: 1846 +--- +**Autonomous reruns now skip phases with deferred verification until you resume them explicitly** — if a prior `/gsd-autonomous` run recorded `verification_deferred_human` or `verification_deferred_gaps`, later reruns no longer drop back into the same prompt loop and instead point you at the saved resume command. (#1846) diff --git a/docs/how-to/run-phases-autonomously.md b/docs/how-to/run-phases-autonomously.md index bc2cfe987..238ca5487 100644 --- a/docs/how-to/run-phases-autonomously.md +++ b/docs/how-to/run-phases-autonomously.md @@ -161,6 +161,13 @@ If autonomous mode stops — whether you chose "Stop autonomous mode" from the b GSD skips already-complete phases automatically, so it is safe to re-run from an earlier phase number if you are not sure where the run stopped. +If a prior run recorded a `Deferred Verification` entry in `STATE.md`, later `/gsd-autonomous` reruns skip that phase instead of re-entering the same deferral prompt. Resume deferred work with the exact command shown in the table: + +```bash +/gsd:verify-work 4 # for verification_deferred_human +/gsd:plan-phase 6 --gaps # for verification_deferred_gaps +``` + --- ## Related diff --git a/gsd-core/workflows/autonomous.md b/gsd-core/workflows/autonomous.md index 6f779630a..7a9de7e26 100644 --- a/gsd-core/workflows/autonomous.md +++ b/gsd-core/workflows/autonomous.md @@ -61,7 +61,7 @@ fi When `--only` is set, also set `FROM_PHASE` to the same value so existing filter logic applies. -When `--interactive` is set, discuss runs inline with questions. When `dispatch-should-flatten` returns `false` (e.g. codex, cursor — runtimes where a backgrounded agent can still spawn subagents), plan and execute are dispatched as background agents — keeping the main context lean (only discuss conversations accumulate) and enabling overlap. When `dispatch-should-flatten` returns `true` (e.g. claude and other runtimes where backgrounded agents cannot reliably nest subagents), plan and execute run inline to preserve worktree isolation and independent verification, and phases run sequentially with their work accumulating in the main context. Either way, user input is preserved on all design decisions. +When `--interactive` is set, discuss stays inline. If `dispatch-should-flatten` returns `false`, dispatch plan and execute as background agents; if it returns `true`, run them inline and keep phases sequential. Preserve user input on all design decisions. When `PLAN_STRATEGY=converge`, the planning step MUST invoke the plan-review convergence workflow instead of `gsd-plan-phase`. `--cross-ai` is an alias for `--converge`. Forward `CONVERGENCE_ARGS` exactly as parsed so reviewer flags and `--max-cycles N` retain the same meaning as they have on `/gsd:plan-review-convergence`. @@ -125,10 +125,17 @@ Run phase discovery: ```bash INIT_MANAGER=$(gsd_run query init.manager) if [[ "$INIT_MANAGER" == @file:* ]]; then INIT_MANAGER=$(cat "${INIT_MANAGER#@file:}"); fi +STATE_CONTENT=$(cat .planning/STATE.md 2>/dev/null || true) ``` Parse the JSON `phases` array. +Parse the optional `## Deferred Verification` table from `STATE_CONTENT` into a phase-number map: +- `verification_deferred_human` -> `/gsd:verify-work ` +- `verification_deferred_gaps` -> `/gsd:plan-phase --gaps` + +**Skip deferred phases on autonomous re-entry:** drop any phase whose number appears in the deferred-phase map from this run's queue; resume it only through the recorded command. + **Filter to incomplete phases:** Keep `phase_complete !== true`, including implemented phases with `verification_status !== "passed"`. **Apply `--from N`:** If set, filter out phases where `number < FROM_PHASE` (numeric compare; handles "5.1"). @@ -180,6 +187,8 @@ Exit cleanly. | 8 | Lifecycle Orchestration | Not Started | ``` +**If any deferred phases were skipped:** display `## Deferred Verification (Skipped on Re-entry)` with the skipped rows and resume commands, then omit them from this run's queue. + **Fetch details for each phase:** ```bash @@ -202,9 +211,7 @@ For the current phase, display the progress banner: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ``` -Where N = current phase number (from the ROADMAP, e.g., 63), T = total milestone phases (from `phase_count` parsed in initialize step, e.g., 67). **Important:** T must be `phase_count` (the total number of phases in this milestone), NOT the count of remaining/incomplete phases. When phases are numbered 61-67, T=7 and the banner should read `Phase 63/7` (phase 63, 7 total in milestone), not `Phase 63/3` (which would confuse 3 remaining with 3 total). P = percentage of all milestone phases completed so far. Calculate P as: (number of phases with `disk_status` "complete" from the latest `roadmap analyze` / T × 100). Use █ for filled and ░ for empty segments in the progress bar (8 characters wide). - -**Alternative display when phase numbers exceed total** (e.g., multi-milestone projects where phases are numbered globally): If N > T (phase number exceeds milestone phase count), use the format `Phase {N} ({position}/{T})` where `position` is the 1-based index of this phase among incomplete phases being processed. This prevents confusing displays like "Phase 63/5". +Where N is the ROADMAP phase number, T is the milestone `phase_count`, and P = completed milestone phases / T × 100. Use `phase_count`, not remaining phases: phase 63 in a 7-phase milestone is `Phase 63/7`, not `Phase 63/3`. If N > T, render `Phase {N} ({position}/{T})`. Use an 8-character bar with █ and ░. **3a. Smart Discuss** @@ -496,13 +503,7 @@ Display `Phase ${PHASE_NUM} ✅ ${PHASE_NAME} — Verification passed`, run `@~/ **If `human_needed`:** -Read `human_verification` items. In text mode (`--text` or init `text_mode=true`), replace AskUserQuestion with a numbered list and typed choice. Otherwise display items and ask: -- **question:** "Phase ${PHASE_NUM} has items needing manual verification. Validate now or continue to next phase?" -- **options:** "Validate now" / "Continue without validation" - -On **"Validate now"**: Present items, then ask: -- **question:** "Validation result?" -- **options:** "All good — continue" / "Found issues" +Read `human_verification` items. In text mode (`--text` or init `text_mode=true`), replace AskUserQuestion with a plain-text numbered list. Otherwise ask whether to validate now or continue without validation. If validating now, present items, then ask `Validation result?` with `All good — continue` / `Found issues`. On "All good — continue": set VERIFICATION frontmatter `status: passed`, display `Phase ${PHASE_NUM} ✅ Human validation passed`, run `@~/.claude/gsd-core/workflows/transition.md`, then iterate. @@ -528,9 +529,7 @@ Read gap score/items from VERIFICATION.md. Display: Score: {N}/{M} must-haves verified ``` -Ask user via AskUserQuestion: -- **question:** "Gaps found in phase ${PHASE_NUM}. How to proceed?" -- **options:** "Run gap closure" / "Continue without fixing" / "Stop autonomous mode" +Ask how to proceed: `Run gap closure` / `Continue without fixing` / `Stop autonomous mode`. On **"Run gap closure"**: one gap-closure attempt: @@ -554,9 +553,7 @@ If `passed` or `human_needed`: route normally. If `stale`: handle_blocker: "Stale verification for phase ${PHASE_NUM}." -If still `gaps_found` after this retry: Display "Gaps persist after closure attempt." and ask via AskUserQuestion: -- **question:** "Gap closure did not fully resolve issues. How to proceed?" -- **options:** "Continue anyway" / "Stop autonomous mode" +If still `gaps_found` after this retry, display `Gaps persist after closure attempt.` and ask `Continue anyway` / `Stop autonomous mode`. On "Continue anyway": record `verification_deferred_gaps` using the table below, display `Phase ${PHASE_NUM} ⏭ verification_deferred_gaps — resume with /gsd:plan-phase ${PHASE_NUM} --gaps`, then handle_blocker: "Verification gaps deferred for phase ${PHASE_NUM}." On "Stop autonomous mode": Go to handle_blocker. @@ -645,13 +642,10 @@ Proceed to lifecycle step (partial completion skips audit/complete/cleanup). Exi ```bash INIT_MANAGER=$(gsd_run query init.manager) if [[ "$INIT_MANAGER" == @file:* ]]; then INIT_MANAGER=$(cat "${INIT_MANAGER#@file:}"); fi +STATE_CONTENT=$(cat .planning/STATE.md 2>/dev/null || true) ``` -Re-filter incomplete phases using the same logic as discover_phases: -- Keep phases where `phase_complete !== true` or `verification_status !== "passed"` -- Apply `--from N` filter if originally provided -- Apply `--to N` filter if originally provided -- Sort by number ascending +Re-filter incomplete phases using discover_phases logic: keep phases where `phase_complete !== true` or `verification_status !== "passed"`, drop deferred phases from the autonomous queue, re-apply `--from` / `--to`, then sort by number ascending. Read STATE.md fresh: @@ -663,12 +657,14 @@ Check for blockers in the Blockers/Concerns section. If blockers are found, go t If incomplete phases remain: proceed to next phase, loop back to execute_phase. -**Interactive mode overlap:** When `INTERACTIVE` is set, the iterate step enables pipeline parallelism **on Codex** (on every other runtime, plan/execute run inline — see 3b/3c — so there is no overlap and phases run sequentially): +If no runnable phases remain but deferred phases were skipped, display `Autonomous run stopped with deferred verification phases still pending. Resume them with the commands listed in Deferred Verification.` Proceed to lifecycle only if every non-deferred phase is complete; otherwise go to handle_blocker. + +**Interactive mode overlap:** When `INTERACTIVE` is set, Codex can overlap discuss for Phase N+1 with background plan+execute for Phase N. Other runtimes keep plan/execute inline, so phases stay sequential: 1. After discuss completes for Phase N, dispatch plan+execute as background agents 2. Immediately start discuss for Phase N+1 (the next incomplete phase) while Phase N builds 3. Before starting plan for Phase N+1, wait for Phase N's execute agent to complete and handle its post-execution routing (verification, gap closure, etc.) -This means the user is always answering discuss questions (lightweight, interactive) while the heavy work (planning, code generation) runs in the background. The main context only accumulates discuss conversations — plan and execute contexts are isolated in their agents. (On Claude Code and all other non-Codex runtimes, plan and execute run inline, so they run sequentially and their work accumulates in the main context.) +The main context only accumulates discuss conversations; background plan/execute work stays isolated in its agents. If all phases complete, proceed to lifecycle step. diff --git a/tests/autonomous-converge.test.cjs b/tests/autonomous-converge.test.cjs index 1cb96f342..ca398e70b 100644 --- a/tests/autonomous-converge.test.cjs +++ b/tests/autonomous-converge.test.cjs @@ -139,6 +139,16 @@ describe('autonomous verification deferral contract', () => { /Gaps deferred` and proceed to iterate step/, 'gap deferral must not silently proceed to the next phase', ); + assert.match( + workflow, + /Skip deferred phases on autonomous re-entry/, + 'reruns must explicitly skip deferred verification phases', + ); + assert.match( + workflow, + /Deferred Verification \(Skipped on Re-entry\)/, + 'workflow should surface skipped deferred phases and their resume commands', + ); }); test('workflow runs normal transition post-processing after passed verification (#1526)', () => { @@ -197,6 +207,8 @@ describe('autonomous verification deferral contract', () => { ); assert.match(discoverStep, /phase_complete !== true/); assert.match(discoverStep, /verification_status !== "passed"/); + assert.match(discoverStep, /STATE_CONTENT=\$\(cat \.planning\/STATE\.md 2>\/dev\/null \|\| true\)/); + assert.match(discoverStep, /drop any phase whose number appears in the deferred-phase map/); assert.doesNotMatch(discoverStep, /ROADMAP=\$\(gsd_run query roadmap\.analyze\)/); assert.doesNotMatch(discoverStep, /disk_status !== "complete"/); @@ -207,5 +219,7 @@ describe('autonomous verification deferral contract', () => { ); assert.match(iterateStep, /phase_complete !== true/); assert.match(iterateStep, /verification_status !== "passed"/); + assert.match(iterateStep, /STATE_CONTENT=\$\(cat \.planning\/STATE\.md 2>\/dev\/null \|\| true\)/); + assert.match(iterateStep, /drop deferred phases from the autonomous queue/); }); }); diff --git a/tests/fixtures/golden-install-parity/antigravity.json b/tests/fixtures/golden-install-parity/antigravity.json index a9e012e64..35f5f6350 100644 --- a/tests/fixtures/golden-install-parity/antigravity.json +++ b/tests/fixtures/golden-install-parity/antigravity.json @@ -199,7 +199,7 @@ "gsd-core/workflows/audit-fix.md": "9787a0ef00be649c", "gsd-core/workflows/audit-milestone.md": "5e73ad8112a9b9b4", "gsd-core/workflows/audit-uat.md": "777262a63dcaacdc", - "gsd-core/workflows/autonomous.md": "f145bbc649fd8aa6", + "gsd-core/workflows/autonomous.md": "443c5f64043df8ae", "gsd-core/workflows/check-todos.md": "f6a93906922dd939", "gsd-core/workflows/cleanup.md": "53aa20672fe253bd", "gsd-core/workflows/code-review-fix.md": "715d98a6ff5931e2", diff --git a/tests/fixtures/golden-install-parity/augment.json b/tests/fixtures/golden-install-parity/augment.json index 9ee5f0921..f35b2f1cf 100644 --- a/tests/fixtures/golden-install-parity/augment.json +++ b/tests/fixtures/golden-install-parity/augment.json @@ -268,7 +268,7 @@ "gsd-core/workflows/audit-fix.md": "eaead7ea85761e5b", "gsd-core/workflows/audit-milestone.md": "fe7185bb70eafdb9", "gsd-core/workflows/audit-uat.md": "ca6f1f0ef174f793", - "gsd-core/workflows/autonomous.md": "ba6828223ba13c1f", + "gsd-core/workflows/autonomous.md": "6a69708c184ac6a7", "gsd-core/workflows/check-todos.md": "1e3026abe782bd36", "gsd-core/workflows/cleanup.md": "5cf0772521c192a4", "gsd-core/workflows/code-review-fix.md": "b442c670f8c1fe93", diff --git a/tests/fixtures/golden-install-parity/claude.json b/tests/fixtures/golden-install-parity/claude.json index 1ebaf8fac..525475ef2 100644 --- a/tests/fixtures/golden-install-parity/claude.json +++ b/tests/fixtures/golden-install-parity/claude.json @@ -198,7 +198,7 @@ "gsd-core/workflows/audit-fix.md": "eaead7ea85761e5b", "gsd-core/workflows/audit-milestone.md": "5e9a35af57344e04", "gsd-core/workflows/audit-uat.md": "69dbdec25cae2f12", - "gsd-core/workflows/autonomous.md": "c72e08a2afc08828", + "gsd-core/workflows/autonomous.md": "62d751d0fe6f14bc", "gsd-core/workflows/check-todos.md": "fa637b6cc9be647c", "gsd-core/workflows/cleanup.md": "5cf0772521c192a4", "gsd-core/workflows/code-review-fix.md": "a416c764425cbb61", diff --git a/tests/fixtures/golden-install-parity/cline.json b/tests/fixtures/golden-install-parity/cline.json index d08726364..098004a44 100644 --- a/tests/fixtures/golden-install-parity/cline.json +++ b/tests/fixtures/golden-install-parity/cline.json @@ -202,7 +202,7 @@ "gsd-core/workflows/audit-fix.md": "85a83f15012e220d", "gsd-core/workflows/audit-milestone.md": "7e68a0d33382fbca", "gsd-core/workflows/audit-uat.md": "2ab2975014fbb210", - "gsd-core/workflows/autonomous.md": "40a72366442139c2", + "gsd-core/workflows/autonomous.md": "c4d9da6056aeb782", "gsd-core/workflows/check-todos.md": "ab6239efbf42077b", "gsd-core/workflows/cleanup.md": "869f953fb25e57a7", "gsd-core/workflows/code-review-fix.md": "40d723ec1c6b45d1", diff --git a/tests/fixtures/golden-install-parity/codebuddy.json b/tests/fixtures/golden-install-parity/codebuddy.json index b73232bdf..f893c93e6 100644 --- a/tests/fixtures/golden-install-parity/codebuddy.json +++ b/tests/fixtures/golden-install-parity/codebuddy.json @@ -268,7 +268,7 @@ "gsd-core/workflows/audit-fix.md": "eaead7ea85761e5b", "gsd-core/workflows/audit-milestone.md": "fe7185bb70eafdb9", "gsd-core/workflows/audit-uat.md": "ca6f1f0ef174f793", - "gsd-core/workflows/autonomous.md": "ba6828223ba13c1f", + "gsd-core/workflows/autonomous.md": "6a69708c184ac6a7", "gsd-core/workflows/check-todos.md": "1e3026abe782bd36", "gsd-core/workflows/cleanup.md": "5cf0772521c192a4", "gsd-core/workflows/code-review-fix.md": "b442c670f8c1fe93", diff --git a/tests/fixtures/golden-install-parity/codex.json b/tests/fixtures/golden-install-parity/codex.json index 96aecc31b..2e2ec5a88 100644 --- a/tests/fixtures/golden-install-parity/codex.json +++ b/tests/fixtures/golden-install-parity/codex.json @@ -234,7 +234,7 @@ "gsd-core/workflows/audit-fix.md": "eaead7ea85761e5b", "gsd-core/workflows/audit-milestone.md": "28afeeab183e254f", "gsd-core/workflows/audit-uat.md": "b3b503e9f80dc9f5", - "gsd-core/workflows/autonomous.md": "d8ed420051f4d670", + "gsd-core/workflows/autonomous.md": "b371d3c2ccaa0f42", "gsd-core/workflows/check-todos.md": "d847aa758e18d698", "gsd-core/workflows/cleanup.md": "b990139192ab7c08", "gsd-core/workflows/code-review-fix.md": "3311f5fa61f8a90b", diff --git a/tests/fixtures/golden-install-parity/copilot.json b/tests/fixtures/golden-install-parity/copilot.json index 02b14b300..7e0db75d6 100644 --- a/tests/fixtures/golden-install-parity/copilot.json +++ b/tests/fixtures/golden-install-parity/copilot.json @@ -200,7 +200,7 @@ "gsd-core/workflows/audit-fix.md": "bd88277e075ec47f", "gsd-core/workflows/audit-milestone.md": "3c1624402b54073d", "gsd-core/workflows/audit-uat.md": "eccd8feb247425b0", - "gsd-core/workflows/autonomous.md": "00fa6860aa7653a8", + "gsd-core/workflows/autonomous.md": "44ba4186752758c0", "gsd-core/workflows/check-todos.md": "4cdc0f448c772fbf", "gsd-core/workflows/cleanup.md": "a67cfbd98eca5b86", "gsd-core/workflows/code-review-fix.md": "84d5f91b2856a1f2", diff --git a/tests/fixtures/golden-install-parity/cursor.json b/tests/fixtures/golden-install-parity/cursor.json index 1cba2cfef..db5cb98d6 100644 --- a/tests/fixtures/golden-install-parity/cursor.json +++ b/tests/fixtures/golden-install-parity/cursor.json @@ -268,7 +268,7 @@ "gsd-core/workflows/audit-fix.md": "eaead7ea85761e5b", "gsd-core/workflows/audit-milestone.md": "5e9a35af57344e04", "gsd-core/workflows/audit-uat.md": "69dbdec25cae2f12", - "gsd-core/workflows/autonomous.md": "70065ffffd8886cf", + "gsd-core/workflows/autonomous.md": "c51a8b19aa869149", "gsd-core/workflows/check-todos.md": "a993c1e7b0eff1f6", "gsd-core/workflows/cleanup.md": "2bd6ad5f107fe439", "gsd-core/workflows/code-review-fix.md": "c62547be55f13dcd", diff --git a/tests/fixtures/golden-install-parity/gemini.json b/tests/fixtures/golden-install-parity/gemini.json index 6352bcce4..ebd50cfa3 100644 --- a/tests/fixtures/golden-install-parity/gemini.json +++ b/tests/fixtures/golden-install-parity/gemini.json @@ -268,7 +268,7 @@ "gsd-core/workflows/audit-fix.md": "eaead7ea85761e5b", "gsd-core/workflows/audit-milestone.md": "fe7185bb70eafdb9", "gsd-core/workflows/audit-uat.md": "ca6f1f0ef174f793", - "gsd-core/workflows/autonomous.md": "7cc800bc9a5bfc8a", + "gsd-core/workflows/autonomous.md": "c1bbd56f92dda773", "gsd-core/workflows/check-todos.md": "34b3034a31e7dbe5", "gsd-core/workflows/cleanup.md": "e73b3969f5c10dcf", "gsd-core/workflows/code-review-fix.md": "b442c670f8c1fe93", diff --git a/tests/fixtures/golden-install-parity/hermes.json b/tests/fixtures/golden-install-parity/hermes.json index 8788fc934..104dac1c9 100644 --- a/tests/fixtures/golden-install-parity/hermes.json +++ b/tests/fixtures/golden-install-parity/hermes.json @@ -199,7 +199,7 @@ "gsd-core/workflows/audit-fix.md": "5aded4cc2682502b", "gsd-core/workflows/audit-milestone.md": "c36d1c46d4aecca7", "gsd-core/workflows/audit-uat.md": "5e93ef61f91ad0c0", - "gsd-core/workflows/autonomous.md": "7a482d2afb346e57", + "gsd-core/workflows/autonomous.md": "6f4df1e9a17a71c4", "gsd-core/workflows/check-todos.md": "8807759cc4df46c0", "gsd-core/workflows/cleanup.md": "6380894e67d2435d", "gsd-core/workflows/code-review-fix.md": "5090840d834ec31a", diff --git a/tests/fixtures/golden-install-parity/kilo.json b/tests/fixtures/golden-install-parity/kilo.json index 135dbda89..b2d3168d0 100644 --- a/tests/fixtures/golden-install-parity/kilo.json +++ b/tests/fixtures/golden-install-parity/kilo.json @@ -268,7 +268,7 @@ "gsd-core/workflows/audit-fix.md": "eaead7ea85761e5b", "gsd-core/workflows/audit-milestone.md": "5e9a35af57344e04", "gsd-core/workflows/audit-uat.md": "69dbdec25cae2f12", - "gsd-core/workflows/autonomous.md": "ac4f18090bd4b178", + "gsd-core/workflows/autonomous.md": "6ebf93b8ed1155f3", "gsd-core/workflows/check-todos.md": "cde0a3025828bad8", "gsd-core/workflows/cleanup.md": "d124682912dab9f6", "gsd-core/workflows/code-review-fix.md": "a416c764425cbb61", diff --git a/tests/fixtures/golden-install-parity/kimi.json b/tests/fixtures/golden-install-parity/kimi.json index 83a0ddd59..ed76b7f90 100644 --- a/tests/fixtures/golden-install-parity/kimi.json +++ b/tests/fixtures/golden-install-parity/kimi.json @@ -235,7 +235,7 @@ "gsd-core/workflows/audit-fix.md": "eaead7ea85761e5b", "gsd-core/workflows/audit-milestone.md": "fe7185bb70eafdb9", "gsd-core/workflows/audit-uat.md": "ca6f1f0ef174f793", - "gsd-core/workflows/autonomous.md": "ba6828223ba13c1f", + "gsd-core/workflows/autonomous.md": "6a69708c184ac6a7", "gsd-core/workflows/check-todos.md": "1e3026abe782bd36", "gsd-core/workflows/cleanup.md": "5cf0772521c192a4", "gsd-core/workflows/code-review-fix.md": "b442c670f8c1fe93", diff --git a/tests/fixtures/golden-install-parity/opencode.json b/tests/fixtures/golden-install-parity/opencode.json index 6653e1c63..e7e5e19c0 100644 --- a/tests/fixtures/golden-install-parity/opencode.json +++ b/tests/fixtures/golden-install-parity/opencode.json @@ -268,7 +268,7 @@ "gsd-core/workflows/audit-fix.md": "c7878f1dff04adf1", "gsd-core/workflows/audit-milestone.md": "99f0b884c9208dde", "gsd-core/workflows/audit-uat.md": "e530acf37fd9c699", - "gsd-core/workflows/autonomous.md": "9a19deccc06a702e", + "gsd-core/workflows/autonomous.md": "dacf6c73407fe29c", "gsd-core/workflows/check-todos.md": "42806d03eacf9ff6", "gsd-core/workflows/cleanup.md": "daca0c4f1d531a02", "gsd-core/workflows/code-review-fix.md": "618c850533bd5c9e", diff --git a/tests/fixtures/golden-install-parity/qwen.json b/tests/fixtures/golden-install-parity/qwen.json index 0dbe55b19..2a3bd360a 100644 --- a/tests/fixtures/golden-install-parity/qwen.json +++ b/tests/fixtures/golden-install-parity/qwen.json @@ -199,7 +199,7 @@ "gsd-core/workflows/audit-fix.md": "75f9d02e6924913c", "gsd-core/workflows/audit-milestone.md": "49d1978340cf89ef", "gsd-core/workflows/audit-uat.md": "b38b4e18e2abd51b", - "gsd-core/workflows/autonomous.md": "aa104d2eec3f112f", + "gsd-core/workflows/autonomous.md": "df74d15ceddb0190", "gsd-core/workflows/check-todos.md": "7fc4a39f3e83dde7", "gsd-core/workflows/cleanup.md": "804c5d481279f008", "gsd-core/workflows/code-review-fix.md": "7d6caed073170b2a", diff --git a/tests/fixtures/golden-install-parity/trae.json b/tests/fixtures/golden-install-parity/trae.json index f97735fbe..0200312b1 100644 --- a/tests/fixtures/golden-install-parity/trae.json +++ b/tests/fixtures/golden-install-parity/trae.json @@ -199,7 +199,7 @@ "gsd-core/workflows/audit-fix.md": "493e4abe4f701662", "gsd-core/workflows/audit-milestone.md": "26b2428d9a8df7b2", "gsd-core/workflows/audit-uat.md": "f02cd45df6304b06", - "gsd-core/workflows/autonomous.md": "09f390b492bc8801", + "gsd-core/workflows/autonomous.md": "e2b5525089396882", "gsd-core/workflows/check-todos.md": "e4209530a4132312", "gsd-core/workflows/cleanup.md": "78080ff25ba43f8c", "gsd-core/workflows/code-review-fix.md": "4eebfb7f1612b441", diff --git a/tests/fixtures/golden-install-parity/windsurf.json b/tests/fixtures/golden-install-parity/windsurf.json index 7aadc1ca2..e2d274294 100644 --- a/tests/fixtures/golden-install-parity/windsurf.json +++ b/tests/fixtures/golden-install-parity/windsurf.json @@ -199,7 +199,7 @@ "gsd-core/workflows/audit-fix.md": "e1ad3e58979d9eef", "gsd-core/workflows/audit-milestone.md": "e578d1ce71b172e6", "gsd-core/workflows/audit-uat.md": "0f779101b40c7229", - "gsd-core/workflows/autonomous.md": "fbd5185763707f74", + "gsd-core/workflows/autonomous.md": "605f4563acd39ae4", "gsd-core/workflows/check-todos.md": "ad2ff17a672e7101", "gsd-core/workflows/cleanup.md": "edc0bc375acdb563", "gsd-core/workflows/code-review-fix.md": "aa750f6d1abd3b9f", diff --git a/tests/helpers.cjs b/tests/helpers.cjs index 4fe19c474..3538a26a5 100644 --- a/tests/helpers.cjs +++ b/tests/helpers.cjs @@ -4,6 +4,7 @@ const { execFileSync } = require('child_process'); const fs = require('fs'); +const os = require('os'); const path = require('path'); const { createFixture } = require('./fixtures/index.cjs'); @@ -130,6 +131,7 @@ function cleanup(tmpDir) { if (typeof tmpDir !== 'string' || tmpDir.length === 0) return; const target = path.resolve(tmpDir); const cwd = path.resolve(process.cwd()); + const tmpRoot = path.resolve(os.tmpdir()); if (cwd === target || cwd.startsWith(`${target}${path.sep}`)) { // Windows cannot remove a directory that is the current working directory. process.chdir(path.dirname(target)); @@ -139,7 +141,17 @@ function cleanup(tmpDir) { // teardown runs. On POSIX the retry loop is a no-op (rmSync succeeds first try). // Budget: 20 × 250ms = 5s total — Windows Defender's deferred scan can hold // newly-written files for several seconds on cold runners. - fs.rmSync(target, { recursive: true, force: true, maxRetries: 20, retryDelay: 250 }); + try { + fs.rmSync(target, { recursive: true, force: true, maxRetries: 20, retryDelay: 250 }); + } catch (error) { + // After retries, Windows can still briefly hold temp dirs open after a timed-out + // child exits. Ignore that teardown-only flake for temp roots, but rethrow everything else. + const isTmpPath = target === tmpRoot || target.startsWith(`${tmpRoot}${path.sep}`); + const isTransientWinErr = process.platform === 'win32' + && isTmpPath + && ['EBUSY', 'ENOTEMPTY', 'EPERM'].includes(error && error.code); + if (!isTransientWinErr) throw error; + } } /** diff --git a/tests/workflow-size-baseline.json b/tests/workflow-size-baseline.json index 8a057fb7a..2abd944f9 100644 --- a/tests/workflow-size-baseline.json +++ b/tests/workflow-size-baseline.json @@ -8,7 +8,7 @@ "audit-fix.md": 10988, "audit-milestone.md": 17637, "audit-uat.md": 7425, - "autonomous.md": 42747, + "autonomous.md": 41790, "check-todos.md": 9431, "cleanup.md": 9897, "code-review-fix.md": 23890,