gsd-pi 2.41.0-dev.9446b20 → 2.41.0-dev.b832948
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 +69 -29
- package/dist/cli-web-branch.d.ts +6 -0
- package/dist/cli-web-branch.js +17 -0
- package/dist/onboarding.js +2 -1
- package/dist/resources/extensions/gsd/auto/loop.js +9 -1
- package/dist/resources/extensions/gsd/auto/phases.js +26 -8
- package/dist/resources/extensions/gsd/auto-dashboard.js +6 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +19 -2
- package/dist/resources/extensions/gsd/auto-post-unit.js +7 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +12 -4
- package/dist/resources/extensions/gsd/auto-start.js +8 -3
- package/dist/resources/extensions/gsd/auto-worktree.js +147 -13
- package/dist/resources/extensions/gsd/auto.js +36 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +199 -164
- package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +62 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +16 -0
- package/dist/resources/extensions/gsd/commands/catalog.js +8 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
- package/dist/resources/extensions/gsd/context-store.js +4 -3
- package/dist/resources/extensions/gsd/db-writer.js +5 -2
- package/dist/resources/extensions/gsd/detection.js +1 -1
- package/dist/resources/extensions/gsd/doctor.js +11 -1
- package/dist/resources/extensions/gsd/exit-command.js +12 -2
- package/dist/resources/extensions/gsd/export.js +9 -13
- package/dist/resources/extensions/gsd/extension-manifest.json +2 -2
- package/dist/resources/extensions/gsd/files.js +28 -11
- package/dist/resources/extensions/gsd/forensics.js +10 -3
- package/dist/resources/extensions/gsd/git-service.js +5 -1
- package/dist/resources/extensions/gsd/gsd-db.js +25 -8
- package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
- package/dist/resources/extensions/gsd/guided-flow.js +7 -3
- package/dist/resources/extensions/gsd/journal.js +85 -0
- package/dist/resources/extensions/gsd/md-importer.js +5 -0
- package/dist/resources/extensions/gsd/milestone-ids.js +1 -1
- package/dist/resources/extensions/gsd/native-git-bridge.js +2 -2
- package/dist/resources/extensions/gsd/post-unit-hooks.js +24 -412
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences.js +1 -0
- package/dist/resources/extensions/gsd/prompt-loader.js +34 -4
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
- package/dist/resources/extensions/gsd/prompts/discuss.md +1 -1
- package/dist/resources/extensions/gsd/prompts/queue.md +1 -1
- package/dist/resources/extensions/gsd/repo-identity.js +46 -2
- package/dist/resources/extensions/gsd/rule-registry.js +489 -0
- package/dist/resources/extensions/gsd/rule-types.js +6 -0
- package/dist/resources/extensions/gsd/service-tier.js +138 -0
- package/dist/resources/extensions/gsd/structured-data-formatter.js +2 -1
- package/dist/resources/extensions/gsd/templates/decisions.md +2 -2
- package/dist/resources/extensions/gsd/workflow-templates.js +13 -1
- package/dist/resources/extensions/gsd/worktree-manager.js +20 -6
- package/dist/resources/extensions/gsd/worktree-resolver.js +19 -2
- package/dist/resources/extensions/subagent/index.js +7 -3
- package/dist/resources/extensions/voice/index.js +4 -4
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- 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 +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +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 +17 -17
- package/dist/web/standalone/.next/server/chunks/229.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/4024.c195dc1fdd2adbea.js +9 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-9afaaebf6042a1d7.js → webpack-fa307370fcf9fb2c.js} +1 -1
- package/dist/web-mode.d.ts +2 -0
- package/dist/web-mode.js +29 -7
- package/package.json +1 -1
- package/packages/native/src/__tests__/text.test.mjs +33 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +3 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +10 -7
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +4 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +11 -7
- package/src/resources/extensions/gsd/auto/loop-deps.ts +5 -1
- package/src/resources/extensions/gsd/auto/loop.ts +10 -1
- package/src/resources/extensions/gsd/auto/phases.ts +28 -8
- package/src/resources/extensions/gsd/auto/types.ts +4 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +7 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +25 -5
- package/src/resources/extensions/gsd/auto-post-unit.ts +8 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +12 -4
- package/src/resources/extensions/gsd/auto-start.ts +8 -3
- package/src/resources/extensions/gsd/auto-worktree.ts +162 -18
- package/src/resources/extensions/gsd/auto.ts +40 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +209 -162
- package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +62 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +13 -0
- package/src/resources/extensions/gsd/commands/catalog.ts +8 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
- package/src/resources/extensions/gsd/context-store.ts +4 -3
- package/src/resources/extensions/gsd/db-writer.ts +6 -2
- package/src/resources/extensions/gsd/detection.ts +1 -1
- package/src/resources/extensions/gsd/doctor.ts +12 -1
- package/src/resources/extensions/gsd/exit-command.ts +14 -2
- package/src/resources/extensions/gsd/export.ts +8 -15
- package/src/resources/extensions/gsd/extension-manifest.json +2 -2
- package/src/resources/extensions/gsd/files.ts +29 -12
- package/src/resources/extensions/gsd/forensics.ts +9 -3
- package/src/resources/extensions/gsd/git-service.ts +5 -4
- package/src/resources/extensions/gsd/gsd-db.ts +37 -8
- package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
- package/src/resources/extensions/gsd/guided-flow.ts +7 -3
- package/src/resources/extensions/gsd/journal.ts +134 -0
- package/src/resources/extensions/gsd/md-importer.ts +6 -0
- package/src/resources/extensions/gsd/milestone-ids.ts +1 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +2 -2
- package/src/resources/extensions/gsd/post-unit-hooks.ts +24 -462
- package/src/resources/extensions/gsd/preferences-types.ts +3 -0
- package/src/resources/extensions/gsd/preferences.ts +1 -0
- package/src/resources/extensions/gsd/prompt-loader.ts +35 -4
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
- package/src/resources/extensions/gsd/prompts/discuss.md +1 -1
- package/src/resources/extensions/gsd/prompts/queue.md +1 -1
- package/src/resources/extensions/gsd/repo-identity.ts +47 -2
- package/src/resources/extensions/gsd/rule-registry.ts +599 -0
- package/src/resources/extensions/gsd/rule-types.ts +68 -0
- package/src/resources/extensions/gsd/service-tier.ts +171 -0
- package/src/resources/extensions/gsd/structured-data-formatter.ts +3 -1
- package/src/resources/extensions/gsd/templates/decisions.md +2 -2
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +202 -0
- package/src/resources/extensions/gsd/tests/context-store.test.ts +10 -5
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +15 -10
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +5 -4
- package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/doctor-task-done-missing-summary-slice-loop.test.ts +174 -0
- package/src/resources/extensions/gsd/tests/exit-command.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +8 -1
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +7 -7
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +513 -0
- package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +147 -0
- package/src/resources/extensions/gsd/tests/journal.test.ts +386 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +31 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/milestone-id-reservation.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/parsers.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -25
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +61 -1
- package/src/resources/extensions/gsd/tests/routing-history.test.ts +11 -22
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +413 -0
- package/src/resources/extensions/gsd/tests/service-tier.test.ts +98 -0
- package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +102 -0
- package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +117 -0
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +178 -0
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +78 -3
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +74 -0
- package/src/resources/extensions/gsd/types.ts +3 -0
- package/src/resources/extensions/gsd/workflow-templates.ts +12 -1
- package/src/resources/extensions/gsd/worktree-manager.ts +21 -6
- package/src/resources/extensions/gsd/worktree-resolver.ts +30 -9
- package/src/resources/extensions/subagent/index.ts +7 -3
- package/src/resources/extensions/voice/index.ts +4 -4
- package/dist/web/standalone/.next/static/chunks/4024.279c423e4661ece1.js +0 -9
- /package/dist/web/standalone/.next/static/{02cti5IXH7FycOqkbAkWL → 43Aw72Fdw8k1aAHQOYOXr}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{02cti5IXH7FycOqkbAkWL → 43Aw72Fdw8k1aAHQOYOXr}/_ssgManifest.js +0 -0
|
@@ -80,7 +80,7 @@ test("COMPLETION_TRANSITION_CODES only contains slice summary code", () => {
|
|
|
80
80
|
);
|
|
81
81
|
});
|
|
82
82
|
|
|
83
|
-
test("fixLevel:task — fixes
|
|
83
|
+
test("fixLevel:task — fixes UAT stub immediately, defers summary and roadmap checkbox (#1808, #1910)", async () => {
|
|
84
84
|
const tmp = makeTmp("partial-deferral");
|
|
85
85
|
try {
|
|
86
86
|
buildScaffold(tmp);
|
|
@@ -101,15 +101,16 @@ test("fixLevel:task — fixes roadmap checkbox and UAT stub immediately, defers
|
|
|
101
101
|
const sliceUatPath = join(tmp, ".gsd", "milestones", "M001", "slices", "S01", "S01-UAT.md");
|
|
102
102
|
assert.ok(existsSync(sliceUatPath), "should have created UAT stub immediately");
|
|
103
103
|
|
|
104
|
-
// Roadmap checkbox
|
|
104
|
+
// Roadmap checkbox must NOT be checked without summary on disk (#1910).
|
|
105
|
+
// Checking it without the summary causes deriveState() to skip complete-slice.
|
|
105
106
|
const roadmapContent = readFileSync(join(tmp, ".gsd", "milestones", "M001", "M001-ROADMAP.md"), "utf8");
|
|
106
|
-
assert.ok(roadmapContent.includes("- [
|
|
107
|
+
assert.ok(roadmapContent.includes("- [ ] **S01"), "roadmap must NOT be checked without summary on disk (#1910)");
|
|
107
108
|
} finally {
|
|
108
109
|
rmSync(tmp, { recursive: true, force: true });
|
|
109
110
|
}
|
|
110
111
|
});
|
|
111
112
|
|
|
112
|
-
test("fixLevel:task — session crash after last task leaves roadmap
|
|
113
|
+
test("fixLevel:task — session crash after last task leaves UAT consistent, roadmap deferred with summary (#1808, #1910)", async () => {
|
|
113
114
|
const tmp = makeTmp("crash-consistency");
|
|
114
115
|
try {
|
|
115
116
|
buildScaffold(tmp);
|
|
@@ -121,13 +122,7 @@ test("fixLevel:task — session crash after last task leaves roadmap and UAT con
|
|
|
121
122
|
// A new session starts and runs doctor again at task level.
|
|
122
123
|
const report2 = await runGSDDoctor(tmp, { fix: true, fixLevel: "task" });
|
|
123
124
|
|
|
124
|
-
// The only remaining issue should be the deferred summary.
|
|
125
|
-
// Roadmap and UAT should already be fixed from the first run.
|
|
126
125
|
const remainingCodes = report2.issues.map(i => i.code);
|
|
127
|
-
assert.ok(
|
|
128
|
-
!remainingCodes.includes("all_tasks_done_roadmap_not_checked"),
|
|
129
|
-
"roadmap should already be fixed from first doctor run"
|
|
130
|
-
);
|
|
131
126
|
assert.ok(
|
|
132
127
|
!remainingCodes.includes("all_tasks_done_missing_slice_uat"),
|
|
133
128
|
"UAT should already be fixed from first doctor run"
|
|
@@ -137,6 +132,16 @@ test("fixLevel:task — session crash after last task leaves roadmap and UAT con
|
|
|
137
132
|
remainingCodes.includes("all_tasks_done_missing_slice_summary"),
|
|
138
133
|
"summary should still be detected as missing (deferred)"
|
|
139
134
|
);
|
|
135
|
+
// Roadmap should still be unchecked because summary doesn't exist (#1910)
|
|
136
|
+
assert.ok(
|
|
137
|
+
remainingCodes.includes("all_tasks_done_roadmap_not_checked"),
|
|
138
|
+
"roadmap should still be unchecked — summary does not exist on disk (#1910)"
|
|
139
|
+
);
|
|
140
|
+
// Must NOT produce the cascade error from checking roadmap without summary
|
|
141
|
+
assert.ok(
|
|
142
|
+
!remainingCodes.includes("slice_checked_missing_summary"),
|
|
143
|
+
"must not produce slice_checked_missing_summary (#1910)"
|
|
144
|
+
);
|
|
140
145
|
} finally {
|
|
141
146
|
rmSync(tmp, { recursive: true, force: true });
|
|
142
147
|
}
|
|
@@ -63,7 +63,7 @@ Done.
|
|
|
63
63
|
`);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
test("fixLevel:task — defers
|
|
66
|
+
test("fixLevel:task — defers summary stub and roadmap checkbox, fixes UAT immediately (#1808, #1910)", async () => {
|
|
67
67
|
const tmp = makeTmp("task-level");
|
|
68
68
|
try {
|
|
69
69
|
buildScaffold(tmp);
|
|
@@ -79,13 +79,14 @@ test("fixLevel:task — defers only summary stub, fixes roadmap and UAT immediat
|
|
|
79
79
|
const sliceSummaryPath = join(tmp, ".gsd", "milestones", "M001", "slices", "S01", "S01-SUMMARY.md");
|
|
80
80
|
assert.ok(!existsSync(sliceSummaryPath), "should NOT have created summary stub");
|
|
81
81
|
|
|
82
|
-
// Roadmap
|
|
82
|
+
// Roadmap must NOT be checked without summary on disk (#1910)
|
|
83
83
|
const roadmapContent = readFileSync(join(tmp, ".gsd", "milestones", "M001", "M001-ROADMAP.md"), "utf8");
|
|
84
|
-
assert.ok(roadmapContent.includes("- [
|
|
84
|
+
assert.ok(roadmapContent.includes("- [ ] **S01"), "roadmap must NOT be checked without summary (#1910)");
|
|
85
85
|
|
|
86
|
-
// Fixes applied should NOT include summary
|
|
86
|
+
// Fixes applied should NOT include summary or roadmap
|
|
87
87
|
for (const f of report.fixesApplied) {
|
|
88
88
|
assert.ok(!f.includes("SUMMARY"), `should not have fixed summary: ${f}`);
|
|
89
|
+
assert.ok(!f.includes("ROADMAP") && !f.includes("roadmap"), `should not have fixed roadmap: ${f}`);
|
|
89
90
|
}
|
|
90
91
|
} finally {
|
|
91
92
|
rmSync(tmp, { recursive: true, force: true });
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test for #1910: Doctor marks roadmap checkbox at fixLevel="task"
|
|
3
|
+
* without summary on disk, causing deriveState() to skip complete-slice and
|
|
4
|
+
* hard-stop at validating-milestone.
|
|
5
|
+
*
|
|
6
|
+
* The roadmap checkbox must only be marked when the slice summary actually
|
|
7
|
+
* exists on disk (either pre-existing or created in the current doctor run).
|
|
8
|
+
* At fixLevel="task", the summary is deferred (COMPLETION_TRANSITION_CODES),
|
|
9
|
+
* so the roadmap checkbox must also be deferred.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { mkdirSync, writeFileSync, rmSync, readFileSync, existsSync } from "node:fs";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
|
+
import test from "node:test";
|
|
16
|
+
import assert from "node:assert/strict";
|
|
17
|
+
import { runGSDDoctor } from "../doctor.ts";
|
|
18
|
+
|
|
19
|
+
function makeTmp(name: string): string {
|
|
20
|
+
const dir = join(tmpdir(), `doctor-roadmap-summary-${name}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
21
|
+
mkdirSync(dir, { recursive: true });
|
|
22
|
+
return dir;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Build a minimal .gsd structure: milestone with one slice, one task
|
|
27
|
+
* marked done with a summary — but no slice summary and roadmap unchecked.
|
|
28
|
+
* This is the state after the last task completes.
|
|
29
|
+
*/
|
|
30
|
+
function buildScaffold(base: string) {
|
|
31
|
+
const gsd = join(base, ".gsd");
|
|
32
|
+
const m = join(gsd, "milestones", "M001");
|
|
33
|
+
const s = join(m, "slices", "S01", "tasks");
|
|
34
|
+
mkdirSync(s, { recursive: true });
|
|
35
|
+
|
|
36
|
+
writeFileSync(join(m, "M001-ROADMAP.md"), `# M001: Test
|
|
37
|
+
|
|
38
|
+
## Slices
|
|
39
|
+
|
|
40
|
+
- [ ] **S01: Test Slice** \`risk:low\` \`depends:[]\`
|
|
41
|
+
> Demo text
|
|
42
|
+
`);
|
|
43
|
+
|
|
44
|
+
writeFileSync(join(m, "slices", "S01", "S01-PLAN.md"), `# S01: Test Slice
|
|
45
|
+
|
|
46
|
+
**Goal:** test
|
|
47
|
+
|
|
48
|
+
## Tasks
|
|
49
|
+
|
|
50
|
+
- [x] **T01: Do stuff** \`est:5m\`
|
|
51
|
+
`);
|
|
52
|
+
|
|
53
|
+
writeFileSync(join(s, "T01-SUMMARY.md"), `---
|
|
54
|
+
id: T01
|
|
55
|
+
parent: S01
|
|
56
|
+
milestone: M001
|
|
57
|
+
duration: 5m
|
|
58
|
+
verification_result: passed
|
|
59
|
+
completed_at: 2026-01-01
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
# T01: Do stuff
|
|
63
|
+
|
|
64
|
+
Done.
|
|
65
|
+
`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
test("fixLevel:task — must NOT mark roadmap checkbox when summary does not exist on disk (#1910)", async () => {
|
|
69
|
+
const tmp = makeTmp("no-roadmap-without-summary");
|
|
70
|
+
try {
|
|
71
|
+
buildScaffold(tmp);
|
|
72
|
+
|
|
73
|
+
const report = await runGSDDoctor(tmp, { fix: true, fixLevel: "task" });
|
|
74
|
+
|
|
75
|
+
// Doctor should detect both issues
|
|
76
|
+
const codes = report.issues.map(i => i.code);
|
|
77
|
+
assert.ok(codes.includes("all_tasks_done_missing_slice_summary"), "should detect missing summary");
|
|
78
|
+
assert.ok(codes.includes("all_tasks_done_roadmap_not_checked"), "should detect unchecked roadmap");
|
|
79
|
+
|
|
80
|
+
// Summary should NOT exist (deferred at task level)
|
|
81
|
+
const sliceSummaryPath = join(tmp, ".gsd", "milestones", "M001", "slices", "S01", "S01-SUMMARY.md");
|
|
82
|
+
assert.ok(!existsSync(sliceSummaryPath), "summary should NOT be created (deferred)");
|
|
83
|
+
|
|
84
|
+
// CRITICAL: Roadmap checkbox must NOT be checked without summary on disk.
|
|
85
|
+
// If it is checked, deriveState() sees the milestone as complete and skips
|
|
86
|
+
// the summarizing phase, causing a hard-stop at validating-milestone.
|
|
87
|
+
const roadmapContent = readFileSync(join(tmp, ".gsd", "milestones", "M001", "M001-ROADMAP.md"), "utf8");
|
|
88
|
+
assert.ok(
|
|
89
|
+
roadmapContent.includes("- [ ] **S01"),
|
|
90
|
+
"roadmap must NOT mark S01 as checked when summary does not exist on disk"
|
|
91
|
+
);
|
|
92
|
+
} finally {
|
|
93
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("fixLevel:task — consecutive runs must not produce slice_checked_missing_summary (#1910)", async () => {
|
|
98
|
+
const tmp = makeTmp("no-cascade-error");
|
|
99
|
+
try {
|
|
100
|
+
buildScaffold(tmp);
|
|
101
|
+
|
|
102
|
+
// First doctor run at task level
|
|
103
|
+
await runGSDDoctor(tmp, { fix: true, fixLevel: "task" });
|
|
104
|
+
|
|
105
|
+
// Second doctor run — if the first run incorrectly checked the roadmap,
|
|
106
|
+
// this run would detect slice_checked_missing_summary (the cascade error
|
|
107
|
+
// described in the issue's forensic evidence).
|
|
108
|
+
const report2 = await runGSDDoctor(tmp, { fix: true, fixLevel: "task" });
|
|
109
|
+
const codes2 = report2.issues.map(i => i.code);
|
|
110
|
+
|
|
111
|
+
assert.ok(
|
|
112
|
+
!codes2.includes("slice_checked_missing_summary"),
|
|
113
|
+
"must not produce slice_checked_missing_summary — roadmap should not have been checked without summary"
|
|
114
|
+
);
|
|
115
|
+
} finally {
|
|
116
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("fixLevel:all — roadmap checkbox IS marked because summary is created in same run (#1910)", async () => {
|
|
121
|
+
const tmp = makeTmp("all-level-creates-both");
|
|
122
|
+
try {
|
|
123
|
+
buildScaffold(tmp);
|
|
124
|
+
|
|
125
|
+
const report = await runGSDDoctor(tmp, { fix: true });
|
|
126
|
+
|
|
127
|
+
// At fixLevel:all, summary stub is created first, then roadmap is checked.
|
|
128
|
+
// Both should be fixed.
|
|
129
|
+
const sliceSummaryPath = join(tmp, ".gsd", "milestones", "M001", "slices", "S01", "S01-SUMMARY.md");
|
|
130
|
+
assert.ok(existsSync(sliceSummaryPath), "summary should be created at fixLevel:all");
|
|
131
|
+
|
|
132
|
+
const roadmapContent = readFileSync(join(tmp, ".gsd", "milestones", "M001", "M001-ROADMAP.md"), "utf8");
|
|
133
|
+
assert.ok(roadmapContent.includes("- [x] **S01"), "roadmap should show S01 as checked at fixLevel:all");
|
|
134
|
+
} finally {
|
|
135
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("fixLevel:task — roadmap IS marked when summary already exists on disk (#1910)", async () => {
|
|
140
|
+
const tmp = makeTmp("summary-preexists");
|
|
141
|
+
try {
|
|
142
|
+
buildScaffold(tmp);
|
|
143
|
+
|
|
144
|
+
// Pre-create the slice summary (as if complete-slice already ran)
|
|
145
|
+
const sliceSummaryPath = join(tmp, ".gsd", "milestones", "M001", "slices", "S01", "S01-SUMMARY.md");
|
|
146
|
+
writeFileSync(sliceSummaryPath, `---
|
|
147
|
+
id: S01
|
|
148
|
+
milestone: M001
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
# S01: Test Slice
|
|
152
|
+
|
|
153
|
+
Summary content.
|
|
154
|
+
`);
|
|
155
|
+
|
|
156
|
+
const report = await runGSDDoctor(tmp, { fix: true, fixLevel: "task" });
|
|
157
|
+
|
|
158
|
+
// Summary exists, so roadmap SHOULD be checked even at task level
|
|
159
|
+
const roadmapContent = readFileSync(join(tmp, ".gsd", "milestones", "M001", "M001-ROADMAP.md"), "utf8");
|
|
160
|
+
assert.ok(
|
|
161
|
+
roadmapContent.includes("- [x] **S01"),
|
|
162
|
+
"roadmap should be checked when summary already exists on disk"
|
|
163
|
+
);
|
|
164
|
+
} finally {
|
|
165
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
166
|
+
}
|
|
167
|
+
});
|
package/src/resources/extensions/gsd/tests/doctor-task-done-missing-summary-slice-loop.test.ts
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test for #1850: doctor task_done_missing_summary fix leaves
|
|
3
|
+
* slice [x] done in roadmap, causing an infinite doctor loop.
|
|
4
|
+
*
|
|
5
|
+
* Scenario: A slice is [x] done in the roadmap, has S01-SUMMARY.md (so
|
|
6
|
+
* slice_checked_missing_summary never fires), but tasks are [x] done with
|
|
7
|
+
* no T##-SUMMARY.md files. Doctor unchecks the tasks but must also uncheck
|
|
8
|
+
* the slice so the state machine re-enters the executing phase.
|
|
9
|
+
*/
|
|
10
|
+
import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { tmpdir } from "node:os";
|
|
13
|
+
|
|
14
|
+
import { runGSDDoctor } from "../doctor.js";
|
|
15
|
+
import { createTestContext } from "./test-helpers.ts";
|
|
16
|
+
|
|
17
|
+
const { assertEq, assertTrue, report } = createTestContext();
|
|
18
|
+
|
|
19
|
+
async function main(): Promise<void> {
|
|
20
|
+
// ─── Setup: slice [x] done with S01-SUMMARY.md, tasks [x] but NO task summaries ───
|
|
21
|
+
console.log("\n=== #1850: task_done_missing_summary fix must also uncheck slice ===");
|
|
22
|
+
{
|
|
23
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-doctor-1850-"));
|
|
24
|
+
const gsd = join(base, ".gsd");
|
|
25
|
+
const mDir = join(gsd, "milestones", "M001");
|
|
26
|
+
const sDir = join(mDir, "slices", "S01");
|
|
27
|
+
const tDir = join(sDir, "tasks");
|
|
28
|
+
mkdirSync(tDir, { recursive: true });
|
|
29
|
+
|
|
30
|
+
// Roadmap: slice is [x] done
|
|
31
|
+
writeFileSync(join(mDir, "M001-ROADMAP.md"), `# M001: Test Milestone
|
|
32
|
+
|
|
33
|
+
## Slices
|
|
34
|
+
- [x] **S01: Guided Slice** \`risk:low\` \`depends:[]\`
|
|
35
|
+
> After this: guided flow works
|
|
36
|
+
`);
|
|
37
|
+
|
|
38
|
+
// Plan: tasks are [x] done
|
|
39
|
+
writeFileSync(join(sDir, "S01-PLAN.md"), `# S01: Guided Slice
|
|
40
|
+
|
|
41
|
+
**Goal:** Test guided flow
|
|
42
|
+
**Demo:** Works
|
|
43
|
+
|
|
44
|
+
## Tasks
|
|
45
|
+
- [x] **T01: First task** \`est:10m\`
|
|
46
|
+
Do the first thing.
|
|
47
|
+
- [x] **T02: Second task** \`est:10m\`
|
|
48
|
+
Do the second thing.
|
|
49
|
+
- [x] **T03: Third task** \`est:10m\`
|
|
50
|
+
Do the third thing.
|
|
51
|
+
`);
|
|
52
|
+
|
|
53
|
+
// Slice summary EXISTS (so slice_checked_missing_summary guard does NOT fire)
|
|
54
|
+
writeFileSync(join(sDir, "S01-SUMMARY.md"), `---
|
|
55
|
+
id: S01
|
|
56
|
+
parent: M001
|
|
57
|
+
---
|
|
58
|
+
# S01: Guided Slice
|
|
59
|
+
Done via guided flow.
|
|
60
|
+
`);
|
|
61
|
+
|
|
62
|
+
// Slice UAT exists
|
|
63
|
+
writeFileSync(join(sDir, "S01-UAT.md"), `# S01 UAT
|
|
64
|
+
Verified.
|
|
65
|
+
`);
|
|
66
|
+
|
|
67
|
+
// NO task summaries on disk — this is the trigger condition
|
|
68
|
+
|
|
69
|
+
// ── First pass: diagnose ──
|
|
70
|
+
const diagReport = await runGSDDoctor(base, { fix: false });
|
|
71
|
+
const taskDoneMissing = diagReport.issues.filter(i => i.code === "task_done_missing_summary");
|
|
72
|
+
assertEq(taskDoneMissing.length, 3, "detects 3 tasks with task_done_missing_summary");
|
|
73
|
+
|
|
74
|
+
// ── Second pass: fix ──
|
|
75
|
+
const fixReport = await runGSDDoctor(base, { fix: true });
|
|
76
|
+
|
|
77
|
+
// Tasks should be unchecked in plan
|
|
78
|
+
const plan = readFileSync(join(sDir, "S01-PLAN.md"), "utf-8");
|
|
79
|
+
assertTrue(plan.includes("- [ ] **T01:"), "T01 is unchecked in plan after fix");
|
|
80
|
+
assertTrue(plan.includes("- [ ] **T02:"), "T02 is unchecked in plan after fix");
|
|
81
|
+
assertTrue(plan.includes("- [ ] **T03:"), "T03 is unchecked in plan after fix");
|
|
82
|
+
|
|
83
|
+
// CRITICAL: Slice must also be unchecked in roadmap to prevent infinite loop
|
|
84
|
+
const roadmap = readFileSync(join(mDir, "M001-ROADMAP.md"), "utf-8");
|
|
85
|
+
assertTrue(
|
|
86
|
+
roadmap.includes("- [ ] **S01:"),
|
|
87
|
+
"slice is unchecked in roadmap after task_done_missing_summary fix (prevents infinite loop)"
|
|
88
|
+
);
|
|
89
|
+
assertTrue(
|
|
90
|
+
!roadmap.includes("- [x] **S01:"),
|
|
91
|
+
"slice is NOT still [x] done in roadmap"
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
// ── Third pass: re-run doctor should NOT re-detect task_done_missing_summary ──
|
|
95
|
+
const rerunReport = await runGSDDoctor(base, { fix: false });
|
|
96
|
+
const rerunTaskDone = rerunReport.issues.filter(i => i.code === "task_done_missing_summary");
|
|
97
|
+
assertEq(rerunTaskDone.length, 0, "no task_done_missing_summary on re-run (no infinite loop)");
|
|
98
|
+
|
|
99
|
+
rmSync(base, { recursive: true, force: true });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ─── Partial fix: only some tasks missing summaries ───
|
|
103
|
+
console.log("\n=== #1850: partial — some tasks have summaries, some do not ===");
|
|
104
|
+
{
|
|
105
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-doctor-1850-partial-"));
|
|
106
|
+
const gsd = join(base, ".gsd");
|
|
107
|
+
const mDir = join(gsd, "milestones", "M001");
|
|
108
|
+
const sDir = join(mDir, "slices", "S01");
|
|
109
|
+
const tDir = join(sDir, "tasks");
|
|
110
|
+
mkdirSync(tDir, { recursive: true });
|
|
111
|
+
|
|
112
|
+
writeFileSync(join(mDir, "M001-ROADMAP.md"), `# M001: Test Milestone
|
|
113
|
+
|
|
114
|
+
## Slices
|
|
115
|
+
- [x] **S01: Partial Slice** \`risk:low\` \`depends:[]\`
|
|
116
|
+
> After this: partial
|
|
117
|
+
`);
|
|
118
|
+
|
|
119
|
+
writeFileSync(join(sDir, "S01-PLAN.md"), `# S01: Partial Slice
|
|
120
|
+
|
|
121
|
+
**Goal:** Test partial
|
|
122
|
+
**Demo:** Works
|
|
123
|
+
|
|
124
|
+
## Tasks
|
|
125
|
+
- [x] **T01: Has summary** \`est:10m\`
|
|
126
|
+
This task has a summary.
|
|
127
|
+
- [x] **T02: Missing summary** \`est:10m\`
|
|
128
|
+
This task does not.
|
|
129
|
+
`);
|
|
130
|
+
|
|
131
|
+
// T01 has a summary, T02 does not
|
|
132
|
+
writeFileSync(join(tDir, "T01-SUMMARY.md"), `---
|
|
133
|
+
id: T01
|
|
134
|
+
parent: S01
|
|
135
|
+
milestone: M001
|
|
136
|
+
---
|
|
137
|
+
# T01: Has summary
|
|
138
|
+
**Done**
|
|
139
|
+
## What Happened
|
|
140
|
+
Done.
|
|
141
|
+
`);
|
|
142
|
+
|
|
143
|
+
writeFileSync(join(sDir, "S01-SUMMARY.md"), `---
|
|
144
|
+
id: S01
|
|
145
|
+
parent: M001
|
|
146
|
+
---
|
|
147
|
+
# S01: Partial
|
|
148
|
+
`);
|
|
149
|
+
|
|
150
|
+
writeFileSync(join(sDir, "S01-UAT.md"), `# S01 UAT
|
|
151
|
+
Done.
|
|
152
|
+
`);
|
|
153
|
+
|
|
154
|
+
const fixReport = await runGSDDoctor(base, { fix: true });
|
|
155
|
+
|
|
156
|
+
// T02 should be unchecked, T01 should stay checked
|
|
157
|
+
const plan = readFileSync(join(sDir, "S01-PLAN.md"), "utf-8");
|
|
158
|
+
assertTrue(plan.includes("- [x] **T01:"), "T01 stays checked (has summary)");
|
|
159
|
+
assertTrue(plan.includes("- [ ] **T02:"), "T02 is unchecked (missing summary)");
|
|
160
|
+
|
|
161
|
+
// Slice must be unchecked because not all tasks are done anymore
|
|
162
|
+
const roadmap = readFileSync(join(mDir, "M001-ROADMAP.md"), "utf-8");
|
|
163
|
+
assertTrue(
|
|
164
|
+
roadmap.includes("- [ ] **S01:"),
|
|
165
|
+
"slice is unchecked when any task is unchecked by task_done_missing_summary"
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
rmSync(base, { recursive: true, force: true });
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
report();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
main();
|
|
@@ -48,3 +48,58 @@ test("/exit requests graceful shutdown instead of process.exit", async () => {
|
|
|
48
48
|
assert.equal(stopAutoCalls, 1, "handler should stop auto-mode exactly once before shutdown");
|
|
49
49
|
assert.equal(shutdownCalls, 1, "handler should request graceful shutdown exactly once");
|
|
50
50
|
});
|
|
51
|
+
|
|
52
|
+
// ─── #1839 regression: ESM cache mismatch must not crash exit ────────────────
|
|
53
|
+
|
|
54
|
+
test("/exit still shuts down gracefully when stopAuto throws (ESM module cache mismatch)", async () => {
|
|
55
|
+
const commands = new Map<string, { description?: string; handler: (args: string, ctx: any) => Promise<void> }>();
|
|
56
|
+
|
|
57
|
+
const pi = {
|
|
58
|
+
registerCommand(name: string, options: any) {
|
|
59
|
+
commands.set(name, options);
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Simulate the ESM cache mismatch: stopAuto throws because a static import
|
|
64
|
+
// in the dependency chain references an export absent from the cached module.
|
|
65
|
+
registerExitCommand(pi as any, {
|
|
66
|
+
async stopAuto() {
|
|
67
|
+
throw new Error(
|
|
68
|
+
"The requested module './native-git-bridge.js' does not provide an export named 'nativeAddAllWithExclusions'",
|
|
69
|
+
);
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const exit = commands.get("exit")!;
|
|
74
|
+
|
|
75
|
+
let shutdownCalls = 0;
|
|
76
|
+
const notifications: Array<{ msg: string; level: string }> = [];
|
|
77
|
+
|
|
78
|
+
const originalExit = process.exit;
|
|
79
|
+
process.exit = ((code?: number) => {
|
|
80
|
+
throw new Error(`process.exit should not be called: ${code ?? "undefined"}`);
|
|
81
|
+
}) as typeof process.exit;
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
await exit.handler("", {
|
|
85
|
+
async shutdown() {
|
|
86
|
+
shutdownCalls += 1;
|
|
87
|
+
},
|
|
88
|
+
ui: {
|
|
89
|
+
notify(msg: string, level: string) {
|
|
90
|
+
notifications.push({ msg, level });
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
} finally {
|
|
95
|
+
process.exit = originalExit;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
assert.equal(shutdownCalls, 1, "shutdown must still be called even when stopAuto throws");
|
|
99
|
+
assert.equal(notifications.length, 1, "should emit exactly one warning notification");
|
|
100
|
+
assert.equal(notifications[0].level, "warning", "notification level should be warning");
|
|
101
|
+
assert.ok(
|
|
102
|
+
notifications[0].msg.includes("module version mismatch"),
|
|
103
|
+
"notification should mention module version mismatch",
|
|
104
|
+
);
|
|
105
|
+
});
|
|
@@ -66,7 +66,7 @@ console.log('\n=== gsd-db: fresh DB schema init (memory) ===');
|
|
|
66
66
|
// Check schema_version table
|
|
67
67
|
const adapter = _getAdapter()!;
|
|
68
68
|
const version = adapter.prepare('SELECT MAX(version) as version FROM schema_version').get();
|
|
69
|
-
assertEq(version?.['version'],
|
|
69
|
+
assertEq(version?.['version'], 4, 'schema version should be 4');
|
|
70
70
|
|
|
71
71
|
// Check tables exist by querying them
|
|
72
72
|
const dRows = adapter.prepare('SELECT count(*) as cnt FROM decisions').get();
|
|
@@ -93,6 +93,7 @@ console.log('\n=== gsd-db: double-init idempotency ===');
|
|
|
93
93
|
choice: 'option A',
|
|
94
94
|
rationale: 'because',
|
|
95
95
|
revisable: 'yes',
|
|
96
|
+
made_by: 'agent',
|
|
96
97
|
superseded_by: null,
|
|
97
98
|
});
|
|
98
99
|
|
|
@@ -123,6 +124,7 @@ console.log('\n=== gsd-db: insert + get decision ===');
|
|
|
123
124
|
choice: 'node:sqlite',
|
|
124
125
|
rationale: 'built-in, zero deps',
|
|
125
126
|
revisable: 'yes, if perf insufficient',
|
|
127
|
+
made_by: 'agent',
|
|
126
128
|
superseded_by: null,
|
|
127
129
|
});
|
|
128
130
|
|
|
@@ -186,6 +188,7 @@ console.log('\n=== gsd-db: active_decisions view excludes superseded ===');
|
|
|
186
188
|
choice: 'JSON',
|
|
187
189
|
rationale: 'simple',
|
|
188
190
|
revisable: 'yes',
|
|
191
|
+
made_by: 'agent',
|
|
189
192
|
superseded_by: 'D002', // superseded!
|
|
190
193
|
});
|
|
191
194
|
|
|
@@ -197,6 +200,7 @@ console.log('\n=== gsd-db: active_decisions view excludes superseded ===');
|
|
|
197
200
|
choice: 'SQLite',
|
|
198
201
|
rationale: 'better querying',
|
|
199
202
|
revisable: 'yes',
|
|
203
|
+
made_by: 'agent',
|
|
200
204
|
superseded_by: null, // active
|
|
201
205
|
});
|
|
202
206
|
|
|
@@ -208,6 +212,7 @@ console.log('\n=== gsd-db: active_decisions view excludes superseded ===');
|
|
|
208
212
|
choice: 'WAL',
|
|
209
213
|
rationale: 'concurrent reads',
|
|
210
214
|
revisable: 'no',
|
|
215
|
+
made_by: 'agent',
|
|
211
216
|
superseded_by: null, // active
|
|
212
217
|
});
|
|
213
218
|
|
|
@@ -294,6 +299,7 @@ console.log('\n=== gsd-db: transaction rollback on error ===');
|
|
|
294
299
|
choice: 'test',
|
|
295
300
|
rationale: 'test',
|
|
296
301
|
revisable: 'test',
|
|
302
|
+
made_by: 'agent',
|
|
297
303
|
superseded_by: null,
|
|
298
304
|
});
|
|
299
305
|
|
|
@@ -309,6 +315,7 @@ console.log('\n=== gsd-db: transaction rollback on error ===');
|
|
|
309
315
|
choice: 'test',
|
|
310
316
|
rationale: 'test',
|
|
311
317
|
revisable: 'test',
|
|
318
|
+
made_by: 'agent',
|
|
312
319
|
superseded_by: null,
|
|
313
320
|
});
|
|
314
321
|
throw new Error('intentional failure');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// gsd-tools — Structured LLM tool tests
|
|
2
2
|
//
|
|
3
|
-
// Tests the three registered tools:
|
|
3
|
+
// Tests the three registered tools: gsd_decision_save, gsd_requirement_update, gsd_summary_save.
|
|
4
4
|
// Each tool is tested via direct function invocation against an in-memory DB.
|
|
5
5
|
|
|
6
6
|
import { createTestContext } from './test-helpers.ts';
|
|
@@ -50,10 +50,10 @@ function cleanupDir(dir: string): void {
|
|
|
50
50
|
*/
|
|
51
51
|
|
|
52
52
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
53
|
-
//
|
|
53
|
+
// gsd_decision_save tool tests
|
|
54
54
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
55
55
|
|
|
56
|
-
console.log('\n──
|
|
56
|
+
console.log('\n── gsd_decision_save ──');
|
|
57
57
|
|
|
58
58
|
{
|
|
59
59
|
const tmpDir = makeTmpDir();
|
|
@@ -121,10 +121,10 @@ console.log('\n── gsd_save_decision ──');
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
124
|
-
//
|
|
124
|
+
// gsd_requirement_update tool tests
|
|
125
125
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
126
126
|
|
|
127
|
-
console.log('\n──
|
|
127
|
+
console.log('\n── gsd_requirement_update ──');
|
|
128
128
|
|
|
129
129
|
{
|
|
130
130
|
const tmpDir = makeTmpDir();
|
|
@@ -192,10 +192,10 @@ console.log('\n── gsd_update_requirement ──');
|
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
195
|
-
//
|
|
195
|
+
// gsd_summary_save tool tests
|
|
196
196
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
197
197
|
|
|
198
|
-
console.log('\n──
|
|
198
|
+
console.log('\n── gsd_summary_save ──');
|
|
199
199
|
|
|
200
200
|
{
|
|
201
201
|
const tmpDir = makeTmpDir();
|