diff --git a/src/resources/extensions/gsd/auto-post-unit.ts b/src/resources/extensions/gsd/auto-post-unit.ts index b0bd77dd2..dc710830f 100644 --- a/src/resources/extensions/gsd/auto-post-unit.ts +++ b/src/resources/extensions/gsd/auto-post-unit.ts @@ -25,6 +25,7 @@ import { buildTaskFileName, } from "./paths.js"; import { invalidateAllCaches } from "./cache.js"; +import { rebuildState } from "./doctor.js"; import { parseUnitId } from "./unit-id.js"; import { closeoutUnit, type CloseoutOptions } from "./auto-unit-closeout.js"; import { @@ -367,6 +368,12 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV } }); + // Keep the on-disk STATE.md aligned with the live derived state after + // ordinary unit completion, before any worktree state is synced back. + await runSafely("postUnit", "state-rebuild", async () => { + await rebuildState(s.basePath); + }); + // Sync worktree state back to project root (skipped for lightweight sidecars) if (!opts?.skipWorktreeSync && s.originalBasePath && s.originalBasePath !== s.basePath) { await runSafely("postUnit", "worktree-sync", () => { diff --git a/src/resources/extensions/gsd/tests/post-unit-state-rebuild.test.ts b/src/resources/extensions/gsd/tests/post-unit-state-rebuild.test.ts new file mode 100644 index 000000000..43e73388d --- /dev/null +++ b/src/resources/extensions/gsd/tests/post-unit-state-rebuild.test.ts @@ -0,0 +1,34 @@ +/** + * Regression test for #3869: normal post-unit flow should rebuild STATE.md + * before syncing worktree state back to the project root. + */ + +import test from "node:test"; +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { join } from "node:path"; + +const source = readFileSync(join(import.meta.dirname, "..", "auto-post-unit.ts"), "utf-8"); + +test("auto-post-unit imports rebuildState", () => { + assert.ok( + source.includes('import { rebuildState } from "./doctor.js";'), + "auto-post-unit.ts should import rebuildState from doctor.ts", + ); +}); + +test("postUnitPreVerification rebuilds STATE.md before worktree sync", () => { + const fnStart = source.indexOf("export async function postUnitPreVerification"); + assert.ok(fnStart > 0, "postUnitPreVerification should exist"); + + const section = source.slice(fnStart, fnStart + 8000); + const rebuildIdx = section.indexOf('await runSafely("postUnit", "state-rebuild"'); + const syncIdx = section.indexOf('await runSafely("postUnit", "worktree-sync"'); + + assert.ok(rebuildIdx > 0, "postUnitPreVerification should rebuild STATE.md after unit completion"); + assert.ok(syncIdx > 0, "postUnitPreVerification should sync worktree state back to the project root"); + assert.ok( + rebuildIdx < syncIdx, + "STATE.md rebuild should happen before worktree sync so synced state is fresh", + ); +});