gsd-pi 2.78.1-dev.b0759e59b → 2.78.1-dev.e9d88a536
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -5
- package/dist/headless-recover.d.ts +23 -0
- package/dist/headless-recover.js +93 -0
- package/dist/headless.js +9 -0
- package/dist/help-text.js +1 -0
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/browser-tools/tools/intent.js +8 -1
- package/dist/resources/extensions/gsd/auto-dispatch.js +4 -56
- package/dist/resources/extensions/gsd/auto-post-unit.js +7 -27
- package/dist/resources/extensions/gsd/auto-start.js +1 -8
- package/dist/resources/extensions/gsd/auto-worktree.js +59 -176
- package/dist/resources/extensions/gsd/auto.js +24 -6
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +9 -77
- package/dist/resources/extensions/gsd/commands-codebase.js +2 -2
- package/dist/resources/extensions/gsd/commands-handlers.js +5 -5
- package/dist/resources/extensions/gsd/commands-logs.js +2 -2
- package/dist/resources/extensions/gsd/commands-scan.js +2 -2
- package/dist/resources/extensions/gsd/commands-ship.js +2 -2
- package/dist/resources/extensions/gsd/commands-workflow-templates.js +5 -5
- package/dist/resources/extensions/gsd/db-writer.js +16 -85
- package/dist/resources/extensions/gsd/dispatch-guard.js +6 -10
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +2 -2
- package/dist/resources/extensions/gsd/gsd-db.js +74 -8
- package/dist/resources/extensions/gsd/guided-flow.js +31 -8
- package/dist/resources/extensions/gsd/markdown-renderer.js +14 -51
- package/dist/resources/extensions/gsd/parallel-merge.js +14 -13
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +5 -2
- package/dist/resources/extensions/gsd/paths.js +35 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +6 -0
- package/dist/resources/extensions/gsd/queue-order.js +6 -1
- package/dist/resources/extensions/gsd/rethink.js +2 -2
- package/dist/resources/extensions/gsd/state.js +91 -372
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +6 -5
- package/dist/resources/extensions/gsd/tools/complete-slice.js +7 -12
- package/dist/resources/extensions/gsd/tools/complete-task.js +19 -31
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +7 -5
- package/dist/resources/extensions/gsd/workflow-manifest.js +2 -1
- package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +3 -21
- package/dist/resources/extensions/gsd/workflow-reconcile.js +3 -3
- package/dist/resources/extensions/gsd/worktree-command.js +4 -3
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
- package/dist/web/standalone/.next/server/chunks/6336.js +1 -0
- package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +6 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +56 -2
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/parse-workflow-args.test.ts +80 -0
- package/packages/mcp-server/src/workflow-tools.ts +61 -2
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/browser-tools/tools/intent.ts +13 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +4 -60
- package/src/resources/extensions/gsd/auto-post-unit.ts +7 -26
- package/src/resources/extensions/gsd/auto-start.ts +1 -8
- package/src/resources/extensions/gsd/auto-worktree.ts +61 -204
- package/src/resources/extensions/gsd/auto.ts +23 -6
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +9 -84
- package/src/resources/extensions/gsd/commands-codebase.ts +2 -2
- package/src/resources/extensions/gsd/commands-handlers.ts +5 -5
- package/src/resources/extensions/gsd/commands-logs.ts +2 -2
- package/src/resources/extensions/gsd/commands-scan.ts +2 -2
- package/src/resources/extensions/gsd/commands-ship.ts +2 -2
- package/src/resources/extensions/gsd/commands-workflow-templates.ts +5 -5
- package/src/resources/extensions/gsd/db-writer.ts +16 -83
- package/src/resources/extensions/gsd/dispatch-guard.ts +6 -11
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +2 -2
- package/src/resources/extensions/gsd/gsd-db.ts +85 -8
- package/src/resources/extensions/gsd/guided-flow.ts +35 -8
- package/src/resources/extensions/gsd/markdown-renderer.ts +13 -64
- package/src/resources/extensions/gsd/parallel-merge.ts +14 -13
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +5 -2
- package/src/resources/extensions/gsd/paths.ts +55 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +6 -0
- package/src/resources/extensions/gsd/queue-order.ts +6 -1
- package/src/resources/extensions/gsd/rethink.ts +2 -2
- package/src/resources/extensions/gsd/state.ts +91 -389
- package/src/resources/extensions/gsd/tests/artifact-corruption-2630.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +6 -0
- package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +21 -34
- package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +6 -7
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +8 -6
- package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +12 -27
- package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +18 -5
- package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +14 -16
- package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +6 -5
- package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +10 -38
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +136 -56
- package/src/resources/extensions/gsd/tests/derive-state-draft.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +119 -61
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +6 -20
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +4 -5
- package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +14 -15
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +11 -16
- package/src/resources/extensions/gsd/tests/escalation.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/gsdroot-worktree-detection.test.ts +15 -36
- package/src/resources/extensions/gsd/tests/handler-worktree-write-isolation.test.ts +57 -0
- package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +15 -15
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +15 -5
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +14 -8
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/park-milestone.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +25 -16
- package/src/resources/extensions/gsd/tests/projection-regression.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +184 -0
- package/src/resources/extensions/gsd/tests/register-hooks-compaction-checkpoint.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/resolve-ts.mjs +4 -0
- package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +3 -4
- package/src/resources/extensions/gsd/tests/slice-disk-reconcile.test.ts +10 -56
- package/src/resources/extensions/gsd/tests/stale-slice-rows.test.ts +15 -16
- package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +23 -27
- package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +13 -14
- package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +10 -33
- package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +7 -8
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +9 -15
- package/src/resources/extensions/gsd/tests/workflow-logger-wiring.test.ts +12 -7
- package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +24 -1
- package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +13 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +65 -71
- package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +26 -151
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +7 -5
- package/src/resources/extensions/gsd/tools/complete-slice.ts +7 -14
- package/src/resources/extensions/gsd/tools/complete-task.ts +19 -34
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +7 -5
- package/src/resources/extensions/gsd/workflow-manifest.ts +4 -1
- package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +2 -18
- package/src/resources/extensions/gsd/workflow-reconcile.ts +3 -3
- package/src/resources/extensions/gsd/worktree-command.ts +4 -3
- package/dist/web/standalone/.next/server/chunks/8527.js +0 -1
- /package/dist/web/standalone/.next/static/{rk1EN3FQTE6Z1yalkW_GE → oZGTPvJBQX_IDKKnuV8Bt}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{rk1EN3FQTE6Z1yalkW_GE → oZGTPvJBQX_IDKKnuV8Bt}/_ssgManifest.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Post-unit processing for auto-loop — auto-commit, doctor run,
|
|
3
|
-
* state rebuild,
|
|
3
|
+
* state rebuild, projection checks, DB tool closeout, hooks, triage, and
|
|
4
4
|
* quick-task dispatch.
|
|
5
5
|
*
|
|
6
6
|
* Split into two functions called sequentially by auto-loop with
|
|
@@ -42,7 +42,7 @@ import {
|
|
|
42
42
|
} from "./auto-recovery.js";
|
|
43
43
|
import { regenerateIfMissing } from "./workflow-projections.js";
|
|
44
44
|
import { syncStateToProjectRoot } from "./auto-worktree.js";
|
|
45
|
-
import { isDbAvailable, getTask, getSlice, getMilestone, updateTaskStatus,
|
|
45
|
+
import { isDbAvailable, getTask, getSlice, getMilestone, updateTaskStatus, _getAdapter } from "./gsd-db.js";
|
|
46
46
|
import { renderPlanCheckboxes } from "./markdown-renderer.js";
|
|
47
47
|
import { consumeSignal } from "./session-status-io.js";
|
|
48
48
|
import {
|
|
@@ -160,9 +160,8 @@ export interface RogueFileWrite {
|
|
|
160
160
|
* the completion tool. A "rogue" file is one that exists on disk but has
|
|
161
161
|
* no corresponding DB row with status "complete".
|
|
162
162
|
*
|
|
163
|
-
* This is a safety-net diagnostic (D003).
|
|
164
|
-
*
|
|
165
|
-
* detection provides immediate diagnostics so operators know the prompt failed.
|
|
163
|
+
* This is a safety-net diagnostic (D003). Runtime detection never imports
|
|
164
|
+
* markdown into the DB; explicit migration/import/recovery commands own that.
|
|
166
165
|
*/
|
|
167
166
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
168
167
|
function hasNonEmptyFields(row: Record<string, any> | null, fields: string[]): boolean {
|
|
@@ -201,14 +200,7 @@ export function detectRogueFileWrites(
|
|
|
201
200
|
|
|
202
201
|
const dbRow = getSlice(mid, sid);
|
|
203
202
|
if (!dbRow || dbRow.status !== "complete") {
|
|
204
|
-
|
|
205
|
-
// match filesystem instead of reporting as rogue (#3633).
|
|
206
|
-
try {
|
|
207
|
-
updateSliceStatus(mid, sid, "complete", new Date().toISOString());
|
|
208
|
-
} catch {
|
|
209
|
-
// If DB update fails, fall back to rogue detection so the issue is visible
|
|
210
|
-
rogues.push({ path: summaryPath, unitType, unitId });
|
|
211
|
-
}
|
|
203
|
+
rogues.push({ path: summaryPath, unitType, unitId });
|
|
212
204
|
}
|
|
213
205
|
} else if (unitType === "plan-milestone") {
|
|
214
206
|
if (!mid) return [];
|
|
@@ -738,7 +730,7 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
|
|
|
738
730
|
"error",
|
|
739
731
|
);
|
|
740
732
|
// Stop auto AND signal the outer postUnit flow to exit early.
|
|
741
|
-
// Without the flag, subsequent hooks (triage,
|
|
733
|
+
// Without the flag, subsequent hooks (triage,
|
|
742
734
|
// DB writes) would keep running against a conflicted main
|
|
743
735
|
// checkout after the loop was already told to stop.
|
|
744
736
|
const { stopAuto } = await import("./auto.js");
|
|
@@ -758,7 +750,7 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
|
|
|
758
750
|
}
|
|
759
751
|
});
|
|
760
752
|
// Exit early after stopAuto so the rest of post-unit processing
|
|
761
|
-
// (triage,
|
|
753
|
+
// (triage, hook dispatch, DB writes) doesn't run
|
|
762
754
|
// against a conflicted main checkout. Return "dispatched" to match
|
|
763
755
|
// the convention used by other stop/pauseAuto paths in this function
|
|
764
756
|
// (see signal handling earlier: stop/pause also return "dispatched").
|
|
@@ -813,17 +805,6 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
|
|
|
813
805
|
}
|
|
814
806
|
}
|
|
815
807
|
|
|
816
|
-
// Rogue file detection — safety net for LLM bypassing completion tools (D003)
|
|
817
|
-
try {
|
|
818
|
-
const rogueFiles = detectRogueFileWrites(s.currentUnit.type, s.currentUnit.id, s.basePath);
|
|
819
|
-
for (const rogue of rogueFiles) {
|
|
820
|
-
logWarning("engine", "rogue file write detected", { path: rogue.path, unitId: rogue.unitId });
|
|
821
|
-
ctx.ui.notify(`Rogue file write detected: ${rogue.path}`, "warning");
|
|
822
|
-
}
|
|
823
|
-
} catch (e) {
|
|
824
|
-
debugLog("postUnit", { phase: "rogue-detection", error: String(e) });
|
|
825
|
-
}
|
|
826
|
-
|
|
827
808
|
// ── Safety harness: post-unit validation ──
|
|
828
809
|
try {
|
|
829
810
|
const { loadEffectiveGSDPreferences } = await import("./preferences.js");
|
|
@@ -855,18 +855,11 @@ export async function bootstrapAutoSession(
|
|
|
855
855
|
const gsdDbPath = resolveProjectRootDbPath(s.basePath);
|
|
856
856
|
const gsdDirPath = join(s.basePath, ".gsd");
|
|
857
857
|
if (existsSync(gsdDirPath) && !existsSync(gsdDbPath)) {
|
|
858
|
-
const hasDecisions = existsSync(join(gsdDirPath, "DECISIONS.md"));
|
|
859
|
-
const hasRequirements = existsSync(join(gsdDirPath, "REQUIREMENTS.md"));
|
|
860
|
-
const hasMilestones = existsSync(join(gsdDirPath, "milestones"));
|
|
861
858
|
try {
|
|
862
859
|
const { openDatabase: openDb } = await import("./gsd-db.js");
|
|
863
860
|
openDb(gsdDbPath);
|
|
864
|
-
if (hasDecisions || hasRequirements || hasMilestones) {
|
|
865
|
-
const { migrateFromMarkdown } = await import("./md-importer.js");
|
|
866
|
-
migrateFromMarkdown(s.basePath);
|
|
867
|
-
}
|
|
868
861
|
} catch (err) {
|
|
869
|
-
logError("engine", `
|
|
862
|
+
logError("engine", `failed to initialize project database: ${(err as Error).message}`);
|
|
870
863
|
}
|
|
871
864
|
}
|
|
872
865
|
if (existsSync(gsdDbPath) && !isDbAvailable()) {
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
import { atomicWriteSync } from "./atomic-write.js";
|
|
33
33
|
import { execFileSync } from "node:child_process";
|
|
34
34
|
import { safeCopy, safeCopyRecursive } from "./safe-fs.js";
|
|
35
|
-
import { gsdRoot } from "./paths.js";
|
|
35
|
+
import { gsdRoot, resolveGsdPathContract } from "./paths.js";
|
|
36
36
|
import {
|
|
37
37
|
createWorktree,
|
|
38
38
|
removeWorktree,
|
|
@@ -90,9 +90,8 @@ const LEGACY_DEEP_SETUP_RUNTIME_UNIT_FILES = new Set([
|
|
|
90
90
|
// ─── Shared Constants & Helpers ─────────────────────────────────────────────
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
|
-
* Root-level .gsd/
|
|
94
|
-
*
|
|
95
|
-
* and the dispatch-level sync functions.
|
|
93
|
+
* Root-level .gsd/ projections copied from project root into worktrees for
|
|
94
|
+
* compatibility. Project root remains the canonical state/projection root.
|
|
96
95
|
*/
|
|
97
96
|
const ROOT_STATE_FILES = [
|
|
98
97
|
"DECISIONS.md",
|
|
@@ -110,6 +109,11 @@ const ROOT_STATE_FILES = [
|
|
|
110
109
|
// because the project root is authoritative for preferences (#2684).
|
|
111
110
|
] as const;
|
|
112
111
|
|
|
112
|
+
const ROOT_DIAGNOSTIC_FILES = [
|
|
113
|
+
"completed-units.json",
|
|
114
|
+
"metrics.json",
|
|
115
|
+
] as const;
|
|
116
|
+
|
|
113
117
|
/**
|
|
114
118
|
* Pop a stash entry by tracking the unique marker embedded in its message so
|
|
115
119
|
* concurrent stash operations against the same project root cannot cause us to
|
|
@@ -194,7 +198,7 @@ const VERDICT_RE = /verdict:\s*[\w-]+/i;
|
|
|
194
198
|
* destination when the source copy contains a `verdict:` field.
|
|
195
199
|
*
|
|
196
200
|
* This is the targeted fix for the UAT stuck-loop (#2821): the main
|
|
197
|
-
* safeCopyRecursive uses force:false to protect worktree-
|
|
201
|
+
* safeCopyRecursive uses force:false to protect worktree-local projection
|
|
198
202
|
* files (#1886), but ASSESSMENT files written by run-uat must be
|
|
199
203
|
* forward-synced when the project root has a verdict. Without this,
|
|
200
204
|
* the worktree retains a stale FAIL or missing ASSESSMENT and
|
|
@@ -272,9 +276,9 @@ function clearProjectRootStateFiles(basePath: string, milestoneId: string): void
|
|
|
272
276
|
}
|
|
273
277
|
}
|
|
274
278
|
|
|
275
|
-
// Clean up
|
|
276
|
-
//
|
|
277
|
-
//
|
|
279
|
+
// Clean up legacy synced milestone directories and runtime/units.
|
|
280
|
+
// Older versions copied these into the project root during execution.
|
|
281
|
+
// If they remain as untracked files when we attempt
|
|
278
282
|
// `git merge --squash`, git rejects the merge with "local changes would
|
|
279
283
|
// be overwritten", causing silent data loss (#1738).
|
|
280
284
|
const syncedDirs = [
|
|
@@ -349,8 +353,9 @@ export function syncProjectRootToWorktree(
|
|
|
349
353
|
if (!worktreePath_ || !projectRoot || worktreePath_ === projectRoot) return;
|
|
350
354
|
if (!milestoneId) return;
|
|
351
355
|
|
|
352
|
-
const
|
|
353
|
-
const
|
|
356
|
+
const contract = resolveGsdPathContract(worktreePath_, projectRoot);
|
|
357
|
+
const prGsd = contract.projectGsd;
|
|
358
|
+
const wtGsd = contract.worktreeGsd ?? join(worktreePath_, ".gsd");
|
|
354
359
|
|
|
355
360
|
// When .gsd is a symlink to the same external directory in both locations,
|
|
356
361
|
// cpSync rejects the copy because source === destination (ERR_FS_CP_EINVAL).
|
|
@@ -359,7 +364,7 @@ export function syncProjectRootToWorktree(
|
|
|
359
364
|
|
|
360
365
|
// Copy milestone directory from project root to worktree — additive only.
|
|
361
366
|
// force:false prevents cpSync from overwriting existing worktree files.
|
|
362
|
-
// Without this, worktree-
|
|
367
|
+
// Without this, worktree-local files (e.g. VALIDATION.md written
|
|
363
368
|
// by validate-milestone) get clobbered by stale project root copies,
|
|
364
369
|
// causing an infinite re-validation loop (#1886).
|
|
365
370
|
safeCopyRecursive(
|
|
@@ -369,7 +374,7 @@ export function syncProjectRootToWorktree(
|
|
|
369
374
|
);
|
|
370
375
|
|
|
371
376
|
// Force-sync ASSESSMENT files that have a verdict from project root (#2821).
|
|
372
|
-
// The additive-only copy above preserves worktree-
|
|
377
|
+
// The additive-only copy above preserves worktree-local files, but
|
|
373
378
|
// ASSESSMENT files are special: after run-uat writes a verdict and post-unit
|
|
374
379
|
// syncs it to the project root, the worktree may retain a stale copy (e.g.
|
|
375
380
|
// verdict:fail while the project root has verdict:pass from a retry). On
|
|
@@ -390,11 +395,9 @@ export function syncProjectRootToWorktree(
|
|
|
390
395
|
{ force: true },
|
|
391
396
|
);
|
|
392
397
|
|
|
393
|
-
// Delete worktree gsd.db ONLY if it is empty (0 bytes).
|
|
394
|
-
//
|
|
395
|
-
//
|
|
396
|
-
// preserved — deleting it truncates the file to 0 bytes when
|
|
397
|
-
// openDatabase re-creates it, causing "no such table" failures (#2815).
|
|
398
|
+
// Delete a legacy worktree-local gsd.db ONLY if it is empty (0 bytes).
|
|
399
|
+
// Runtime opens contract.projectDb; this cleanup only removes corrupt
|
|
400
|
+
// pre-upgrade local DB projections.
|
|
398
401
|
try {
|
|
399
402
|
const wtDb = join(wtGsd, "gsd.db");
|
|
400
403
|
let deleteSidecars = false;
|
|
@@ -428,9 +431,10 @@ export function syncProjectRootToWorktree(
|
|
|
428
431
|
}
|
|
429
432
|
|
|
430
433
|
/**
|
|
431
|
-
* Sync
|
|
434
|
+
* Sync worktree diagnostics from worktree to project root.
|
|
432
435
|
* Only runs when inside an auto-worktree (worktreePath differs from projectRoot).
|
|
433
|
-
*
|
|
436
|
+
* DB/project-root state remains authoritative; markdown projections are not
|
|
437
|
+
* copied from the worktree back to the project root.
|
|
434
438
|
* Non-fatal — sync failure should never block dispatch.
|
|
435
439
|
*/
|
|
436
440
|
export function syncStateToProjectRoot(
|
|
@@ -441,31 +445,25 @@ export function syncStateToProjectRoot(
|
|
|
441
445
|
if (!worktreePath_ || !projectRoot || worktreePath_ === projectRoot) return;
|
|
442
446
|
if (!milestoneId) return;
|
|
443
447
|
|
|
444
|
-
const
|
|
445
|
-
const
|
|
448
|
+
const contract = resolveGsdPathContract(worktreePath_, projectRoot);
|
|
449
|
+
const wtGsd = contract.worktreeGsd ?? join(worktreePath_, ".gsd");
|
|
450
|
+
const prGsd = contract.projectGsd;
|
|
446
451
|
|
|
447
452
|
// When .gsd is a symlink to the same external directory in both locations,
|
|
448
453
|
// cpSync rejects the copy because source === destination (ERR_FS_CP_EINVAL).
|
|
449
454
|
// Compare realpaths and skip when they resolve to the same physical path (#2184).
|
|
450
455
|
if (isSamePath(wtGsd, prGsd)) return;
|
|
451
456
|
|
|
452
|
-
//
|
|
453
|
-
safeCopy(join(wtGsd, "STATE.md"), join(prGsd, "STATE.md"), { force: true });
|
|
454
|
-
|
|
455
|
-
// 2. Milestone directory — ROADMAP, slice PLANs, task summaries
|
|
456
|
-
// Copy the entire milestone .gsd subtree so deriveState reads current checkboxes
|
|
457
|
-
safeCopyRecursive(
|
|
458
|
-
join(wtGsd, "milestones", milestoneId),
|
|
459
|
-
join(prGsd, "milestones", milestoneId),
|
|
460
|
-
{ force: true },
|
|
461
|
-
);
|
|
462
|
-
|
|
463
|
-
// 3. metrics.json — session cost/token tracking (#2313).
|
|
457
|
+
// metrics.json — session cost/token tracking (#2313).
|
|
464
458
|
// Without this, metrics accumulated in the worktree are invisible from the
|
|
465
459
|
// project root and never appear in the dashboard or skill-health reports.
|
|
466
460
|
safeCopy(join(wtGsd, "metrics.json"), join(prGsd, "metrics.json"), { force: true });
|
|
467
461
|
|
|
468
|
-
//
|
|
462
|
+
// completed-units.json — runtime completion diagnostics used to avoid
|
|
463
|
+
// re-dispatching work already completed in an isolated worktree.
|
|
464
|
+
safeCopy(join(wtGsd, "completed-units.json"), join(prGsd, "completed-units.json"), { force: true });
|
|
465
|
+
|
|
466
|
+
// Runtime records — unit dispatch diagnostics used by selfHealRuntimeRecords().
|
|
469
467
|
// Without this, a crash during a unit leaves the runtime record only in the
|
|
470
468
|
// worktree. If the next session resolves basePath before worktree re-entry,
|
|
471
469
|
// selfHeal can't find or clear the stale record (#769).
|
|
@@ -638,15 +636,17 @@ export function cleanStaleRuntimeUnits(
|
|
|
638
636
|
* missing milestones, CONTEXT, ROADMAP, DECISIONS, REQUIREMENTS, and
|
|
639
637
|
* PROJECT files from the main repo's .gsd/ into the worktree's .gsd/.
|
|
640
638
|
*
|
|
641
|
-
* Only adds missing content — never overwrites existing files in the worktree
|
|
642
|
-
*
|
|
639
|
+
* Only adds missing content — never overwrites existing files in the worktree.
|
|
640
|
+
* Worktree files are compatibility projections; DB/project root remains
|
|
641
|
+
* authoritative for runtime state.
|
|
643
642
|
*/
|
|
644
643
|
export function syncGsdStateToWorktree(
|
|
645
644
|
mainBasePath: string,
|
|
646
645
|
worktreePath_: string,
|
|
647
646
|
): { synced: string[] } {
|
|
648
|
-
const
|
|
649
|
-
const
|
|
647
|
+
const contract = resolveGsdPathContract(worktreePath_, mainBasePath);
|
|
648
|
+
const mainGsd = contract.projectGsd;
|
|
649
|
+
const wtGsd = contract.worktreeGsd ?? join(worktreePath_, ".gsd");
|
|
650
650
|
const synced: string[] = [];
|
|
651
651
|
|
|
652
652
|
// If both resolve to the same directory (symlink), no sync needed
|
|
@@ -790,32 +790,26 @@ export function syncGsdStateToWorktree(
|
|
|
790
790
|
}
|
|
791
791
|
|
|
792
792
|
/**
|
|
793
|
-
* Sync
|
|
794
|
-
*
|
|
795
|
-
*
|
|
793
|
+
* Sync compatibility artifacts from worktree back to the main external state
|
|
794
|
+
* directory. Canonical workflow state lives in the project DB; worktree .gsd
|
|
795
|
+
* content is legacy projection/diagnostic data only.
|
|
796
796
|
*
|
|
797
797
|
* Syncs:
|
|
798
|
-
* 1.
|
|
799
|
-
*
|
|
800
|
-
* worktree is the authoritative execution context.
|
|
801
|
-
* 2. ALL milestone directories found in the worktree — not just the
|
|
802
|
-
* current milestoneId. The complete-milestone unit may create artifacts
|
|
803
|
-
* for the *next* milestone (CONTEXT, ROADMAP, new requirements) which
|
|
804
|
-
* must survive worktree teardown.
|
|
798
|
+
* 1. Legacy worktree DBs are reconciled into the canonical project DB.
|
|
799
|
+
* 2. Runtime diagnostic files may be copied for operator visibility.
|
|
805
800
|
*
|
|
806
|
-
*
|
|
807
|
-
*
|
|
808
|
-
*
|
|
809
|
-
* squash merge carries nothing. This caused next-milestone artifacts and
|
|
810
|
-
* updated REQUIREMENTS/PROJECT to be silently lost on teardown.
|
|
801
|
+
* Markdown milestone directories are projections and are not copied from
|
|
802
|
+
* worktrees into the project root. Current workflow state must arrive through
|
|
803
|
+
* the shared project DB or the pre-upgrade DB reconciliation path above.
|
|
811
804
|
*/
|
|
812
805
|
export function syncWorktreeStateBack(
|
|
813
806
|
mainBasePath: string,
|
|
814
807
|
worktreePath: string,
|
|
815
808
|
milestoneId: string,
|
|
816
809
|
): { synced: string[] } {
|
|
817
|
-
const
|
|
818
|
-
const
|
|
810
|
+
const contract = resolveGsdPathContract(worktreePath, mainBasePath);
|
|
811
|
+
const mainGsd = contract.projectGsd;
|
|
812
|
+
const wtGsd = contract.worktreeGsd ?? join(worktreePath, ".gsd");
|
|
819
813
|
const synced: string[] = [];
|
|
820
814
|
|
|
821
815
|
// If both resolve to the same directory (symlink), no sync needed
|
|
@@ -829,7 +823,7 @@ export function syncWorktreeStateBack(
|
|
|
829
823
|
// files. This handles in-flight worktrees that were created before the
|
|
830
824
|
// upgrade to shared WAL mode.
|
|
831
825
|
const wtLocalDb = join(wtGsd, "gsd.db");
|
|
832
|
-
const mainDb =
|
|
826
|
+
const mainDb = contract.projectDb;
|
|
833
827
|
if (existsSync(wtLocalDb) && existsSync(mainDb)) {
|
|
834
828
|
try {
|
|
835
829
|
reconcileWorktreeDb(mainDb, wtLocalDb);
|
|
@@ -840,13 +834,10 @@ export function syncWorktreeStateBack(
|
|
|
840
834
|
}
|
|
841
835
|
}
|
|
842
836
|
|
|
843
|
-
// ── 1. Sync root-level
|
|
844
|
-
//
|
|
845
|
-
//
|
|
846
|
-
|
|
847
|
-
// written during milestone closeout and lost on teardown without explicit sync
|
|
848
|
-
// (#1787, #2313).
|
|
849
|
-
for (const f of ROOT_STATE_FILES) {
|
|
837
|
+
// ── 1. Sync root-level diagnostic files back ─────────────────────────
|
|
838
|
+
// Markdown/JSON state projections remain project-root/DB authoritative.
|
|
839
|
+
// These diagnostic files are copied for observability only.
|
|
840
|
+
for (const f of ROOT_DIAGNOSTIC_FILES) {
|
|
850
841
|
const src = join(wtGsd, f);
|
|
851
842
|
const dst = join(mainGsd, f);
|
|
852
843
|
if (existsSync(src)) {
|
|
@@ -860,121 +851,8 @@ export function syncWorktreeStateBack(
|
|
|
860
851
|
}
|
|
861
852
|
}
|
|
862
853
|
|
|
863
|
-
// ── 2. Sync ALL milestone directories ────────────────────────────────
|
|
864
|
-
// The complete-milestone unit may create next-milestone artifacts (e.g.
|
|
865
|
-
// M007 setup while closing M006). We must sync every milestone directory
|
|
866
|
-
// in the worktree, not just the current one.
|
|
867
|
-
const wtMilestonesDir = join(wtGsd, "milestones");
|
|
868
|
-
if (!existsSync(wtMilestonesDir)) return { synced };
|
|
869
|
-
|
|
870
|
-
try {
|
|
871
|
-
const wtMilestones = readdirSync(wtMilestonesDir, { withFileTypes: true })
|
|
872
|
-
.filter((d) => d.isDirectory())
|
|
873
|
-
.map((d) => d.name);
|
|
874
|
-
|
|
875
|
-
for (const mid of wtMilestones) {
|
|
876
|
-
// Skip the current milestone being merged — its files are already in the
|
|
877
|
-
// milestone branch and would conflict with the squash merge (#3641).
|
|
878
|
-
if (mid === milestoneId) continue;
|
|
879
|
-
syncMilestoneDir(wtGsd, mainGsd, mid, synced);
|
|
880
|
-
}
|
|
881
|
-
} catch (err) {
|
|
882
|
-
/* non-fatal */
|
|
883
|
-
logWarning("worktree", `milestone sync-back failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
return { synced };
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
function syncCurrentMilestoneStateAfterMerge(
|
|
890
|
-
mainBasePath: string,
|
|
891
|
-
worktreePath: string,
|
|
892
|
-
milestoneId: string,
|
|
893
|
-
): { synced: string[] } {
|
|
894
|
-
const mainGsd = gsdRoot(mainBasePath);
|
|
895
|
-
const wtGsd = gsdRoot(worktreePath);
|
|
896
|
-
const synced: string[] = [];
|
|
897
|
-
|
|
898
|
-
if (isSamePath(mainGsd, wtGsd)) return { synced };
|
|
899
|
-
if (!existsSync(wtGsd) || !existsSync(mainGsd)) return { synced };
|
|
900
|
-
|
|
901
|
-
syncMilestoneDir(wtGsd, mainGsd, milestoneId, synced);
|
|
902
854
|
return { synced };
|
|
903
855
|
}
|
|
904
|
-
|
|
905
|
-
/**
|
|
906
|
-
* Sync a single milestone directory from worktree to main.
|
|
907
|
-
* Copies milestone-level .md files, slice-level files, and task summaries.
|
|
908
|
-
*/
|
|
909
|
-
/** Copy matching files from srcDir to dstDir (non-fatal per file). */
|
|
910
|
-
function syncDirFiles(
|
|
911
|
-
srcDir: string,
|
|
912
|
-
dstDir: string,
|
|
913
|
-
filter: (name: string) => boolean,
|
|
914
|
-
synced: string[],
|
|
915
|
-
prefix: string,
|
|
916
|
-
): void {
|
|
917
|
-
try {
|
|
918
|
-
for (const entry of readdirSync(srcDir, { withFileTypes: true })) {
|
|
919
|
-
if (!entry.isFile() || !filter(entry.name)) continue;
|
|
920
|
-
try {
|
|
921
|
-
cpSync(join(srcDir, entry.name), join(dstDir, entry.name), { force: true });
|
|
922
|
-
synced.push(`${prefix}${entry.name}`);
|
|
923
|
-
} catch (err) {
|
|
924
|
-
/* non-fatal */
|
|
925
|
-
logWarning("worktree", `file copy failed (${prefix}${entry.name}): ${err instanceof Error ? err.message : String(err)}`);
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
} catch (err) {
|
|
929
|
-
/* non-fatal — srcDir may not be readable */
|
|
930
|
-
logWarning("worktree", `directory read failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
function syncMilestoneDir(
|
|
935
|
-
wtGsd: string,
|
|
936
|
-
mainGsd: string,
|
|
937
|
-
mid: string,
|
|
938
|
-
synced: string[],
|
|
939
|
-
): void {
|
|
940
|
-
const wtMilestoneDir = join(wtGsd, "milestones", mid);
|
|
941
|
-
const mainMilestoneDir = join(mainGsd, "milestones", mid);
|
|
942
|
-
|
|
943
|
-
if (!existsSync(wtMilestoneDir)) return;
|
|
944
|
-
mkdirSync(mainMilestoneDir, { recursive: true });
|
|
945
|
-
|
|
946
|
-
const isMd = (name: string): boolean => name.endsWith(".md");
|
|
947
|
-
|
|
948
|
-
// Sync milestone-level files (SUMMARY, VALIDATION, ROADMAP, CONTEXT)
|
|
949
|
-
syncDirFiles(wtMilestoneDir, mainMilestoneDir, isMd, synced, `milestones/${mid}/`);
|
|
950
|
-
|
|
951
|
-
// Sync slice-level files (summaries, UATs) and task summaries (#1678)
|
|
952
|
-
const wtSlicesDir = join(wtMilestoneDir, "slices");
|
|
953
|
-
const mainSlicesDir = join(mainMilestoneDir, "slices");
|
|
954
|
-
if (!existsSync(wtSlicesDir)) return;
|
|
955
|
-
|
|
956
|
-
try {
|
|
957
|
-
for (const sliceEntry of readdirSync(wtSlicesDir, { withFileTypes: true })) {
|
|
958
|
-
if (!sliceEntry.isDirectory()) continue;
|
|
959
|
-
const sid = sliceEntry.name;
|
|
960
|
-
const wtSliceDir = join(wtSlicesDir, sid);
|
|
961
|
-
const mainSliceDir = join(mainSlicesDir, sid);
|
|
962
|
-
mkdirSync(mainSliceDir, { recursive: true });
|
|
963
|
-
|
|
964
|
-
syncDirFiles(wtSliceDir, mainSliceDir, isMd, synced, `milestones/${mid}/slices/${sid}/`);
|
|
965
|
-
|
|
966
|
-
const wtTasksDir = join(wtSliceDir, "tasks");
|
|
967
|
-
const mainTasksDir = join(mainSliceDir, "tasks");
|
|
968
|
-
if (existsSync(wtTasksDir)) {
|
|
969
|
-
mkdirSync(mainTasksDir, { recursive: true });
|
|
970
|
-
syncDirFiles(wtTasksDir, mainTasksDir, isMd, synced, `milestones/${mid}/slices/${sid}/tasks/`);
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
} catch (err) {
|
|
974
|
-
/* non-fatal */
|
|
975
|
-
logWarning("worktree", `milestone slice sync failed (${mid}): ${err instanceof Error ? err.message : String(err)}`);
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
856
|
// ─── Worktree Post-Create Hook (#597) ────────────────────────────────────────
|
|
979
857
|
|
|
980
858
|
/**
|
|
@@ -1160,10 +1038,9 @@ export function enterBranchModeForMilestone(
|
|
|
1160
1038
|
* directory at the project root and apply any [x] checkbox states that are
|
|
1161
1039
|
* ahead of the worktree version (forward-only: never downgrade [x] → [ ]).
|
|
1162
1040
|
*
|
|
1163
|
-
* This is
|
|
1164
|
-
*
|
|
1165
|
-
*
|
|
1166
|
-
* filesystem copy is still valid and correct.
|
|
1041
|
+
* This is forward-only compatibility for legacy projection copies. The DB
|
|
1042
|
+
* remains authoritative; this never downgrades checked boxes in a local
|
|
1043
|
+
* worktree projection.
|
|
1167
1044
|
*/
|
|
1168
1045
|
function reconcilePlanCheckboxes(
|
|
1169
1046
|
projectRoot: string,
|
|
@@ -1710,9 +1587,10 @@ export function mergeMilestoneToMain(
|
|
|
1710
1587
|
// database (#2823).
|
|
1711
1588
|
if (isDbAvailable()) {
|
|
1712
1589
|
try {
|
|
1713
|
-
const
|
|
1714
|
-
const
|
|
1715
|
-
|
|
1590
|
+
const contract = resolveGsdPathContract(worktreeCwd, originalBasePath_);
|
|
1591
|
+
const worktreeDbPath = join(contract.worktreeGsd ?? join(worktreeCwd, ".gsd"), "gsd.db");
|
|
1592
|
+
const mainDbPath = contract.projectDb;
|
|
1593
|
+
if (existsSync(worktreeDbPath) && !isSamePath(worktreeDbPath, mainDbPath)) {
|
|
1716
1594
|
reconcileWorktreeDb(mainDbPath, worktreeDbPath);
|
|
1717
1595
|
}
|
|
1718
1596
|
} catch (err) {
|
|
@@ -2248,27 +2126,6 @@ export function mergeMilestoneToMain(
|
|
|
2248
2126
|
// 9a-iii. Restore sheltered queued milestone directories (#2505).
|
|
2249
2127
|
restoreShelter();
|
|
2250
2128
|
|
|
2251
|
-
// 9a-iv. Preserve current milestone artifacts that may be untracked in git.
|
|
2252
|
-
// syncWorktreeStateBack intentionally skips the current milestone before the
|
|
2253
|
-
// squash merge to avoid conflicting with the merge content. Once the squash
|
|
2254
|
-
// commit is complete, copy those files back so summaries, validation, and
|
|
2255
|
-
// task outputs survive worktree teardown in external/.gitignored .gsd setups.
|
|
2256
|
-
try {
|
|
2257
|
-
const { synced } = syncCurrentMilestoneStateAfterMerge(
|
|
2258
|
-
originalBasePath_,
|
|
2259
|
-
worktreeCwd,
|
|
2260
|
-
milestoneId,
|
|
2261
|
-
);
|
|
2262
|
-
if (synced.length > 0) {
|
|
2263
|
-
debugLog("mergeMilestoneToMain", {
|
|
2264
|
-
phase: "current-milestone-sync-after-merge",
|
|
2265
|
-
synced: synced.length,
|
|
2266
|
-
});
|
|
2267
|
-
}
|
|
2268
|
-
} catch (err) {
|
|
2269
|
-
logWarning("worktree", `current milestone sync after merge failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2270
|
-
}
|
|
2271
|
-
|
|
2272
2129
|
// 9b. Safety check (#1792): if nothing was committed, verify the milestone
|
|
2273
2130
|
// work is already on the integration branch before allowing teardown.
|
|
2274
2131
|
// Compare only non-.gsd/ paths — .gsd/ state files diverge normally and
|
|
@@ -175,6 +175,7 @@ import { getErrorMessage } from "./error-utils.js";
|
|
|
175
175
|
import { recoverFailedMigration } from "./migrate-external.js";
|
|
176
176
|
import { initRegistry, convertDispatchRules } from "./rule-registry.js";
|
|
177
177
|
import { emitJournalEvent as _emitJournalEvent, type JournalEntry } from "./journal.js";
|
|
178
|
+
import { isClosedStatus } from "./status-guards.js";
|
|
178
179
|
import {
|
|
179
180
|
type AutoDashboardData,
|
|
180
181
|
updateProgressWidget as _updateProgressWidget,
|
|
@@ -194,6 +195,7 @@ import {
|
|
|
194
195
|
import { isDbAvailable, getMilestone } from "./gsd-db.js";
|
|
195
196
|
import { countPendingCaptures } from "./captures.js";
|
|
196
197
|
import { CMUX_CHANNELS, type CmuxLogLevel } from "../shared/cmux-events.js";
|
|
198
|
+
import { ensureDbOpen } from "./bootstrap/dynamic-tools.js";
|
|
197
199
|
|
|
198
200
|
function makeCmuxEmitters(pi: ExtensionAPI) {
|
|
199
201
|
return {
|
|
@@ -1506,14 +1508,29 @@ export async function startAuto(
|
|
|
1506
1508
|
);
|
|
1507
1509
|
if (shouldResumePausedSession) {
|
|
1508
1510
|
// Validate the milestone still exists and isn't already complete (#1664).
|
|
1511
|
+
// DB status is authoritative when available; SUMMARY.md is a legacy
|
|
1512
|
+
// fallback only for unmigrated/offline projects.
|
|
1509
1513
|
const mDir = resolveMilestonePath(base, meta.milestoneId);
|
|
1510
|
-
const summaryFile = resolveMilestoneFile(base, meta.milestoneId, "SUMMARY");
|
|
1511
1514
|
let summaryIsTerminal = false;
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1515
|
+
let dbAvailable = isDbAvailable();
|
|
1516
|
+
let milestoneRow = dbAvailable ? getMilestone(meta.milestoneId) : null;
|
|
1517
|
+
if (!milestoneRow) {
|
|
1518
|
+
const opened = await ensureDbOpen(base);
|
|
1519
|
+
dbAvailable = opened || isDbAvailable();
|
|
1520
|
+
if (dbAvailable) {
|
|
1521
|
+
milestoneRow = getMilestone(meta.milestoneId);
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
if (dbAvailable) {
|
|
1525
|
+
summaryIsTerminal = !!milestoneRow && isClosedStatus(milestoneRow.status);
|
|
1526
|
+
} else {
|
|
1527
|
+
const summaryFile = resolveMilestoneFile(base, meta.milestoneId, "SUMMARY");
|
|
1528
|
+
if (summaryFile) {
|
|
1529
|
+
try {
|
|
1530
|
+
summaryIsTerminal = classifyMilestoneSummaryContent(readFileSync(summaryFile, "utf-8")) !== "failure";
|
|
1531
|
+
} catch {
|
|
1532
|
+
summaryIsTerminal = false;
|
|
1533
|
+
}
|
|
1517
1534
|
}
|
|
1518
1535
|
}
|
|
1519
1536
|
if (!mDir || summaryIsTerminal) {
|