gsd-pi 2.78.1-dev.b6a389b66 → 2.78.1-dev.d8826a445

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.
Files changed (155) hide show
  1. package/dist/resources/.managed-resources-content-hash +1 -1
  2. package/dist/resources/extensions/gsd/auto/phases.js +7 -2
  3. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  4. package/dist/resources/extensions/gsd/auto-dispatch.js +3 -2
  5. package/dist/resources/extensions/gsd/auto-post-unit.js +7 -1
  6. package/dist/resources/extensions/gsd/auto-worktree.js +185 -40
  7. package/dist/resources/extensions/gsd/auto.js +62 -1
  8. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +1 -1
  9. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +17 -16
  10. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +67 -55
  11. package/dist/resources/extensions/gsd/db-writer.js +96 -16
  12. package/dist/resources/extensions/gsd/delegation-policy.js +155 -0
  13. package/dist/resources/extensions/gsd/gsd-db.js +194 -0
  14. package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
  15. package/dist/resources/extensions/gsd/guided-flow.js +117 -25
  16. package/dist/resources/extensions/gsd/metrics.js +287 -1
  17. package/dist/resources/extensions/gsd/paths.js +79 -8
  18. package/dist/resources/extensions/gsd/prompts/complete-slice.md +4 -4
  19. package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -3
  20. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +8 -1
  21. package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +22 -7
  22. package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +6 -2
  23. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -1
  24. package/dist/resources/extensions/gsd/templates/project.md +10 -0
  25. package/dist/resources/extensions/gsd/workflow-mcp.js +2 -2
  26. package/dist/resources/extensions/gsd/workspace.js +59 -0
  27. package/dist/resources/extensions/gsd/worktree-resolver.js +15 -2
  28. package/dist/resources/extensions/gsd/write-intercept.js +3 -3
  29. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  30. package/dist/web/standalone/.next/BUILD_ID +1 -1
  31. package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
  32. package/dist/web/standalone/.next/build-manifest.json +2 -2
  33. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  34. package/dist/web/standalone/.next/required-server-files.json +1 -1
  35. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  36. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  44. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/index.html +1 -1
  52. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
  59. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  60. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  61. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  62. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  63. package/dist/web/standalone/server.js +1 -1
  64. package/package.json +1 -1
  65. package/packages/mcp-server/README.md +2 -11
  66. package/packages/mcp-server/dist/remote-questions.d.ts +27 -0
  67. package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -1
  68. package/packages/mcp-server/dist/remote-questions.js +28 -0
  69. package/packages/mcp-server/dist/remote-questions.js.map +1 -1
  70. package/packages/mcp-server/dist/server.d.ts +28 -0
  71. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  72. package/packages/mcp-server/dist/server.js +94 -4
  73. package/packages/mcp-server/dist/server.js.map +1 -1
  74. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  75. package/packages/mcp-server/src/mcp-server.test.ts +226 -0
  76. package/packages/mcp-server/src/remote-questions.test.ts +103 -0
  77. package/packages/mcp-server/src/remote-questions.ts +35 -0
  78. package/packages/mcp-server/src/server.ts +129 -6
  79. package/packages/mcp-server/src/workflow-tools.ts +1 -1
  80. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  81. package/src/resources/extensions/gsd/auto/phases.ts +8 -2
  82. package/src/resources/extensions/gsd/auto/session.ts +4 -0
  83. package/src/resources/extensions/gsd/auto-dispatch.ts +10 -2
  84. package/src/resources/extensions/gsd/auto-post-unit.ts +8 -1
  85. package/src/resources/extensions/gsd/auto-worktree.ts +225 -47
  86. package/src/resources/extensions/gsd/auto.ts +79 -1
  87. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +1 -1
  88. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +17 -17
  89. package/src/resources/extensions/gsd/bootstrap/tests/write-gate-basepath.test.ts +103 -0
  90. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +80 -55
  91. package/src/resources/extensions/gsd/db-writer.ts +113 -17
  92. package/src/resources/extensions/gsd/delegation-policy.ts +197 -0
  93. package/src/resources/extensions/gsd/gsd-db.ts +184 -0
  94. package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
  95. package/src/resources/extensions/gsd/guided-flow.ts +154 -25
  96. package/src/resources/extensions/gsd/metrics.ts +321 -1
  97. package/src/resources/extensions/gsd/paths.ts +67 -8
  98. package/src/resources/extensions/gsd/prompts/complete-slice.md +4 -4
  99. package/src/resources/extensions/gsd/prompts/execute-task.md +3 -3
  100. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +8 -1
  101. package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +22 -7
  102. package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +6 -2
  103. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -1
  104. package/src/resources/extensions/gsd/templates/project.md +10 -0
  105. package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +14 -14
  106. package/src/resources/extensions/gsd/tests/auto-session-scope.test.ts +331 -0
  107. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +176 -0
  108. package/src/resources/extensions/gsd/tests/db-writer-path-containment.test.ts +152 -0
  109. package/src/resources/extensions/gsd/tests/db-writer-root-artifact.test.ts +221 -0
  110. package/src/resources/extensions/gsd/tests/db-writer-scope.test.ts +230 -0
  111. package/src/resources/extensions/gsd/tests/delegation-policy.test.ts +151 -0
  112. package/src/resources/extensions/gsd/tests/dispatch-backgroundable-annotation.test.ts +55 -0
  113. package/src/resources/extensions/gsd/tests/draft-promotion.test.ts +3 -23
  114. package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +193 -0
  115. package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound-corrections.test.ts +246 -0
  116. package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound.test.ts +218 -0
  117. package/src/resources/extensions/gsd/tests/gsd-db-failed-open-restore.test.ts +117 -0
  118. package/src/resources/extensions/gsd/tests/gsd-db-workspace-scope.test.ts +226 -0
  119. package/src/resources/extensions/gsd/tests/gsd-root-canonical.test.ts +66 -0
  120. package/src/resources/extensions/gsd/tests/gsd-root-home-guard.test.ts +68 -5
  121. package/src/resources/extensions/gsd/tests/guided-flow-prompt-consolidation.test.ts +4 -4
  122. package/src/resources/extensions/gsd/tests/integration/workspace-collapse-integration.test.ts +371 -0
  123. package/src/resources/extensions/gsd/tests/metrics-atomic-merge.test.ts +222 -0
  124. package/src/resources/extensions/gsd/tests/metrics-lock-hardening.test.ts +400 -0
  125. package/src/resources/extensions/gsd/tests/metrics-lock-not-acquired.test.ts +141 -0
  126. package/src/resources/extensions/gsd/tests/metrics-lock-retry-sleep.test.ts +287 -0
  127. package/src/resources/extensions/gsd/tests/metrics-prune-cache-invalidation.test.ts +149 -0
  128. package/src/resources/extensions/gsd/tests/metrics-scope.test.ts +378 -0
  129. package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +329 -0
  130. package/src/resources/extensions/gsd/tests/path-cache-decoupled.test.ts +209 -0
  131. package/src/resources/extensions/gsd/tests/path-normalization-unified.test.ts +175 -0
  132. package/src/resources/extensions/gsd/tests/paths-cache.test.ts +170 -0
  133. package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +120 -0
  134. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +150 -7
  135. package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +74 -0
  136. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +28 -16
  137. package/src/resources/extensions/gsd/tests/resume-missing-worktree-warning.test.ts +209 -0
  138. package/src/resources/extensions/gsd/tests/sync-layer-scope.test.ts +453 -0
  139. package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +162 -0
  140. package/src/resources/extensions/gsd/tests/teardown-cleanup-parity.test.ts +102 -0
  141. package/src/resources/extensions/gsd/tests/teardown-failure-clears-registry.test.ts +186 -0
  142. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +1 -1
  143. package/src/resources/extensions/gsd/tests/validator-scope-parity.test.ts +239 -0
  144. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -2
  145. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +9 -15
  146. package/src/resources/extensions/gsd/tests/workspace.test.ts +190 -0
  147. package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +35 -35
  148. package/src/resources/extensions/gsd/tests/write-gate.test.ts +67 -52
  149. package/src/resources/extensions/gsd/tests/write-intercept.test.ts +1 -1
  150. package/src/resources/extensions/gsd/workflow-mcp.ts +2 -2
  151. package/src/resources/extensions/gsd/workspace.ts +95 -0
  152. package/src/resources/extensions/gsd/worktree-resolver.ts +16 -2
  153. package/src/resources/extensions/gsd/write-intercept.ts +3 -3
  154. /package/dist/web/standalone/.next/static/{HahrZrc_Xn4wumj0O1Ydp → AT5qi39nKXkdmQIOIoh0f}/_buildManifest.js +0 -0
  155. /package/dist/web/standalone/.next/static/{HahrZrc_Xn4wumj0O1Ydp → AT5qi39nKXkdmQIOIoh0f}/_ssgManifest.js +0 -0
