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
|
@@ -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", () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import test from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
-
import { existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { existsSync, mkdtempSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
@@ -30,6 +30,17 @@ function readSrc(file: string): string {
|
|
|
30
30
|
return readFileSync(join(gsdDir, file), "utf-8");
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
function listTsFiles(dir: string): string[] {
|
|
34
|
+
const out: string[] = [];
|
|
35
|
+
for (const entry of readdirSync(dir)) {
|
|
36
|
+
const full = join(dir, entry);
|
|
37
|
+
const stat = statSync(full);
|
|
38
|
+
if (stat.isDirectory()) out.push(...listTsFiles(full));
|
|
39
|
+
else if (entry.endsWith(".ts")) out.push(full);
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
|
|
33
44
|
function extractElicitPayload(request: unknown): ElicitPayload {
|
|
34
45
|
const payload = (request as { params?: unknown }).params ?? request;
|
|
35
46
|
return payload as ElicitPayload;
|
|
@@ -772,3 +783,15 @@ test("workflow transport error guidance includes /gsd mcp init hint", () => {
|
|
|
772
783
|
const src = readSrc("workflow-mcp.ts");
|
|
773
784
|
assert.match(src, /Please run \/gsd mcp init \./);
|
|
774
785
|
});
|
|
786
|
+
|
|
787
|
+
test("buildWorkflowMcpServers is only imported by tests or Claude Code MCP boundary code", () => {
|
|
788
|
+
const offenders: string[] = [];
|
|
789
|
+
for (const file of listTsFiles(gsdDir)) {
|
|
790
|
+
const rel = file.slice(gsdDir.length + 1).replace(/\\/g, "/");
|
|
791
|
+
if (rel === "workflow-mcp.ts" || rel.startsWith("tests/")) continue;
|
|
792
|
+
const src = readFileSync(file, "utf-8");
|
|
793
|
+
if (/\bimport\b[\s\S]*\bbuildWorkflowMcpServers\b/.test(src)) offenders.push(rel);
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
assert.deepEqual(offenders, []);
|
|
797
|
+
});
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
existsSync,
|
|
16
16
|
mkdirSync,
|
|
17
17
|
mkdtempSync,
|
|
18
|
+
readFileSync,
|
|
18
19
|
rmSync,
|
|
19
20
|
symlinkSync,
|
|
20
21
|
writeFileSync,
|
|
@@ -157,6 +158,18 @@ describe("#2823: reconcileWorktreeDb same-file guard", () => {
|
|
|
157
158
|
});
|
|
158
159
|
});
|
|
159
160
|
|
|
161
|
+
test("merge-time DB reconciliation only runs when legacy worktree DB exists", () => {
|
|
162
|
+
const src = readFileSync(join(import.meta.dirname, "..", "auto-worktree.ts"), "utf-8");
|
|
163
|
+
const reconcileIdx = src.indexOf("reconcileWorktreeDb(mainDbPath, worktreeDbPath)");
|
|
164
|
+
assert.ok(reconcileIdx !== -1, "merge-time reconcile call exists");
|
|
165
|
+
const guardWindow = src.slice(Math.max(0, reconcileIdx - 240), reconcileIdx);
|
|
166
|
+
|
|
167
|
+
assert.ok(
|
|
168
|
+
guardWindow.includes("existsSync(worktreeDbPath)") && guardWindow.includes("!isSamePath(worktreeDbPath, mainDbPath)"),
|
|
169
|
+
"merge-time reconcile requires a real legacy worktree DB and distinct DB paths",
|
|
170
|
+
);
|
|
171
|
+
});
|
|
172
|
+
|
|
160
173
|
// ─── Fix 3: infrastructure error classification ─────────────────────
|
|
161
174
|
|
|
162
175
|
describe("#2823: malformed DB classified as infrastructure error", () => {
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* from the main repo's .gsd/ into the worktree's .gsd/ for the
|
|
6
6
|
* specified milestone, and deletes gsd.db so it rebuilds from fresh state.
|
|
7
7
|
*
|
|
8
|
-
* Also verifies that syncWorktreeStateBack
|
|
9
|
-
*
|
|
8
|
+
* Also verifies that syncWorktreeStateBack does not import worktree markdown
|
|
9
|
+
* projections back into the project root.
|
|
10
10
|
*
|
|
11
11
|
* Covers:
|
|
12
12
|
* - Milestone directory synced from main to worktree
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
* - No-op when paths are equal
|
|
16
16
|
* - No-op when milestoneId is null
|
|
17
17
|
* - Non-existent directories handled gracefully
|
|
18
|
-
* - syncWorktreeStateBack
|
|
19
|
-
* - syncWorktreeStateBack
|
|
20
|
-
* - syncWorktreeStateBack
|
|
21
|
-
* - syncWorktreeStateBack
|
|
18
|
+
* - syncWorktreeStateBack skips milestone markdown projections
|
|
19
|
+
* - syncWorktreeStateBack does not import root-level .gsd/ state projections
|
|
20
|
+
* - syncWorktreeStateBack does not copy worktree milestone projections back
|
|
21
|
+
* - syncWorktreeStateBack leaves next-milestone projections DB/project-root authoritative
|
|
22
22
|
* - syncGsdStateToWorktree syncs non-standard milestone dir names (#1547)
|
|
23
|
-
* - syncWorktreeStateBack
|
|
23
|
+
* - syncWorktreeStateBack skips non-standard milestone projection dir names
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, readFileSync } from 'node:fs';
|
|
@@ -213,8 +213,8 @@ describe('worktree-sync-milestones', async () => {
|
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
// ─── 8. syncWorktreeStateBack
|
|
217
|
-
console.log('\n=== 8. syncWorktreeStateBack
|
|
216
|
+
// ─── 8. syncWorktreeStateBack does not copy task projections ───────────
|
|
217
|
+
console.log('\n=== 8. syncWorktreeStateBack leaves task projections in worktree ===');
|
|
218
218
|
{
|
|
219
219
|
const mainBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-main-'));
|
|
220
220
|
const wtBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-wt-'));
|
|
@@ -232,27 +232,26 @@ describe('worktree-sync-milestones', async () => {
|
|
|
232
232
|
// Main project root starts with only the milestone directory (no slices yet)
|
|
233
233
|
mkdirSync(join(mainBase, '.gsd', 'milestones', 'M002'), { recursive: true });
|
|
234
234
|
|
|
235
|
-
// Pass M001 as milestoneId (the one being merged/skipped), M002 should still sync
|
|
236
235
|
const { synced } = syncWorktreeStateBack(mainBase, wtBase, 'M001');
|
|
237
236
|
|
|
238
237
|
const mainSliceDir = join(mainBase, '.gsd', 'milestones', 'M002', 'slices', 'S01');
|
|
239
238
|
const mainTasksDir = join(mainSliceDir, 'tasks');
|
|
240
239
|
|
|
241
240
|
assert.ok(
|
|
242
|
-
existsSync(join(mainSliceDir, 'S01-SUMMARY.md')),
|
|
243
|
-
'
|
|
241
|
+
!existsSync(join(mainSliceDir, 'S01-SUMMARY.md')),
|
|
242
|
+
'slice SUMMARY projection is not copied to project root',
|
|
244
243
|
);
|
|
245
244
|
assert.ok(
|
|
246
|
-
existsSync(join(mainTasksDir, 'T01-SUMMARY.md')),
|
|
247
|
-
'
|
|
245
|
+
!existsSync(join(mainTasksDir, 'T01-SUMMARY.md')),
|
|
246
|
+
'task T01-SUMMARY projection is not copied to project root',
|
|
248
247
|
);
|
|
249
248
|
assert.ok(
|
|
250
|
-
existsSync(join(mainTasksDir, 'T02-SUMMARY.md')),
|
|
251
|
-
'
|
|
249
|
+
!existsSync(join(mainTasksDir, 'T02-SUMMARY.md')),
|
|
250
|
+
'task T02-SUMMARY projection is not copied to project root',
|
|
252
251
|
);
|
|
253
252
|
assert.ok(
|
|
254
|
-
synced.some((p) => p.includes('tasks/T01-SUMMARY.md')),
|
|
255
|
-
'
|
|
253
|
+
!synced.some((p) => p.includes('tasks/T01-SUMMARY.md')),
|
|
254
|
+
'task summary does not appear in synced list',
|
|
256
255
|
);
|
|
257
256
|
} finally {
|
|
258
257
|
rmSync(mainBase, { recursive: true, force: true });
|
|
@@ -260,8 +259,8 @@ describe('worktree-sync-milestones', async () => {
|
|
|
260
259
|
}
|
|
261
260
|
}
|
|
262
261
|
|
|
263
|
-
// ─── 9. syncWorktreeStateBack
|
|
264
|
-
console.log('\n=== 9. syncWorktreeStateBack
|
|
262
|
+
// ─── 9. syncWorktreeStateBack does not import root-level state projections ──────────
|
|
263
|
+
console.log('\n=== 9. syncWorktreeStateBack leaves root-level state projections authoritative ===');
|
|
265
264
|
{
|
|
266
265
|
const mainBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-root-main-'));
|
|
267
266
|
const wtBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-root-wt-'));
|
|
@@ -281,31 +280,31 @@ describe('worktree-sync-milestones', async () => {
|
|
|
281
280
|
|
|
282
281
|
const { synced } = syncWorktreeStateBack(mainBase, wtBase, 'M001');
|
|
283
282
|
|
|
284
|
-
// Root-level
|
|
283
|
+
// Root-level state projections must not be overwritten with worktree versions.
|
|
285
284
|
const reqContent = readFileSync(join(mainBase, '.gsd', 'REQUIREMENTS.md'), 'utf-8');
|
|
286
285
|
assert.ok(
|
|
287
|
-
reqContent.includes('R002'),
|
|
288
|
-
'REQUIREMENTS.md
|
|
286
|
+
!reqContent.includes('R002'),
|
|
287
|
+
'REQUIREMENTS.md ignores worktree projection content',
|
|
289
288
|
);
|
|
290
289
|
|
|
291
290
|
const projContent = readFileSync(join(mainBase, '.gsd', 'PROJECT.md'), 'utf-8');
|
|
292
291
|
assert.ok(
|
|
293
|
-
projContent.includes('M002'),
|
|
294
|
-
'PROJECT.md
|
|
292
|
+
!projContent.includes('M002'),
|
|
293
|
+
'PROJECT.md ignores worktree projection content',
|
|
295
294
|
);
|
|
296
295
|
|
|
297
296
|
assert.ok(
|
|
298
|
-
existsSync(join(mainBase, '.gsd', 'KNOWLEDGE.md')),
|
|
299
|
-
'KNOWLEDGE.md
|
|
297
|
+
!existsSync(join(mainBase, '.gsd', 'KNOWLEDGE.md')),
|
|
298
|
+
'KNOWLEDGE.md is not copied back from worktree',
|
|
300
299
|
);
|
|
301
300
|
|
|
302
301
|
assert.ok(
|
|
303
|
-
synced.includes('REQUIREMENTS.md'),
|
|
304
|
-
'REQUIREMENTS.md
|
|
302
|
+
!synced.includes('REQUIREMENTS.md'),
|
|
303
|
+
'REQUIREMENTS.md does not appear in synced list',
|
|
305
304
|
);
|
|
306
305
|
assert.ok(
|
|
307
|
-
synced.includes('PROJECT.md'),
|
|
308
|
-
'PROJECT.md
|
|
306
|
+
!synced.includes('PROJECT.md'),
|
|
307
|
+
'PROJECT.md does not appear in synced list',
|
|
309
308
|
);
|
|
310
309
|
} finally {
|
|
311
310
|
rmSync(mainBase, { recursive: true, force: true });
|
|
@@ -313,8 +312,8 @@ describe('worktree-sync-milestones', async () => {
|
|
|
313
312
|
}
|
|
314
313
|
}
|
|
315
314
|
|
|
316
|
-
// ─── 10. syncWorktreeStateBack
|
|
317
|
-
console.log('\n=== 10. syncWorktreeStateBack
|
|
315
|
+
// ─── 10. syncWorktreeStateBack does not copy milestone directories ─────
|
|
316
|
+
console.log('\n=== 10. syncWorktreeStateBack does not copy milestone dirs ===');
|
|
318
317
|
{
|
|
319
318
|
const mainBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-all-main-'));
|
|
320
319
|
const wtBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-all-wt-'));
|
|
@@ -352,19 +351,19 @@ describe('worktree-sync-milestones', async () => {
|
|
|
352
351
|
'M001 SUMMARY NOT synced (current milestone skipped to prevent merge conflicts)',
|
|
353
352
|
);
|
|
354
353
|
|
|
355
|
-
// M002 should be synced
|
|
354
|
+
// M002 should not be synced either; worktree projections are not authoritative.
|
|
356
355
|
assert.ok(
|
|
357
|
-
existsSync(join(mainBase, '.gsd', 'milestones', 'M002-abc123', 'M002-abc123-CONTEXT.md')),
|
|
358
|
-
'M002 CONTEXT
|
|
356
|
+
!existsSync(join(mainBase, '.gsd', 'milestones', 'M002-abc123', 'M002-abc123-CONTEXT.md')),
|
|
357
|
+
'M002 CONTEXT projection is not copied to main',
|
|
359
358
|
);
|
|
360
359
|
assert.ok(
|
|
361
|
-
existsSync(join(mainBase, '.gsd', 'milestones', 'M002-abc123', 'M002-abc123-ROADMAP.md')),
|
|
362
|
-
'M002 ROADMAP
|
|
360
|
+
!existsSync(join(mainBase, '.gsd', 'milestones', 'M002-abc123', 'M002-abc123-ROADMAP.md')),
|
|
361
|
+
'M002 ROADMAP projection is not copied to main',
|
|
363
362
|
);
|
|
364
363
|
|
|
365
364
|
assert.ok(
|
|
366
|
-
synced.some((p) => p.includes('M002-abc123')),
|
|
367
|
-
'M002
|
|
365
|
+
!synced.some((p) => p.includes('M002-abc123')),
|
|
366
|
+
'M002 does not appear in synced list',
|
|
368
367
|
);
|
|
369
368
|
} finally {
|
|
370
369
|
rmSync(mainBase, { recursive: true, force: true });
|
|
@@ -373,7 +372,7 @@ describe('worktree-sync-milestones', async () => {
|
|
|
373
372
|
}
|
|
374
373
|
|
|
375
374
|
// ─── 11. Full M006→M007 transition scenario ───────────────────────────
|
|
376
|
-
console.log('\n=== 11. complete-milestone
|
|
375
|
+
console.log('\n=== 11. complete-milestone worktree projections do not overwrite project root ===');
|
|
377
376
|
{
|
|
378
377
|
const mainBase = mkdtempSync(join(tmpdir(), 'gsd-wt-transition-main-'));
|
|
379
378
|
const wtBase = mkdtempSync(join(tmpdir(), 'gsd-wt-transition-wt-'));
|
|
@@ -419,27 +418,27 @@ describe('worktree-sync-milestones', async () => {
|
|
|
419
418
|
'M006 SUMMARY NOT synced (current milestone skipped)',
|
|
420
419
|
);
|
|
421
420
|
|
|
422
|
-
// Verify M007
|
|
421
|
+
// Verify M007 worktree projections are not copied back.
|
|
423
422
|
assert.ok(
|
|
424
|
-
existsSync(join(mainBase, '.gsd', 'milestones', 'M007-wortc8', 'M007-wortc8-CONTEXT.md')),
|
|
425
|
-
'M007 CONTEXT
|
|
423
|
+
!existsSync(join(mainBase, '.gsd', 'milestones', 'M007-wortc8', 'M007-wortc8-CONTEXT.md')),
|
|
424
|
+
'M007 CONTEXT projection is not copied to main',
|
|
426
425
|
);
|
|
427
426
|
assert.ok(
|
|
428
|
-
existsSync(join(mainBase, '.gsd', 'milestones', 'M007-wortc8', 'M007-wortc8-ROADMAP.md')),
|
|
429
|
-
'M007 ROADMAP
|
|
427
|
+
!existsSync(join(mainBase, '.gsd', 'milestones', 'M007-wortc8', 'M007-wortc8-ROADMAP.md')),
|
|
428
|
+
'M007 ROADMAP projection is not copied to main',
|
|
430
429
|
);
|
|
431
430
|
|
|
432
|
-
// Verify root-level
|
|
431
|
+
// Verify root-level projections remain project-root authoritative.
|
|
433
432
|
const reqContent = readFileSync(join(mainBase, '.gsd', 'REQUIREMENTS.md'), 'utf-8');
|
|
434
433
|
assert.ok(
|
|
435
|
-
reqContent.includes('R090'),
|
|
436
|
-
'REQUIREMENTS.md
|
|
434
|
+
!reqContent.includes('R090'),
|
|
435
|
+
'REQUIREMENTS.md ignores worktree projection updates',
|
|
437
436
|
);
|
|
438
437
|
|
|
439
438
|
const projContent = readFileSync(join(mainBase, '.gsd', 'PROJECT.md'), 'utf-8');
|
|
440
439
|
assert.ok(
|
|
441
|
-
projContent.includes('M007'),
|
|
442
|
-
'PROJECT.md
|
|
440
|
+
!projContent.includes('M007'),
|
|
441
|
+
'PROJECT.md ignores worktree projection updates',
|
|
443
442
|
);
|
|
444
443
|
} finally {
|
|
445
444
|
rmSync(mainBase, { recursive: true, force: true });
|
|
@@ -478,8 +477,8 @@ describe('worktree-sync-milestones', async () => {
|
|
|
478
477
|
}
|
|
479
478
|
}
|
|
480
479
|
|
|
481
|
-
// ─── 13. syncWorktreeStateBack
|
|
482
|
-
console.log('\n=== 13. QUEUE.md
|
|
480
|
+
// ─── 13. syncWorktreeStateBack skips QUEUE.md but preserves completed-units diagnostics ──
|
|
481
|
+
console.log('\n=== 13. QUEUE.md skipped; completed-units.json diagnostic synced ===');
|
|
483
482
|
{
|
|
484
483
|
const mainBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-queue-main-'));
|
|
485
484
|
const wtBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-queue-wt-'));
|
|
@@ -488,7 +487,7 @@ describe('worktree-sync-milestones', async () => {
|
|
|
488
487
|
mkdirSync(join(mainBase, '.gsd', 'milestones', 'M001'), { recursive: true });
|
|
489
488
|
mkdirSync(join(wtBase, '.gsd', 'milestones', 'M001'), { recursive: true });
|
|
490
489
|
|
|
491
|
-
// Worktree has QUEUE.md and completed-units.json
|
|
490
|
+
// Worktree has QUEUE.md projection and completed-units.json diagnostic.
|
|
492
491
|
writeFileSync(join(wtBase, '.gsd', 'QUEUE.md'), '# Queue\n- M002 next');
|
|
493
492
|
writeFileSync(
|
|
494
493
|
join(wtBase, '.gsd', 'completed-units.json'),
|
|
@@ -507,22 +506,17 @@ describe('worktree-sync-milestones', async () => {
|
|
|
507
506
|
|
|
508
507
|
const { synced } = syncWorktreeStateBack(mainBase, wtBase, 'M001');
|
|
509
508
|
|
|
510
|
-
// QUEUE.md should be
|
|
511
|
-
assert.ok(
|
|
512
|
-
existsSync(join(mainBase, '.gsd', 'QUEUE.md')),
|
|
513
|
-
'#1787: QUEUE.md synced from worktree to main',
|
|
514
|
-
);
|
|
515
|
-
const queueContent = readFileSync(join(mainBase, '.gsd', 'QUEUE.md'), 'utf-8');
|
|
509
|
+
// QUEUE.md is state/projection content and should not be copied back.
|
|
516
510
|
assert.ok(
|
|
517
|
-
|
|
518
|
-
'
|
|
511
|
+
!existsSync(join(mainBase, '.gsd', 'QUEUE.md')),
|
|
512
|
+
'QUEUE.md is not synced from worktree to main',
|
|
519
513
|
);
|
|
520
514
|
assert.ok(
|
|
521
|
-
synced.includes('QUEUE.md'),
|
|
522
|
-
'
|
|
515
|
+
!synced.includes('QUEUE.md'),
|
|
516
|
+
'QUEUE.md does not appear in synced list',
|
|
523
517
|
);
|
|
524
518
|
|
|
525
|
-
// completed-units.json
|
|
519
|
+
// completed-units.json is diagnostic and may be copied for operator visibility.
|
|
526
520
|
assert.ok(
|
|
527
521
|
existsSync(join(mainBase, '.gsd', 'completed-units.json')),
|
|
528
522
|
'#1787: completed-units.json synced from worktree to main',
|
|
@@ -578,8 +572,8 @@ describe('worktree-sync-milestones', async () => {
|
|
|
578
572
|
}
|
|
579
573
|
}
|
|
580
574
|
|
|
581
|
-
// ─── 15. syncWorktreeStateBack
|
|
582
|
-
console.log('\n=== 15. syncWorktreeStateBack
|
|
575
|
+
// ─── 15. syncWorktreeStateBack skips non-standard milestone dir names ──
|
|
576
|
+
console.log('\n=== 15. syncWorktreeStateBack skips non-standard milestone dir names ===');
|
|
583
577
|
{
|
|
584
578
|
const mainBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-custom-main-'));
|
|
585
579
|
const wtBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-custom-wt-'));
|
|
@@ -601,12 +595,12 @@ describe('worktree-sync-milestones', async () => {
|
|
|
601
595
|
const { synced } = syncWorktreeStateBack(mainBase, wtBase, 'M001');
|
|
602
596
|
|
|
603
597
|
assert.ok(
|
|
604
|
-
existsSync(join(mainBase, '.gsd', 'milestones', 'sprint-beta', 'SUMMARY.md')),
|
|
605
|
-
'
|
|
598
|
+
!existsSync(join(mainBase, '.gsd', 'milestones', 'sprint-beta', 'SUMMARY.md')),
|
|
599
|
+
'non-standard milestone projection is not copied back to main',
|
|
606
600
|
);
|
|
607
601
|
assert.ok(
|
|
608
|
-
synced.some((p) => p.includes('sprint-beta')),
|
|
609
|
-
'
|
|
602
|
+
!synced.some((p) => p.includes('sprint-beta')),
|
|
603
|
+
'sprint-beta does not appear in synced list',
|
|
610
604
|
);
|
|
611
605
|
} finally {
|
|
612
606
|
rmSync(mainBase, { recursive: true, force: true });
|