gsd-pi 2.78.1-dev.b0759e59b → 2.78.1-dev.b6a389b66
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 +25 -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 +11 -11
- 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 +11 -11
- 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 +27 -7
- 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 +110 -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 → HahrZrc_Xn4wumj0O1Ydp}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{rk1EN3FQTE6Z1yalkW_GE → HahrZrc_Xn4wumj0O1Ydp}/_ssgManifest.js +0 -0
|
@@ -149,7 +149,7 @@ test("rogue detection: DB not available → returns empty array (graceful degrad
|
|
|
149
149
|
}
|
|
150
150
|
});
|
|
151
151
|
|
|
152
|
-
test("rogue detection: slice summary on disk, no DB
|
|
152
|
+
test("rogue detection: slice summary on disk, no DB completion → detected as rogue without DB import", () => {
|
|
153
153
|
const basePath = createTmpBase();
|
|
154
154
|
const dbPath = join(basePath, ".gsd", "gsd.db");
|
|
155
155
|
mkdirSync(join(basePath, ".gsd"), { recursive: true });
|
|
@@ -160,10 +160,9 @@ test("rogue detection: slice summary on disk, no DB row → auto-remediated (not
|
|
|
160
160
|
const summaryPath = createSliceSummaryOnDisk(basePath, "M001", "S01");
|
|
161
161
|
assert.ok(existsSync(summaryPath), "Slice summary file should exist on disk");
|
|
162
162
|
|
|
163
|
-
// Fix #3633: stale slice DB status is auto-remediated via updateSliceStatus()
|
|
164
|
-
// instead of being reported as rogue, so rogues array should be empty.
|
|
165
163
|
const rogues = detectRogueFileWrites("complete-slice", "M001/S01", basePath);
|
|
166
|
-
assert.equal(rogues.length,
|
|
164
|
+
assert.equal(rogues.length, 1, "Should report stale disk summary instead of mutating DB");
|
|
165
|
+
assert.equal(rogues[0]?.path, summaryPath);
|
|
167
166
|
} finally {
|
|
168
167
|
closeDatabase();
|
|
169
168
|
rmSync(basePath, { recursive: true, force: true });
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* slice-disk-reconcile.test.ts — #2533
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* blocks. deriveStateFromDb must reconcile disk slices into the DB, just as
|
|
7
|
-
* it already does for milestones (#2416).
|
|
4
|
+
* DB-authoritative state: slices that exist only in ROADMAP.md are projections
|
|
5
|
+
* and must not be imported into the SQLite database by deriveStateFromDb().
|
|
8
6
|
*
|
|
9
7
|
* Scenario: M001 has a ROADMAP with S01-S04. S01 and S02 have SUMMARY files
|
|
10
8
|
* (complete on disk). S03 depends on S01. Only S04 is in the DB (depends on
|
|
@@ -70,7 +68,7 @@ const ROADMAP_CONTENT = `# M001: Test Milestone
|
|
|
70
68
|
`;
|
|
71
69
|
|
|
72
70
|
async function testMissingSlicesCauseBlock(): Promise<void> {
|
|
73
|
-
console.log("\n--- Test: missing DB slices
|
|
71
|
+
console.log("\n--- Test: missing DB slices are not imported from ROADMAP.md ---");
|
|
74
72
|
|
|
75
73
|
const base = createFixtureBase();
|
|
76
74
|
const dbPath = join(base, ".gsd", "gsd.db");
|
|
@@ -96,51 +94,11 @@ async function testMissingSlicesCauseBlock(): Promise<void> {
|
|
|
96
94
|
invalidateStateCache();
|
|
97
95
|
const state = await deriveStateFromDb(base);
|
|
98
96
|
|
|
99
|
-
// After the fix, slices S01-S03 should be reconciled into DB
|
|
100
97
|
const dbSlices = getMilestoneSlices("M001");
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
// S01 and S02 should be marked complete (have SUMMARY files)
|
|
107
|
-
const s01 = dbSlices.find(s => s.id === "S01");
|
|
108
|
-
assertTrue(s01 !== undefined, "S01 should exist in DB after reconciliation");
|
|
109
|
-
if (s01) {
|
|
110
|
-
assertEq(s01.status, "complete", "S01 should be 'complete' (has SUMMARY on disk)");
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const s02 = dbSlices.find(s => s.id === "S02");
|
|
114
|
-
assertTrue(s02 !== undefined, "S02 should exist in DB after reconciliation");
|
|
115
|
-
if (s02) {
|
|
116
|
-
assertEq(s02.status, "complete", "S02 should be 'complete' (has SUMMARY on disk)");
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// S03 should be pending (no SUMMARY)
|
|
120
|
-
const s03 = dbSlices.find(s => s.id === "S03");
|
|
121
|
-
assertTrue(s03 !== undefined, "S03 should exist in DB after reconciliation");
|
|
122
|
-
if (s03) {
|
|
123
|
-
assertEq(s03.status, "pending", "S03 should be 'pending' (no SUMMARY on disk)");
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// The state should NOT be blocked — S03 should be eligible (S01 dep satisfied)
|
|
127
|
-
assertTrue(
|
|
128
|
-
state.phase !== "blocked",
|
|
129
|
-
`Phase should not be 'blocked' after reconciliation, got '${state.phase}'`,
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
// Active slice should be S03 (S01 dep met, S03 is first incomplete with satisfied deps)
|
|
133
|
-
assertTrue(
|
|
134
|
-
state.activeSlice !== null,
|
|
135
|
-
"There should be an active slice after reconciliation",
|
|
136
|
-
);
|
|
137
|
-
if (state.activeSlice) {
|
|
138
|
-
assertEq(
|
|
139
|
-
state.activeSlice.id,
|
|
140
|
-
"S03",
|
|
141
|
-
"Active slice should be S03 (its dependency S01 is complete) (#2533)",
|
|
142
|
-
);
|
|
143
|
-
}
|
|
98
|
+
assertEq(dbSlices.length, 1, `Only DB slice S04 should remain, got ${dbSlices.length}`);
|
|
99
|
+
assertEq(dbSlices[0]?.id, "S04", "Disk-only slices are not inserted");
|
|
100
|
+
assertEq(state.phase, "blocked", "DB-only S04 remains blocked on missing DB dependency S03");
|
|
101
|
+
assertEq(state.activeSlice, null, "No active slice is inferred from roadmap-only rows");
|
|
144
102
|
} finally {
|
|
145
103
|
closeDatabase();
|
|
146
104
|
cleanup(base);
|
|
@@ -148,7 +106,7 @@ async function testMissingSlicesCauseBlock(): Promise<void> {
|
|
|
148
106
|
}
|
|
149
107
|
|
|
150
108
|
async function testSliceReconciliationIdempotent(): Promise<void> {
|
|
151
|
-
console.log("\n--- Test:
|
|
109
|
+
console.log("\n--- Test: disk-only slices remain absent on repeated derives ---");
|
|
152
110
|
|
|
153
111
|
const base = createFixtureBase();
|
|
154
112
|
const dbPath = join(base, ".gsd", "gsd.db");
|
|
@@ -178,11 +136,7 @@ async function testSliceReconciliationIdempotent(): Promise<void> {
|
|
|
178
136
|
assertEq(s01.status, "complete", "S01 status should remain 'complete' (not overwritten)");
|
|
179
137
|
}
|
|
180
138
|
|
|
181
|
-
|
|
182
|
-
assertTrue(
|
|
183
|
-
dbSlices.length === 4,
|
|
184
|
-
`Should have 4 slices after reconciliation (existing + new), got ${dbSlices.length}`,
|
|
185
|
-
);
|
|
139
|
+
assertEq(dbSlices.length, 1, `Only existing DB slice should remain, got ${dbSlices.length}`);
|
|
186
140
|
} finally {
|
|
187
141
|
closeDatabase();
|
|
188
142
|
cleanup(base);
|
|
@@ -218,7 +172,7 @@ async function testNoRoadmapSkipsReconciliation(): Promise<void> {
|
|
|
218
172
|
}
|
|
219
173
|
|
|
220
174
|
async function main(): Promise<void> {
|
|
221
|
-
console.log("\n===
|
|
175
|
+
console.log("\n=== deriveStateFromDb does not reconcile disk slices ===");
|
|
222
176
|
|
|
223
177
|
await testMissingSlicesCauseBlock();
|
|
224
178
|
await testSliceReconciliationIdempotent();
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* stale-slice-rows.test.ts
|
|
2
|
+
* stale-slice-rows.test.ts
|
|
3
3
|
*
|
|
4
|
-
* Verify that state.ts
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* doneSliceIds from stale DB rows and downstream slices stay blocked.
|
|
4
|
+
* Verify that state.ts no longer treats slice SUMMARY.md projections as
|
|
5
|
+
* authority for DB slice status. Slice rows must be updated through DB-backed
|
|
6
|
+
* completion/import APIs.
|
|
8
7
|
*/
|
|
9
8
|
|
|
10
9
|
import { describe, test } from "node:test";
|
|
@@ -16,26 +15,26 @@ import { fileURLToPath } from "node:url";
|
|
|
16
15
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
16
|
const sourceFile = join(__dirname, "..", "state.ts");
|
|
18
17
|
|
|
19
|
-
describe("stale slice row
|
|
18
|
+
describe("stale slice row DB-authoritative boundary", () => {
|
|
20
19
|
const source = readFileSync(sourceFile, "utf-8");
|
|
21
20
|
|
|
22
|
-
test("
|
|
23
|
-
assert.
|
|
21
|
+
test("does not import updateSliceStatus into state derivation", () => {
|
|
22
|
+
assert.doesNotMatch(source, /import\s*\{[^}]*updateSliceStatus[^}]*\}\s*from/);
|
|
24
23
|
});
|
|
25
24
|
|
|
26
|
-
test("
|
|
27
|
-
assert.
|
|
25
|
+
test("does not scan DB slice rows for disk SUMMARY reconciliation", () => {
|
|
26
|
+
assert.doesNotMatch(source, /dbSlice/);
|
|
28
27
|
});
|
|
29
28
|
|
|
30
|
-
test("
|
|
31
|
-
assert.
|
|
29
|
+
test("does not resolve slice SUMMARY to mutate DB state", () => {
|
|
30
|
+
assert.doesNotMatch(source, /resolveSliceFile\(basePath,\s*mid,\s*dbSlice\.id,\s*["']SUMMARY["']\)/);
|
|
32
31
|
});
|
|
33
32
|
|
|
34
|
-
test("
|
|
35
|
-
assert.
|
|
33
|
+
test("does not call updateSliceStatus from state derivation", () => {
|
|
34
|
+
assert.doesNotMatch(source, /updateSliceStatus\(/);
|
|
36
35
|
});
|
|
37
36
|
|
|
38
|
-
test("
|
|
39
|
-
assert.match(source,
|
|
37
|
+
test("documents markdown projections as non-authoritative", () => {
|
|
38
|
+
assert.match(source, /Markdown files are projections only/);
|
|
40
39
|
});
|
|
41
40
|
});
|
|
@@ -858,11 +858,11 @@ describe("state-machine-full-walkthrough", () => {
|
|
|
858
858
|
});
|
|
859
859
|
|
|
860
860
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
861
|
-
//
|
|
861
|
+
// DB-AUTHORITATIVE DERIVATION
|
|
862
862
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
863
863
|
|
|
864
|
-
describe("
|
|
865
|
-
test("DB: task with SUMMARY on disk but DB says pending →
|
|
864
|
+
describe("DB-authoritative derivation", () => {
|
|
865
|
+
test("DB: task with SUMMARY on disk but DB says pending → DB remains authoritative", async () => {
|
|
866
866
|
const base = createFixtureBase();
|
|
867
867
|
const dbPath = join(base, ".gsd", "gsd.db");
|
|
868
868
|
openDatabase(dbPath);
|
|
@@ -875,19 +875,19 @@ describe("state-machine-full-walkthrough", () => {
|
|
|
875
875
|
writeRoadmap(base, "M001", standardRoadmap());
|
|
876
876
|
writePlan(base, "M001", "S01", standardPlan());
|
|
877
877
|
|
|
878
|
-
// Write SUMMARY files on disk for both tasks
|
|
878
|
+
// Write SUMMARY files on disk for both tasks. These are projections and
|
|
879
|
+
// must not complete pending DB tasks during runtime derivation.
|
|
879
880
|
writeTaskSummary(base, "M001", "S01", "T01");
|
|
880
881
|
writeTaskSummary(base, "M001", "S01", "T02");
|
|
881
882
|
|
|
882
883
|
invalidateStateCache();
|
|
883
884
|
const state = await deriveStateFromDb(base);
|
|
884
885
|
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
assert.equal(state.phase, "summarizing", "reconciliation should advance past pending tasks");
|
|
886
|
+
assert.equal(state.phase, "executing", "disk SUMMARY projections must not complete DB tasks");
|
|
887
|
+
assert.equal(state.activeTask?.id, "T01", "first pending DB task remains active");
|
|
888
888
|
});
|
|
889
889
|
|
|
890
|
-
test("empty DB with disk milestones → disk-to-DB sync
|
|
890
|
+
test("empty DB with disk milestones → no runtime disk-to-DB sync", async () => {
|
|
891
891
|
const base = createFixtureBase();
|
|
892
892
|
writeContext(base, "M001", "# M001: Test\n\nContext.");
|
|
893
893
|
|
|
@@ -899,11 +899,10 @@ describe("state-machine-full-walkthrough", () => {
|
|
|
899
899
|
invalidateStateCache();
|
|
900
900
|
const state = await deriveState(base);
|
|
901
901
|
|
|
902
|
-
//
|
|
902
|
+
// Runtime derivation must not import disk milestones into the DB.
|
|
903
903
|
const after = getAllMilestones();
|
|
904
|
-
assert.
|
|
905
|
-
assert.equal(
|
|
906
|
-
assert.ok(state.activeMilestone !== null);
|
|
904
|
+
assert.equal(after.length, 0, "DB should remain empty without explicit migration");
|
|
905
|
+
assert.equal(state.activeMilestone, null, "disk milestone is ignored while DB is authoritative");
|
|
907
906
|
});
|
|
908
907
|
|
|
909
908
|
test("ghost milestone (empty dir) → NOT in registry", async () => {
|
|
@@ -1063,7 +1062,7 @@ describe("state-machine-full-walkthrough", () => {
|
|
|
1063
1062
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1064
1063
|
|
|
1065
1064
|
describe("Recovery: DB has slice but no task rows (partial migration)", () => {
|
|
1066
|
-
test("DB tasks empty but PLAN on disk has tasks →
|
|
1065
|
+
test("DB tasks empty but PLAN on disk has tasks → stays planning", async () => {
|
|
1067
1066
|
const base = createFixtureBase();
|
|
1068
1067
|
const dbPath = join(base, ".gsd", "gsd.db");
|
|
1069
1068
|
openDatabase(dbPath);
|
|
@@ -1078,15 +1077,13 @@ describe("state-machine-full-walkthrough", () => {
|
|
|
1078
1077
|
invalidateStateCache();
|
|
1079
1078
|
const state = await deriveStateFromDb(base);
|
|
1080
1079
|
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
assert.equal(state.phase, "executing",
|
|
1084
|
-
"reconciled plan-file tasks → executing (not stuck in planning)");
|
|
1080
|
+
assert.equal(state.phase, "planning",
|
|
1081
|
+
"PLAN.md projection must not import DB tasks during runtime derivation");
|
|
1085
1082
|
});
|
|
1086
1083
|
});
|
|
1087
1084
|
|
|
1088
1085
|
describe("Failure: partial SUMMARY reconciliation", () => {
|
|
1089
|
-
test("only one task has SUMMARY, other still pending → executing
|
|
1086
|
+
test("only one task has SUMMARY, other still pending → executing first DB-pending task", async () => {
|
|
1090
1087
|
const base = createFixtureBase();
|
|
1091
1088
|
const dbPath = join(base, ".gsd", "gsd.db");
|
|
1092
1089
|
openDatabase(dbPath);
|
|
@@ -1104,9 +1101,8 @@ describe("state-machine-full-walkthrough", () => {
|
|
|
1104
1101
|
invalidateStateCache();
|
|
1105
1102
|
const state = await deriveStateFromDb(base);
|
|
1106
1103
|
|
|
1107
|
-
// T01 reconciled to complete, T02 still pending → executing T02
|
|
1108
1104
|
assert.equal(state.phase, "executing");
|
|
1109
|
-
assert.equal(state.activeTask?.id, "
|
|
1105
|
+
assert.equal(state.activeTask?.id, "T01", "disk SUMMARY must not advance past pending DB task");
|
|
1110
1106
|
});
|
|
1111
1107
|
});
|
|
1112
1108
|
|
|
@@ -1255,7 +1251,7 @@ describe("state-machine-full-walkthrough", () => {
|
|
|
1255
1251
|
});
|
|
1256
1252
|
|
|
1257
1253
|
describe("Failure: missing task plan files in DB path", () => {
|
|
1258
|
-
test("DB has tasks but no T##-PLAN.md files →
|
|
1254
|
+
test("DB has tasks but no T##-PLAN.md files → executing phase", async () => {
|
|
1259
1255
|
const base = createFixtureBase();
|
|
1260
1256
|
const dbPath = join(base, ".gsd", "gsd.db");
|
|
1261
1257
|
openDatabase(dbPath);
|
|
@@ -1273,8 +1269,8 @@ describe("state-machine-full-walkthrough", () => {
|
|
|
1273
1269
|
invalidateStateCache();
|
|
1274
1270
|
const state = await deriveStateFromDb(base);
|
|
1275
1271
|
|
|
1276
|
-
assert.equal(state.phase, "
|
|
1277
|
-
"
|
|
1272
|
+
assert.equal(state.phase, "executing",
|
|
1273
|
+
"DB tasks are authoritative even when task plan projections are missing");
|
|
1278
1274
|
});
|
|
1279
1275
|
});
|
|
1280
1276
|
|
|
@@ -1591,7 +1587,7 @@ describe("state-machine-full-walkthrough", () => {
|
|
|
1591
1587
|
});
|
|
1592
1588
|
|
|
1593
1589
|
describe("Failure: multiple reconciliation in single derivation", () => {
|
|
1594
|
-
test("DB has 3 stale tasks, all with SUMMARY on disk →
|
|
1590
|
+
test("DB has 3 stale tasks, all with SUMMARY on disk → first DB-pending task remains active", async () => {
|
|
1595
1591
|
const base = createFixtureBase();
|
|
1596
1592
|
const dbPath = join(base, ".gsd", "gsd.db");
|
|
1597
1593
|
openDatabase(dbPath);
|
|
@@ -1641,9 +1637,9 @@ describe("state-machine-full-walkthrough", () => {
|
|
|
1641
1637
|
invalidateStateCache();
|
|
1642
1638
|
const state = await deriveStateFromDb(base);
|
|
1643
1639
|
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1640
|
+
assert.equal(state.phase, "executing",
|
|
1641
|
+
"disk SUMMARY projections must not reconcile DB task state");
|
|
1642
|
+
assert.equal(state.activeTask?.id, "T01", "first non-closed DB task remains active");
|
|
1647
1643
|
});
|
|
1648
1644
|
});
|
|
1649
1645
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// GSD Extension - Steer Worktree Path Resolution Test
|
|
2
|
-
//
|
|
3
|
-
//
|
|
2
|
+
// Worktrees share the canonical project .gsd state root. /gsd steer writes
|
|
3
|
+
// overrides to that canonical root even when invoked with a worktree path.
|
|
4
4
|
|
|
5
5
|
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
6
6
|
import assert from "node:assert/strict";
|
|
@@ -27,32 +27,31 @@ describe("steer worktree path resolution (#3476)", () => {
|
|
|
27
27
|
rmSync(projectRoot, { recursive: true, force: true });
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
test("appendOverride writes to
|
|
30
|
+
test("appendOverride writes to canonical project .gsd/ when worktree path is used", async () => {
|
|
31
31
|
await appendOverride(worktreePath, "Use Postgres instead of SQLite", "M001/S01/T01");
|
|
32
32
|
|
|
33
|
-
// Override should be in the
|
|
33
|
+
// Override should be in the canonical project .gsd/
|
|
34
34
|
const wtOverrides = join(worktreePath, ".gsd", "OVERRIDES.md");
|
|
35
|
-
|
|
35
|
+
const rootOverrides = join(projectRoot, ".gsd", "OVERRIDES.md");
|
|
36
|
+
assert.ok(!existsSync(wtOverrides), "no override file in worktree-local .gsd/");
|
|
37
|
+
assert.ok(existsSync(rootOverrides), "override file exists in project root .gsd/");
|
|
36
38
|
|
|
37
|
-
const content = readFileSync(
|
|
39
|
+
const content = readFileSync(rootOverrides, "utf-8");
|
|
38
40
|
assert.ok(content.includes("Use Postgres instead of SQLite"), "override content is correct");
|
|
39
|
-
|
|
40
|
-
// Override should NOT be in the project root .gsd/
|
|
41
|
-
const rootOverrides = join(projectRoot, ".gsd", "OVERRIDES.md");
|
|
42
|
-
assert.ok(!existsSync(rootOverrides), "no override file in project root .gsd/");
|
|
43
41
|
});
|
|
44
42
|
|
|
45
|
-
test("loadActiveOverrides reads
|
|
43
|
+
test("loadActiveOverrides reads canonical project .gsd/ when worktree path is used", async () => {
|
|
46
44
|
await appendOverride(worktreePath, "Switch to JWT auth", "M001/S02/T01");
|
|
47
45
|
|
|
48
|
-
// Loading from worktree
|
|
46
|
+
// Loading from worktree resolves to the canonical project state root.
|
|
49
47
|
const wtOverrides = await loadActiveOverrides(worktreePath);
|
|
50
48
|
assert.equal(wtOverrides.length, 1, "one active override in worktree");
|
|
51
49
|
assert.equal(wtOverrides[0].change, "Switch to JWT auth");
|
|
52
50
|
|
|
53
|
-
// Loading from project root
|
|
51
|
+
// Loading from project root sees the same canonical override.
|
|
54
52
|
const rootOverrides = await loadActiveOverrides(projectRoot);
|
|
55
|
-
assert.equal(rootOverrides.length,
|
|
53
|
+
assert.equal(rootOverrides.length, 1, "same override visible from project root");
|
|
54
|
+
assert.equal(rootOverrides[0].change, "Switch to JWT auth");
|
|
56
55
|
});
|
|
57
56
|
|
|
58
57
|
test("appendOverride falls back to project root when no worktree exists", async () => {
|
|
@@ -135,7 +135,7 @@ test("stopAutoRemote sends SIGTERM to a live process and returns found:true", {
|
|
|
135
135
|
|
|
136
136
|
// ─── Lock path: original project root vs worktree ────────────────────────
|
|
137
137
|
|
|
138
|
-
test("lock file should be discoverable
|
|
138
|
+
test("lock file should be discoverable from project root and worktree path", () => {
|
|
139
139
|
const projectRoot = makeTmpBase();
|
|
140
140
|
const worktreePath = join(projectRoot, ".gsd", "worktrees", "M001");
|
|
141
141
|
mkdirSync(join(worktreePath, ".gsd"), { recursive: true });
|
|
@@ -149,9 +149,10 @@ test("lock file should be discoverable at project root, not worktree path", () =
|
|
|
149
149
|
assert.ok(lock, "lock should be found at project root");
|
|
150
150
|
assert.equal(lock!.unitType, "execute-task");
|
|
151
151
|
|
|
152
|
-
// Worktree path
|
|
152
|
+
// Worktree path resolves to the same canonical project .gsd lock.
|
|
153
153
|
const worktreeLock = readCrashLock(worktreePath);
|
|
154
|
-
assert.
|
|
154
|
+
assert.ok(worktreeLock, "lock should be discoverable via worktree path");
|
|
155
|
+
assert.equal(worktreeLock!.unitType, "execute-task");
|
|
155
156
|
} finally {
|
|
156
157
|
cleanup(projectRoot);
|
|
157
158
|
}
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Regression test for
|
|
2
|
+
* Regression test for DB-authoritative syncWorktreeStateBack behavior.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* The fix adds a `mid === milestoneId` skip guard inside the milestone
|
|
9
|
-
* iteration loop in syncWorktreeStateBack.
|
|
4
|
+
* Worktree milestone projections are not authoritative. syncWorktreeStateBack
|
|
5
|
+
* may reconcile a legacy worktree DB and copy diagnostics, but must not copy
|
|
6
|
+
* milestone markdown directories back into the project root.
|
|
10
7
|
*/
|
|
11
8
|
|
|
12
9
|
import { describe, it } from 'node:test'
|
|
@@ -20,7 +17,7 @@ const src = readFileSync(
|
|
|
20
17
|
'utf-8',
|
|
21
18
|
)
|
|
22
19
|
|
|
23
|
-
describe('syncWorktreeStateBack
|
|
20
|
+
describe('syncWorktreeStateBack does not copy worktree milestone projections', () => {
|
|
24
21
|
it('syncWorktreeStateBack function exists', () => {
|
|
25
22
|
assert.ok(
|
|
26
23
|
src.includes('function syncWorktreeStateBack('),
|
|
@@ -28,7 +25,7 @@ describe('syncWorktreeStateBack skips current milestone (#3641)', () => {
|
|
|
28
25
|
)
|
|
29
26
|
})
|
|
30
27
|
|
|
31
|
-
it('
|
|
28
|
+
it('does not iterate worktree milestones for copy-back', () => {
|
|
32
29
|
// Find syncWorktreeStateBack
|
|
33
30
|
const fnStart = src.indexOf('function syncWorktreeStateBack(')
|
|
34
31
|
assert.ok(fnStart !== -1)
|
|
@@ -36,31 +33,11 @@ describe('syncWorktreeStateBack skips current milestone (#3641)', () => {
|
|
|
36
33
|
// Get a reasonable portion of the function
|
|
37
34
|
const fnBlock = extractSourceRegion(src, 'function syncWorktreeStateBack(', { fromIdx: fnStart })
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
assert.ok(loopIdx !== -1, 'milestone iteration loop must exist')
|
|
42
|
-
|
|
43
|
-
// After the loop, there should be the skip guard
|
|
44
|
-
const loopBody = extractSourceRegion(fnBlock, 'for (const mid of wtMilestones)', { fromIdx: loopIdx })
|
|
45
|
-
assert.ok(
|
|
46
|
-
loopBody.includes('mid === milestoneId'),
|
|
47
|
-
'mid === milestoneId skip guard must exist inside the milestone loop',
|
|
48
|
-
)
|
|
49
|
-
assert.ok(
|
|
50
|
-
loopBody.includes('continue'),
|
|
51
|
-
'skip guard must use continue to skip the current milestone',
|
|
52
|
-
)
|
|
36
|
+
assert.ok(!fnBlock.includes('for (const mid of wtMilestones)'), 'must not iterate worktree milestones')
|
|
37
|
+
assert.ok(!fnBlock.includes('syncMilestoneDir('), 'must not copy milestone markdown projections')
|
|
53
38
|
})
|
|
54
39
|
|
|
55
|
-
it('
|
|
56
|
-
|
|
57
|
-
assert.ok(fnStart !== -1)
|
|
58
|
-
|
|
59
|
-
const fnBlock = extractSourceRegion(src, 'function syncWorktreeStateBack(', { fromIdx: fnStart })
|
|
60
|
-
|
|
61
|
-
assert.ok(
|
|
62
|
-
fnBlock.includes('syncMilestoneDir('),
|
|
63
|
-
'syncMilestoneDir must still be called for other milestones',
|
|
64
|
-
)
|
|
40
|
+
it('legacy milestone copy helper has been removed', () => {
|
|
41
|
+
assert.ok(!src.includes('function syncMilestoneDir('), 'syncMilestoneDir helper should not exist')
|
|
65
42
|
})
|
|
66
43
|
})
|
|
@@ -85,7 +85,7 @@ describe("handleValidateMilestone write ordering (#2725)", () => {
|
|
|
85
85
|
assert.doesNotMatch(validationMd, /## Verification Class Compliance/);
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
-
it("
|
|
88
|
+
it("keeps DB row and reports stale projection when disk write fails", async () => {
|
|
89
89
|
base = makeTmpBase();
|
|
90
90
|
const dbPath = join(base, ".gsd", "gsd.db");
|
|
91
91
|
openDatabase(dbPath);
|
|
@@ -101,16 +101,15 @@ describe("handleValidateMilestone write ordering (#2725)", () => {
|
|
|
101
101
|
|
|
102
102
|
const result = await handleValidateMilestone(VALID_PARAMS, base);
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
assert.
|
|
106
|
-
assert.ok(result.error.includes("disk render failed"));
|
|
104
|
+
assert.ok(!("error" in result), `unexpected error: ${"error" in result ? result.error : ""}`);
|
|
105
|
+
assert.equal(result.stale, true, "result should report stale projection");
|
|
107
106
|
|
|
108
|
-
// DB row should have been rolled back (deleted)
|
|
109
107
|
const adapter = _getAdapter()!;
|
|
110
108
|
const row = adapter.prepare(
|
|
111
|
-
`SELECT
|
|
112
|
-
).get();
|
|
113
|
-
assert.
|
|
109
|
+
`SELECT status FROM assessments WHERE milestone_id = 'M001' AND scope = 'milestone-validation'`,
|
|
110
|
+
).get() as { status: string } | undefined;
|
|
111
|
+
assert.ok(row, "assessment row should remain committed");
|
|
112
|
+
assert.equal(row!.status, "pass");
|
|
114
113
|
});
|
|
115
114
|
|
|
116
115
|
it("persists milestone validation gate_runs rows when UOK gates are enabled", async () => {
|
|
@@ -394,7 +394,7 @@ test("dispatch rule skips when skip_milestone_validation preference is set", asy
|
|
|
394
394
|
}
|
|
395
395
|
});
|
|
396
396
|
|
|
397
|
-
test("dispatch rule
|
|
397
|
+
test("dispatch rule ignores failure-path SUMMARY projection when DB milestone is not complete (#4658 superseded)", async () => {
|
|
398
398
|
const state: GSDState = {
|
|
399
399
|
activeMilestone: { id: "M001", title: "Test" },
|
|
400
400
|
activeSlice: null,
|
|
@@ -422,17 +422,14 @@ test("dispatch rule fails closed for failure-path SUMMARY when DB milestone is n
|
|
|
422
422
|
prefs: undefined,
|
|
423
423
|
};
|
|
424
424
|
const result = await resolveDispatch(ctx);
|
|
425
|
-
assert.equal(result.action, "
|
|
426
|
-
|
|
427
|
-
assert.equal(result.level, "warning");
|
|
428
|
-
assert.match(result.reason, /failure-path SUMMARY/i);
|
|
429
|
-
}
|
|
425
|
+
assert.equal(result.action, "dispatch");
|
|
426
|
+
assert.equal(getMilestone("M001")?.status, "active");
|
|
430
427
|
} finally {
|
|
431
428
|
cleanup(base);
|
|
432
429
|
}
|
|
433
430
|
});
|
|
434
431
|
|
|
435
|
-
test("dispatch rule
|
|
432
|
+
test("dispatch rule does not reconcile DB from successful stale SUMMARY projection (#4658 superseded)", async () => {
|
|
436
433
|
const state: GSDState = {
|
|
437
434
|
activeMilestone: { id: "M001", title: "Test" },
|
|
438
435
|
activeSlice: null,
|
|
@@ -473,15 +470,15 @@ test("dispatch rule reconciles DB for successful stale SUMMARY (#4658)", async (
|
|
|
473
470
|
prefs: undefined,
|
|
474
471
|
};
|
|
475
472
|
const result = await resolveDispatch(ctx);
|
|
476
|
-
assert.equal(result.action, "
|
|
473
|
+
assert.equal(result.action, "dispatch");
|
|
477
474
|
const milestone = getMilestone("M001");
|
|
478
|
-
assert.equal(milestone?.status, "
|
|
475
|
+
assert.equal(milestone?.status, "active");
|
|
479
476
|
} finally {
|
|
480
477
|
cleanup(base);
|
|
481
478
|
}
|
|
482
479
|
});
|
|
483
480
|
|
|
484
|
-
test("dispatch rule
|
|
481
|
+
test("dispatch rule ignores ambiguous stale SUMMARY projection (#4658 superseded)", async () => {
|
|
485
482
|
const state: GSDState = {
|
|
486
483
|
activeMilestone: { id: "M001", title: "Test" },
|
|
487
484
|
activeSlice: null,
|
|
@@ -509,11 +506,8 @@ test("dispatch rule fails closed for ambiguous stale SUMMARY (#4658)", async ()
|
|
|
509
506
|
prefs: undefined,
|
|
510
507
|
};
|
|
511
508
|
const result = await resolveDispatch(ctx);
|
|
512
|
-
assert.equal(result.action, "
|
|
513
|
-
|
|
514
|
-
assert.equal(result.level, "warning");
|
|
515
|
-
assert.match(result.reason, /ambiguous SUMMARY/i);
|
|
516
|
-
}
|
|
509
|
+
assert.equal(result.action, "dispatch");
|
|
510
|
+
assert.equal(getMilestone("M001")?.status, "active");
|
|
517
511
|
} finally {
|
|
518
512
|
cleanup(base);
|
|
519
513
|
}
|
|
@@ -205,17 +205,22 @@ test("readTransaction logs ROLLBACK failures as split-brain signal", () => {
|
|
|
205
205
|
);
|
|
206
206
|
});
|
|
207
207
|
|
|
208
|
-
// ─── Runtime:
|
|
208
|
+
// ─── Runtime: startup/projection diagnostics stay explicit ─────────────────
|
|
209
209
|
|
|
210
|
-
test("
|
|
211
|
-
const
|
|
212
|
-
join(import.meta.dirname, "..", "
|
|
210
|
+
test("auto-start initializes the DB without implicit markdown migration", () => {
|
|
211
|
+
const autoStartSrc = readFileSync(
|
|
212
|
+
join(import.meta.dirname, "..", "auto-start.ts"),
|
|
213
213
|
"utf-8",
|
|
214
214
|
);
|
|
215
|
+
assert.doesNotMatch(
|
|
216
|
+
autoStartSrc,
|
|
217
|
+
/migrateFromMarkdown|md-importer/,
|
|
218
|
+
"auto-start must not import markdown into the runtime DB implicitly",
|
|
219
|
+
);
|
|
215
220
|
assert.match(
|
|
216
|
-
|
|
217
|
-
/
|
|
218
|
-
"
|
|
221
|
+
autoStartSrc,
|
|
222
|
+
/failed to initialize project database/,
|
|
223
|
+
"DB initialization failures should still be logged",
|
|
219
224
|
);
|
|
220
225
|
});
|
|
221
226
|
|
|
@@ -15,7 +15,7 @@ test("shouldAutoPrepareWorkflowMcp enables prep for externalCli local transport"
|
|
|
15
15
|
assert.equal(result, true);
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
-
test("shouldAutoPrepareWorkflowMcp
|
|
18
|
+
test("shouldAutoPrepareWorkflowMcp stays disabled for non-Claude active provider even when claude-code is ready", () => {
|
|
19
19
|
const result = shouldAutoPrepareWorkflowMcp({
|
|
20
20
|
model: { provider: "openai", baseUrl: "https://api.openai.com" },
|
|
21
21
|
modelRegistry: {
|
|
@@ -24,10 +24,10 @@ test("shouldAutoPrepareWorkflowMcp enables prep when claude-code provider is rea
|
|
|
24
24
|
},
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
assert.equal(result,
|
|
27
|
+
assert.equal(result, false);
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
test("shouldAutoPrepareWorkflowMcp
|
|
30
|
+
test("shouldAutoPrepareWorkflowMcp stays disabled for non-Claude active provider even when claude-code is registered", () => {
|
|
31
31
|
const result = shouldAutoPrepareWorkflowMcp({
|
|
32
32
|
model: { provider: "openai", baseUrl: "https://api.openai.com" },
|
|
33
33
|
modelRegistry: {
|
|
@@ -36,7 +36,7 @@ test("shouldAutoPrepareWorkflowMcp enables prep when claude-code provider is reg
|
|
|
36
36
|
},
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
assert.equal(result,
|
|
39
|
+
assert.equal(result, false);
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
test("shouldAutoPrepareWorkflowMcp stays disabled when neither transport nor provider readiness match", () => {
|