@@ -0,0 +1,190 @@
1
+ // GSD-2 + Workspace handle tests: createWorkspace and scopeMilestone
2
+
3
+ import { describe, test, beforeEach, afterEach } from "node:test";
4
+ import assert from "node:assert/strict";
5
+ import {
6
+ mkdtempSync,
7
+ mkdirSync,
8
+ rmSync,
9
+ realpathSync,
10
+ symlinkSync,
11
+ } from "node:fs";
12
+ import { tmpdir } from "node:os";
13
+ import { join } from "node:path";
14
+
15
+ import { createWorkspace, scopeMilestone } from "../workspace.ts";
16
+
17
+ // ─── Helpers ────────────────────────────────────────────────────────────────
18
+
19
+ function makeProjectDir(): string {
20
+ const dir = realpathSync(mkdtempSync(join(tmpdir(), "gsd-ws-test-")));
21
+ mkdirSync(join(dir, ".gsd", "milestones"), { recursive: true });
22
+ return dir;
23
+ }
24
+
25
+ // ─── Tests ──────────────────────────────────────────────────────────────────
26
+
27
+ describe("createWorkspace", () => {
28
+ let projectDir: string;
29
+
30
+ beforeEach(() => {
31
+ projectDir = makeProjectDir();
32
+ });
33
+
34
+ afterEach(() => {
35
+ rmSync(projectDir, { recursive: true, force: true });
36
+ });
37
+
38
+ test("from a project root produces mode=project and worktreeRoot=null", () => {
39
+ const ws = createWorkspace(projectDir);
40
+ assert.equal(ws.mode, "project");
41
+ assert.equal(ws.worktreeRoot, null);
42
+ assert.equal(ws.projectRoot, realpathSync(projectDir));
43
+ });
44
+
45
+ test("from a worktree path produces mode=worktree, worktreeRoot=realpath, projectRoot=realpath of project", () => {
46
+ // Construct a worktree path: <projectDir>/.gsd/worktrees/M001
47
+ const worktreePath = join(projectDir, ".gsd", "worktrees", "M001");
48
+ mkdirSync(worktreePath, { recursive: true });
49
+
50
+ const ws = createWorkspace(worktreePath);
51
+ assert.equal(ws.mode, "worktree");
52
+ assert.equal(ws.worktreeRoot, realpathSync(worktreePath));
53
+ assert.equal(ws.projectRoot, realpathSync(projectDir));
54
+ });
55
+
56
+ test("normalizes /foo and /foo/ to identical identityKey", () => {
57
+ const wsTrailing = createWorkspace(projectDir + "/");
58
+ const wsNoTrailing = createWorkspace(projectDir);
59
+ assert.equal(wsTrailing.identityKey, wsNoTrailing.identityKey);
60
+ });
61
+
62
+ test("follows symlinks — identityKey matches realpath of target", (t) => {
63
+ const linkPath = join(tmpdir(), `gsd-ws-link-${Date.now()}`);
64
+ symlinkSync(projectDir, linkPath);
65
+ t.after(() => {
66
+ try { rmSync(linkPath); } catch { /* ignore */ }
67
+ });
68
+
69
+ const ws = createWorkspace(linkPath);
70
+ assert.equal(ws.identityKey, realpathSync(projectDir));
71
+ });
72
+ });
73
+
74
+ describe("GsdWorkspace and MilestoneScope are frozen", () => {
75
+ let projectDir: string;
76
+
77
+ beforeEach(() => {
78
+ projectDir = makeProjectDir();
79
+ });
80
+
81
+ afterEach(() => {
82
+ rmSync(projectDir, { recursive: true, force: true });
83
+ });
84
+
85
+ test("workspace is frozen", () => {
86
+ const ws = createWorkspace(projectDir);
87
+ assert.ok(Object.isFrozen(ws), "workspace should be frozen");
88
+ assert.throws(() => {
89
+ (ws as { mode: string }).mode = "worktree";
90
+ }, /Cannot assign/);
91
+ });
92
+
93
+ test("scope is frozen", () => {
94
+ const ws = createWorkspace(projectDir);
95
+ const scope = scopeMilestone(ws, "M001");
96
+ assert.ok(Object.isFrozen(scope), "scope should be frozen");
97
+ assert.throws(() => {
98
+ (scope as { milestoneId: string }).milestoneId = "M999";
99
+ }, /Cannot assign/);
100
+ });
101
+
102
+ test("contract inside workspace is frozen", () => {
103
+ const ws = createWorkspace(projectDir);
104
+ assert.ok(Object.isFrozen(ws.contract), "contract should be frozen");
105
+ });
106
+ });
107
+
108
+ describe("scopeMilestone path methods", () => {
109
+ let projectDir: string;
110
+ const MID = "M001";
111
+
112
+ beforeEach(() => {
113
+ projectDir = makeProjectDir();
114
+ });
115
+
116
+ afterEach(() => {
117
+ rmSync(projectDir, { recursive: true, force: true });
118
+ });
119
+
120
+ test("produces correct paths for a known milestone ID", () => {
121
+ const ws = createWorkspace(projectDir);
122
+ const scope = scopeMilestone(ws, MID);
123
+ const gsd = ws.contract.projectGsd;
124
+
125
+ assert.equal(scope.milestoneId, MID);
126
+ assert.equal(scope.contextFile(), join(gsd, "milestones", MID, `${MID}-CONTEXT.md`));
127
+ assert.equal(scope.roadmapFile(), join(gsd, "milestones", MID, `${MID}-ROADMAP.md`));
128
+ assert.equal(scope.stateFile(), join(gsd, "STATE.md"));
129
+ assert.equal(scope.dbPath(), ws.contract.projectDb);
130
+ assert.equal(scope.milestoneDir(), join(gsd, "milestones", MID));
131
+ assert.equal(scope.metaJson(), join(gsd, `${MID}-META.json`));
132
+ });
133
+
134
+ test("two scopes from same workspace + same MID produce identical paths", () => {
135
+ const ws = createWorkspace(projectDir);
136
+ const scope1 = scopeMilestone(ws, MID);
137
+ const scope2 = scopeMilestone(ws, MID);
138
+
139
+ assert.equal(scope1.contextFile(), scope2.contextFile());
140
+ assert.equal(scope1.roadmapFile(), scope2.roadmapFile());
141
+ assert.equal(scope1.stateFile(), scope2.stateFile());
142
+ assert.equal(scope1.dbPath(), scope2.dbPath());
143
+ assert.equal(scope1.milestoneDir(), scope2.milestoneDir());
144
+ assert.equal(scope1.metaJson(), scope2.metaJson());
145
+ });
146
+ });
147
+
148
+ describe("createWorkspace: contract.projectGsd is realpath-canonicalized when basePath is a symlink", () => {
149
+ let projectDir: string;
150
+ let linkPath: string;
151
+
152
+ beforeEach(() => {
153
+ projectDir = makeProjectDir();
154
+ linkPath = join(tmpdir(), `gsd-ws-symlink-${Date.now()}`);
155
+ symlinkSync(projectDir, linkPath);
156
+ });
157
+
158
+ afterEach(() => {
159
+ try { rmSync(linkPath); } catch { /* ignore */ }
160
+ rmSync(projectDir, { recursive: true, force: true });
161
+ });
162
+
163
+ test("contract.projectGsd matches realpath of projectRoot when workspace is created via symlink", () => {
164
+ const ws = createWorkspace(linkPath);
165
+
166
+ const canonicalProjectRoot = realpathSync(projectDir);
167
+
168
+ // identityKey must be the realpath of the canonical project root
169
+ assert.equal(ws.identityKey, canonicalProjectRoot);
170
+ assert.equal(ws.projectRoot, canonicalProjectRoot);
171
+
172
+ // contract.projectGsd must start with the canonical project root —
173
+ // not with the symlink path. If the bug is present, contract.projectGsd
174
+ // would be linkPath + "/.gsd" instead of canonicalProjectRoot + "/.gsd".
175
+ assert.ok(
176
+ ws.contract.projectGsd.startsWith(canonicalProjectRoot),
177
+ `contract.projectGsd ("${ws.contract.projectGsd}") must be under the realpath'd projectRoot ("${canonicalProjectRoot}"), not the symlink path`,
178
+ );
179
+ });
180
+
181
+ test("contract.projectDb matches realpath of projectRoot when workspace is created via symlink", () => {
182
+ const ws = createWorkspace(linkPath);
183
+ const canonicalProjectRoot = realpathSync(projectDir);
184
+
185
+ assert.ok(
186
+ ws.contract.projectDb.startsWith(canonicalProjectRoot),
187
+ `contract.projectDb ("${ws.contract.projectDb}") must be under the realpath'd projectRoot ("${canonicalProjectRoot}")`,
188
+ );
189
+ });
190
+ });
@@ -24,38 +24,38 @@ import {
24
24
  // ─── shouldBlockQueueExecution ────────────────────────────────────────────
25
25
 
26
26
  test('shouldBlockQueueExecution: queue inactive → allow write to user source', (t) => {
27
- t.after(() => clearDiscussionFlowState());
28
- setQueuePhaseActive(false);
27
+ t.after(() => clearDiscussionFlowState(process.cwd()));
28
+ setQueuePhaseActive(false, process.cwd());
29
29
  const r = shouldBlockQueueExecution('write', 'src/main.ts', false);
30
30
  assert.strictEqual(r.block, false);
31
31
  });
32
32
 
33
33
  test('shouldBlockQueueExecution: queue active → block write to user source', (t) => {
34
- t.after(() => clearDiscussionFlowState());
35
- setQueuePhaseActive(true);
34
+ t.after(() => clearDiscussionFlowState(process.cwd()));
35
+ setQueuePhaseActive(true, process.cwd());
36
36
  const r = shouldBlockQueueExecution('write', 'src/main.ts', true);
37
37
  assert.strictEqual(r.block, true);
38
38
  assert.ok(r.reason);
39
39
  });
40
40
 
41
41
  test('shouldBlockQueueExecution: queue active → allow write to .gsd/ path', (t) => {
42
- t.after(() => clearDiscussionFlowState());
43
- setQueuePhaseActive(true);
42
+ t.after(() => clearDiscussionFlowState(process.cwd()));
43
+ setQueuePhaseActive(true, process.cwd());
44
44
  const r = shouldBlockQueueExecution('write', '.gsd/milestones/M001/M001-CONTEXT.md', true);
45
45
  assert.strictEqual(r.block, false);
46
46
  });
47
47
 
48
48
  test('shouldBlockQueueExecution: queue active → block mutating bash', (t) => {
49
- t.after(() => clearDiscussionFlowState());
50
- setQueuePhaseActive(true);
49
+ t.after(() => clearDiscussionFlowState(process.cwd()));
50
+ setQueuePhaseActive(true, process.cwd());
51
51
  const r = shouldBlockQueueExecution('bash', 'npm run build', true);
52
52
  assert.strictEqual(r.block, true);
53
53
  assert.ok(r.reason);
54
54
  });
55
55
 
56
56
  test('shouldBlockQueueExecution: queue active → allow read-only bash', (t) => {
57
- t.after(() => clearDiscussionFlowState());
58
- setQueuePhaseActive(true);
57
+ t.after(() => clearDiscussionFlowState(process.cwd()));
58
+ setQueuePhaseActive(true, process.cwd());
59
59
  const r = shouldBlockQueueExecution('bash', 'git log --oneline -5', true);
60
60
  assert.strictEqual(r.block, false);
61
61
  });
@@ -63,30 +63,30 @@ test('shouldBlockQueueExecution: queue active → allow read-only bash', (t) =>
63
63
  // ─── shouldBlockPendingGate ───────────────────────────────────────────────
64
64
 
65
65
  test('shouldBlockPendingGate: no pending gate → allow any tool', (t) => {
66
- t.after(() => clearDiscussionFlowState());
67
- clearPendingGate();
66
+ t.after(() => clearDiscussionFlowState(process.cwd()));
67
+ clearPendingGate(process.cwd());
68
68
  const r = shouldBlockPendingGate('write', 'M001');
69
69
  assert.strictEqual(r.block, false);
70
70
  });
71
71
 
72
72
  test('shouldBlockPendingGate: pending gate → block write', (t) => {
73
- t.after(() => clearDiscussionFlowState());
74
- setPendingGate('depth_verification_M001');
73
+ t.after(() => clearDiscussionFlowState(process.cwd()));
74
+ setPendingGate('depth_verification_M001', process.cwd());
75
75
  const r = shouldBlockPendingGate('write', 'M001');
76
76
  assert.strictEqual(r.block, true);
77
77
  assert.ok(r.reason?.includes('depth_verification_M001'));
78
78
  });
79
79
 
80
80
  test('shouldBlockPendingGate: pending gate → allow ask_user_questions', (t) => {
81
- t.after(() => clearDiscussionFlowState());
82
- setPendingGate('depth_verification_M001');
81
+ t.after(() => clearDiscussionFlowState(process.cwd()));
82
+ setPendingGate('depth_verification_M001', process.cwd());
83
83
  const r = shouldBlockPendingGate('ask_user_questions', 'M001');
84
84
  assert.strictEqual(r.block, false);
85
85
  });
86
86
 
87
87
  test('shouldBlockPendingGate: pending gate → block read so approval question stays visible', (t) => {
88
- t.after(() => clearDiscussionFlowState());
89
- setPendingGate('depth_verification_M001');
88
+ t.after(() => clearDiscussionFlowState(process.cwd()));
89
+ setPendingGate('depth_verification_M001', process.cwd());
90
90
  const r = shouldBlockPendingGate('read', 'M001');
91
91
  assert.strictEqual(r.block, true);
92
92
  assert.ok(r.reason?.includes('already asked for user confirmation'));
@@ -95,31 +95,31 @@ test('shouldBlockPendingGate: pending gate → block read so approval question s
95
95
  // ─── shouldBlockPendingGateBash ───────────────────────────────────────────
96
96
 
97
97
  test('shouldBlockPendingGateBash: no pending gate → allow mutating bash', (t) => {
98
- t.after(() => clearDiscussionFlowState());
99
- clearPendingGate();
98
+ t.after(() => clearDiscussionFlowState(process.cwd()));
99
+ clearPendingGate(process.cwd());
100
100
  const r = shouldBlockPendingGateBash('npm run build', 'M001');
101
101
  assert.strictEqual(r.block, false);
102
102
  });
103
103
 
104
104
  test('shouldBlockPendingGateBash: pending gate → block mutating bash', (t) => {
105
- t.after(() => clearDiscussionFlowState());
106
- setPendingGate('depth_verification_M001');
105
+ t.after(() => clearDiscussionFlowState(process.cwd()));
106
+ setPendingGate('depth_verification_M001', process.cwd());
107
107
  const r = shouldBlockPendingGateBash('npm run build', 'M001');
108
108
  assert.strictEqual(r.block, true);
109
109
  assert.ok(r.reason?.includes('depth_verification_M001'));
110
110
  });
111
111
 
112
112
  test('shouldBlockPendingGateBash: pending gate → block read-only bash (cat)', (t) => {
113
- t.after(() => clearDiscussionFlowState());
114
- setPendingGate('depth_verification_M001');
113
+ t.after(() => clearDiscussionFlowState(process.cwd()));
114
+ setPendingGate('depth_verification_M001', process.cwd());
115
115
  const r = shouldBlockPendingGateBash('cat README.md', 'M001');
116
116
  assert.strictEqual(r.block, true);
117
117
  assert.ok(r.reason?.includes('already asked for user confirmation'));
118
118
  });
119
119
 
120
120
  test('shouldBlockPendingGateBash: pending gate → block read-only bash (git log)', (t) => {
121
- t.after(() => clearDiscussionFlowState());
122
- setPendingGate('depth_verification_M001');
121
+ t.after(() => clearDiscussionFlowState(process.cwd()));
122
+ setPendingGate('depth_verification_M001', process.cwd());
123
123
  const r = shouldBlockPendingGateBash('git log --oneline -10', 'M001');
124
124
  assert.strictEqual(r.block, true);
125
125
  });
@@ -127,26 +127,26 @@ test('shouldBlockPendingGateBash: pending gate → block read-only bash (git log
127
127
  // ─── shouldBlockContextWrite ──────────────────────────────────────────────
128
128
 
129
129
  test('shouldBlockContextWrite: non-write tool → allow', (t) => {
130
- t.after(() => clearDiscussionFlowState());
130
+ t.after(() => clearDiscussionFlowState(process.cwd()));
131
131
  const r = shouldBlockContextWrite('read', '.gsd/milestones/M001/M001-CONTEXT.md', 'M001');
132
132
  assert.strictEqual(r.block, false);
133
133
  });
134
134
 
135
135
  test('shouldBlockContextWrite: write to non-CONTEXT file → allow', (t) => {
136
- t.after(() => clearDiscussionFlowState());
136
+ t.after(() => clearDiscussionFlowState(process.cwd()));
137
137
  const r = shouldBlockContextWrite('write', 'src/index.ts', 'M001');
138
138
  assert.strictEqual(r.block, false);
139
139
  });
140
140
 
141
141
  test('shouldBlockContextWrite: write to CONTEXT.md without verification → block', (t) => {
142
- t.after(() => clearDiscussionFlowState());
142
+ t.after(() => clearDiscussionFlowState(process.cwd()));
143
143
  const r = shouldBlockContextWrite('write', '.gsd/milestones/M007/M007-CONTEXT.md', 'M007');
144
144
  assert.strictEqual(r.block, true);
145
145
  assert.ok(r.reason);
146
146
  });
147
147
 
148
148
  test('shouldBlockContextWrite: write to CONTEXT.md after verification → allow', (t) => {
149
- t.after(() => clearDiscussionFlowState());
149
+ t.after(() => clearDiscussionFlowState(process.cwd()));
150
150
  markDepthVerified('M008');
151
151
  const r = shouldBlockContextWrite('write', '.gsd/milestones/M008/M008-CONTEXT.md', 'M008');
152
152
  assert.strictEqual(r.block, false);
@@ -155,33 +155,33 @@ test('shouldBlockContextWrite: write to CONTEXT.md after verification → allow'
155
155
  // ─── shouldBlockContextArtifactSave ───────────────────────────────────────
156
156
 
157
157
  test('shouldBlockContextArtifactSave: non-CONTEXT artifact type → allow', (t) => {
158
- t.after(() => clearDiscussionFlowState());
158
+ t.after(() => clearDiscussionFlowState(process.cwd()));
159
159
  const r = shouldBlockContextArtifactSave('CONTEXT-DRAFT', 'M001');
160
160
  assert.strictEqual(r.block, false);
161
161
  });
162
162
 
163
163
  test('shouldBlockContextArtifactSave: slice-level CONTEXT → allow', (t) => {
164
- t.after(() => clearDiscussionFlowState());
164
+ t.after(() => clearDiscussionFlowState(process.cwd()));
165
165
  const r = shouldBlockContextArtifactSave('CONTEXT', 'M001', 'S01');
166
166
  assert.strictEqual(r.block, false);
167
167
  });
168
168
 
169
169
  test('shouldBlockContextArtifactSave: milestone CONTEXT without verification → block', (t) => {
170
- t.after(() => clearDiscussionFlowState());
170
+ t.after(() => clearDiscussionFlowState(process.cwd()));
171
171
  const r = shouldBlockContextArtifactSave('CONTEXT', 'M009');
172
172
  assert.strictEqual(r.block, true);
173
173
  assert.ok(r.reason?.includes('M009'));
174
174
  });
175
175
 
176
176
  test('shouldBlockContextArtifactSave: milestone CONTEXT after verification → allow', (t) => {
177
- t.after(() => clearDiscussionFlowState());
177
+ t.after(() => clearDiscussionFlowState(process.cwd()));
178
178
  markDepthVerified('M010');
179
179
  const r = shouldBlockContextArtifactSave('CONTEXT', 'M010');
180
180
  assert.strictEqual(r.block, false);
181
181
  });
182
182
 
183
183
  test('shouldBlockContextArtifactSave: CONTEXT with no milestoneId → block', (t) => {
184
- t.after(() => clearDiscussionFlowState());
184
+ t.after(() => clearDiscussionFlowState(process.cwd()));
185
185
  const r = shouldBlockContextArtifactSave('CONTEXT', null);
186
186
  assert.strictEqual(r.block, true);
187
187
  assert.ok(r.reason);