gsd-pi 2.82.0-dev.2841a1e44 → 2.82.0-dev.98ea09b1e
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 +2 -2
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/GSD-WORKFLOW.md +7 -0
- package/dist/resources/extensions/claude-code-cli/partial-builder.js +2 -1
- package/dist/resources/extensions/gsd/auto/infra-errors.js +9 -3
- package/dist/resources/extensions/gsd/auto/loop.js +5 -5
- package/dist/resources/extensions/gsd/auto/orchestrator.js +11 -0
- package/dist/resources/extensions/gsd/auto/phases.js +8 -1
- package/dist/resources/extensions/gsd/auto/workflow-memory-pressure.js +12 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +13 -6
- package/dist/resources/extensions/gsd/auto-model-selection.js +2 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +70 -9
- package/dist/resources/extensions/gsd/auto-recovery.js +31 -1
- package/dist/resources/extensions/gsd/auto-start.js +85 -12
- package/dist/resources/extensions/gsd/auto-worktree.js +111 -1
- package/dist/resources/extensions/gsd/auto.js +30 -3
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +4 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +9 -8
- package/dist/resources/extensions/gsd/bootstrap/subagent-input.js +5 -2
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +13 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +17 -1
- package/dist/resources/extensions/gsd/crash-recovery.js +31 -5
- package/dist/resources/extensions/gsd/db/unit-dispatches.js +3 -2
- package/dist/resources/extensions/gsd/dispatch-guard.js +2 -2
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +28 -11
- package/dist/resources/extensions/gsd/doctor.js +2 -28
- package/dist/resources/extensions/gsd/export-html.js +27 -425
- package/dist/resources/extensions/gsd/git-service.js +39 -1
- package/dist/resources/extensions/gsd/gsd-db.js +1 -0
- package/dist/resources/extensions/gsd/guided-flow.js +13 -6
- package/dist/resources/extensions/gsd/migrate/parsers.js +10 -0
- package/dist/resources/extensions/gsd/migration-auto-check.js +12 -17
- package/dist/resources/extensions/gsd/milestone-actions.js +11 -4
- package/dist/resources/extensions/gsd/native-git-bridge.js +48 -12
- package/dist/resources/extensions/gsd/post-execution-checks.js +73 -2
- package/dist/resources/extensions/gsd/pre-execution-checks.js +28 -1
- package/dist/resources/extensions/gsd/prompt-loader.js +1 -1
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +8 -8
- package/dist/resources/extensions/gsd/prompts/discuss.md +9 -9
- package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +4 -4
- package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +3 -3
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -4
- package/dist/resources/extensions/gsd/prompts/queue.md +4 -4
- package/dist/resources/extensions/gsd/prompts/refine-slice.md +2 -2
- package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
- package/dist/resources/extensions/gsd/state-reconciliation/drift/merge-state.js +6 -1
- package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +9 -14
- package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +19 -24
- package/dist/resources/extensions/gsd/status-guards.js +4 -0
- package/dist/resources/extensions/gsd/templates/plan.md +8 -5
- package/dist/resources/extensions/gsd/templates/task-plan.md +4 -2
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +6 -8
- package/dist/resources/extensions/gsd/tools/complete-slice.js +6 -8
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +7 -1
- package/dist/resources/extensions/gsd/tools/plan-slice.js +89 -14
- package/dist/resources/extensions/gsd/unit-context-manifest.js +7 -8
- package/dist/resources/extensions/gsd/validation.js +23 -1
- package/dist/resources/extensions/gsd/verification-gate.js +68 -7
- package/dist/resources/extensions/gsd/workflow-projections.js +6 -8
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +33 -8
- package/dist/resources/extensions/shared/html-shell.js +388 -0
- package/dist/resources/extensions/visual-brief/page-contract.js +2 -0
- package/dist/resources/extensions/visual-brief/prompts.js +29 -0
- 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 +13 -13
- 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/server/app/_global-error/page_client-reference-manifest.js +1 -1
- 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/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -7
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -7
- 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 +4 -5
- 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 +2 -5
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -7
- 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 +4 -7
- 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 +4 -5
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -5
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
- package/dist/web/standalone/.next/server/chunks/4266.js +2 -0
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +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/dist/web/standalone/.next/static/chunks/app/layout-8c10ec293ae0f1d5.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-6a95bc41e0f7ec89.js → webpack-9a4db269f9ed63ad.js} +1 -1
- package/dist/web/standalone/.next/static/css/746ee28c929d1880.css +1 -0
- package/package.json +2 -2
- package/packages/mcp-server/src/workflow-tools.test.ts +1 -1
- package/packages/native/tsconfig.json +2 -1
- package/packages/native/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js +82 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/openai-codex-responses.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/openai-codex-responses.test.js +52 -0
- package/packages/pi-ai/dist/providers/openai-codex-responses.test.js.map +1 -0
- package/packages/pi-ai/dist/providers/simple-options.d.ts +2 -4
- package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.js +5 -6
- package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/simple-options.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/simple-options.test.js +50 -0
- package/packages/pi-ai/dist/providers/simple-options.test.js.map +1 -0
- package/packages/pi-ai/src/providers/openai-codex-responses.test.ts +63 -0
- package/packages/pi-ai/src/providers/openai-codex-responses.ts +91 -1
- package/packages/pi-ai/src/providers/simple-options.test.ts +60 -0
- package/packages/pi-ai/src/providers/simple-options.ts +5 -6
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.js +66 -0
- package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session.js +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session-thinking-level.test.ts +79 -0
- package/packages/pi-coding-agent/src/core/agent-session.ts +1 -1
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/src/resources/GSD-WORKFLOW.md +7 -0
- package/src/resources/extensions/claude-code-cli/partial-builder.ts +2 -1
- package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +19 -2
- package/src/resources/extensions/gsd/auto/contracts.ts +14 -6
- package/src/resources/extensions/gsd/auto/infra-errors.ts +9 -3
- package/src/resources/extensions/gsd/auto/loop.ts +8 -5
- package/src/resources/extensions/gsd/auto/orchestrator.ts +11 -0
- package/src/resources/extensions/gsd/auto/phases.ts +7 -1
- package/src/resources/extensions/gsd/auto/workflow-memory-pressure.ts +13 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +14 -6
- package/src/resources/extensions/gsd/auto-model-selection.ts +2 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +77 -7
- package/src/resources/extensions/gsd/auto-recovery.ts +29 -0
- package/src/resources/extensions/gsd/auto-start.ts +92 -9
- package/src/resources/extensions/gsd/auto-worktree.ts +119 -1
- package/src/resources/extensions/gsd/auto.ts +32 -3
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +6 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +9 -8
- package/src/resources/extensions/gsd/bootstrap/subagent-input.ts +3 -1
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +16 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +17 -1
- package/src/resources/extensions/gsd/crash-recovery.ts +30 -4
- package/src/resources/extensions/gsd/db/unit-dispatches.ts +4 -3
- package/src/resources/extensions/gsd/dispatch-guard.ts +2 -2
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +25 -13
- package/src/resources/extensions/gsd/doctor.ts +2 -27
- package/src/resources/extensions/gsd/export-html.ts +27 -427
- package/src/resources/extensions/gsd/git-service.ts +45 -1
- package/src/resources/extensions/gsd/gsd-db.ts +3 -0
- package/src/resources/extensions/gsd/guided-flow.ts +14 -7
- package/src/resources/extensions/gsd/migrate/parsers.ts +11 -0
- package/src/resources/extensions/gsd/migration-auto-check.ts +15 -23
- package/src/resources/extensions/gsd/milestone-actions.ts +10 -4
- package/src/resources/extensions/gsd/native-git-bridge.ts +54 -12
- package/src/resources/extensions/gsd/post-execution-checks.ts +87 -2
- package/src/resources/extensions/gsd/pre-execution-checks.ts +32 -1
- package/src/resources/extensions/gsd/prompt-loader.ts +1 -1
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +8 -8
- package/src/resources/extensions/gsd/prompts/discuss.md +9 -9
- package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +4 -4
- package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +3 -3
- package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -4
- package/src/resources/extensions/gsd/prompts/queue.md +4 -4
- package/src/resources/extensions/gsd/prompts/refine-slice.md +2 -2
- package/src/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
- package/src/resources/extensions/gsd/state-reconciliation/drift/merge-state.ts +8 -1
- package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +12 -15
- package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +17 -25
- package/src/resources/extensions/gsd/status-guards.ts +5 -0
- package/src/resources/extensions/gsd/templates/plan.md +8 -5
- package/src/resources/extensions/gsd/templates/task-plan.md +4 -2
- package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +116 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +80 -1
- package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/auto-post-unit-step-message.test.ts +12 -1
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +15 -1
- package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +69 -1
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +4 -1
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +5 -9
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +43 -2
- package/src/resources/extensions/gsd/tests/db-authority-regression.test.ts +208 -0
- package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +59 -2
- package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/export-html-enhancements.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/guided-discuss-project-prompt-rendering.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/guided-flow.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/hook-model-resolution.test.ts +5 -0
- package/src/resources/extensions/gsd/tests/infra-error.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/infra-errors-cooldown.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/integration/doctor-runtime.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +103 -1
- package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +24 -1
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +26 -18
- package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +63 -2
- package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +121 -1
- package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +55 -1
- package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +225 -1
- package/src/resources/extensions/gsd/tests/plan-task.test.ts +17 -0
- package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +86 -0
- package/src/resources/extensions/gsd/tests/post-unit-git-failure.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/prompt-loader.test.ts +23 -0
- package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +46 -2
- package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +31 -1
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +119 -23
- package/src/resources/extensions/gsd/tests/stuck-state-via-db.test.ts +64 -1
- package/src/resources/extensions/gsd/tests/summary-render-parity.test.ts +7 -3
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +65 -7
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +110 -1
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/workflow-memory-pressure.test.ts +21 -1
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-git-pathspec.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +64 -12
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +38 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +8 -10
- package/src/resources/extensions/gsd/tools/complete-slice.ts +6 -8
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +5 -1
- package/src/resources/extensions/gsd/tools/plan-slice.ts +98 -12
- package/src/resources/extensions/gsd/types.ts +1 -1
- package/src/resources/extensions/gsd/unit-context-manifest.ts +12 -9
- package/src/resources/extensions/gsd/validation.ts +23 -1
- package/src/resources/extensions/gsd/verification-gate.ts +78 -6
- package/src/resources/extensions/gsd/workflow-projections.ts +6 -8
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +41 -8
- package/src/resources/extensions/shared/html-shell.ts +412 -0
- package/src/resources/extensions/visual-brief/page-contract.ts +2 -0
- package/src/resources/extensions/visual-brief/prompts.ts +37 -1
- package/src/resources/extensions/visual-brief/tests/visual-brief.test.ts +40 -0
- package/dist/web/standalone/.next/server/chunks/5822.js +0 -2
- package/dist/web/standalone/.next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js +0 -1
- package/dist/web/standalone/.next/static/css/0262768ec1b89d34.css +0 -1
- package/dist/web/standalone/.next/static/css/de70bee13400563f.css +0 -1
- package/dist/web/standalone/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
- package/dist/web/standalone/.next/static/media/747892c23ea88013-s.woff2 +0 -0
- package/dist/web/standalone/.next/static/media/8d697b304b401681-s.woff2 +0 -0
- package/dist/web/standalone/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
- package/dist/web/standalone/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
- package/dist/web/standalone/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
- /package/dist/web/standalone/.next/static/{Qgr2B_MRhPxC0z8fwv4vT → euQ0CLP_v8V4e76Tu3odJ}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{Qgr2B_MRhPxC0z8fwv4vT → euQ0CLP_v8V4e76Tu3odJ}/_ssgManifest.js +0 -0
package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts
CHANGED
|
@@ -195,6 +195,11 @@ describe("infrastructure error detection", () => {
|
|
|
195
195
|
assert.equal(isInfrastructureError(err), "EAGAIN");
|
|
196
196
|
});
|
|
197
197
|
|
|
198
|
+
test("ENOBUFS (no buffer space available) is detected", () => {
|
|
199
|
+
const err = Object.assign(new Error("spawnSync git ENOBUFS"), { code: "ENOBUFS" });
|
|
200
|
+
assert.equal(isInfrastructureError(err), "ENOBUFS");
|
|
201
|
+
});
|
|
202
|
+
|
|
198
203
|
test("SQLite WAL corruption is detected via message scan", () => {
|
|
199
204
|
const err = new Error("database disk image is malformed");
|
|
200
205
|
assert.equal(isInfrastructureError(err), "SQLITE_CORRUPT");
|
|
@@ -223,7 +228,7 @@ describe("infrastructure error detection", () => {
|
|
|
223
228
|
test("all INFRA_ERROR_CODES are covered", () => {
|
|
224
229
|
const expectedCodes = [
|
|
225
230
|
"ENOSPC", "ENOMEM", "EROFS", "EDQUOT", "EMFILE",
|
|
226
|
-
"ENFILE", "EAGAIN", "ECONNREFUSED", "ENOTFOUND", "ENETUNREACH",
|
|
231
|
+
"ENFILE", "EAGAIN", "ENOBUFS", "ECONNREFUSED", "ENOTFOUND", "ENETUNREACH",
|
|
227
232
|
];
|
|
228
233
|
for (const code of expectedCodes) {
|
|
229
234
|
assert.ok(INFRA_ERROR_CODES.has(code), `${code} should be in INFRA_ERROR_CODES`);
|
|
@@ -41,6 +41,15 @@ const SAMPLE_ROADMAP = `# Project Roadmap
|
|
|
41
41
|
- [ ] 31 — Notifications
|
|
42
42
|
`;
|
|
43
43
|
|
|
44
|
+
const SAMPLE_VERSION_PREFIX_ROADMAP = `# Project Roadmap
|
|
45
|
+
|
|
46
|
+
## Phases
|
|
47
|
+
|
|
48
|
+
- ✅ **v1.0 MVP** — Phases 1-6 (shipped 2026-02-24)
|
|
49
|
+
- ✅ **v1.1 Onboarding** — Phases 7-9 (shipped 2026-03-01)
|
|
50
|
+
- 🚧 **v1.8 Production** — Phases 44-53
|
|
51
|
+
`;
|
|
52
|
+
|
|
44
53
|
const SAMPLE_PROJECT = `# My Project
|
|
45
54
|
|
|
46
55
|
A sample project for testing the migration parser.
|
|
@@ -246,6 +255,21 @@ test('parseOldRoadmap: flat format', () => {
|
|
|
246
255
|
assert.deepStrictEqual(roadmap.phases[1].done, false, 'flat roadmap: second phase not done');
|
|
247
256
|
});
|
|
248
257
|
|
|
258
|
+
test('parseOldRoadmap: emoji version-prefix phase ranges', () => {
|
|
259
|
+
const roadmap = parseOldRoadmap(SAMPLE_VERSION_PREFIX_ROADMAP);
|
|
260
|
+
assert.deepStrictEqual(roadmap.milestones.length, 0, 'version roadmap: no milestone sections');
|
|
261
|
+
assert.deepStrictEqual(roadmap.phases.length, 3, 'version roadmap: 3 phase ranges');
|
|
262
|
+
assert.deepStrictEqual(roadmap.phases[0].number, 1, 'version roadmap: first range starts at phase 1');
|
|
263
|
+
assert.deepStrictEqual(roadmap.phases[0].title, 'MVP', 'version roadmap: first title');
|
|
264
|
+
assert.deepStrictEqual(roadmap.phases[0].done, true, 'version roadmap: first range done');
|
|
265
|
+
assert.deepStrictEqual(roadmap.phases[1].number, 7, 'version roadmap: second range starts at phase 7');
|
|
266
|
+
assert.deepStrictEqual(roadmap.phases[1].title, 'Onboarding', 'version roadmap: second title');
|
|
267
|
+
assert.deepStrictEqual(roadmap.phases[1].done, true, 'version roadmap: second range done');
|
|
268
|
+
assert.deepStrictEqual(roadmap.phases[2].number, 44, 'version roadmap: third range starts at phase 44');
|
|
269
|
+
assert.deepStrictEqual(roadmap.phases[2].title, 'Production', 'version roadmap: third title');
|
|
270
|
+
assert.deepStrictEqual(roadmap.phases[2].done, false, 'version roadmap: third range in progress');
|
|
271
|
+
});
|
|
272
|
+
|
|
249
273
|
test('parseOldRoadmap: milestone-sectioned with <details>', () => {
|
|
250
274
|
const roadmap = parseOldRoadmap(SAMPLE_MILESTONE_SECTIONED_ROADMAP);
|
|
251
275
|
assert.ok(roadmap.milestones.length >= 2, 'ms roadmap: has milestone sections');
|
|
@@ -387,4 +411,3 @@ test('parseOldProject', () => {
|
|
|
387
411
|
const project = parseOldProject(SAMPLE_PROJECT);
|
|
388
412
|
assert.deepStrictEqual(project, SAMPLE_PROJECT, 'project: returns raw content');
|
|
389
413
|
});
|
|
390
|
-
|
|
@@ -6,13 +6,15 @@ import test from "node:test";
|
|
|
6
6
|
|
|
7
7
|
import { ensureDbOpen } from "../bootstrap/dynamic-tools.ts";
|
|
8
8
|
import {
|
|
9
|
-
_getAdapter,
|
|
10
9
|
closeDatabase,
|
|
11
10
|
getAllMilestones,
|
|
11
|
+
insertMilestone,
|
|
12
|
+
insertSlice,
|
|
13
|
+
insertTask,
|
|
12
14
|
getSliceTasks,
|
|
13
15
|
} from "../gsd-db.ts";
|
|
14
16
|
import {
|
|
15
|
-
|
|
17
|
+
checkMarkdownHierarchyAgainstDb,
|
|
16
18
|
countMarkdownHierarchy,
|
|
17
19
|
} from "../migration-auto-check.ts";
|
|
18
20
|
import { writeGSDDirectory } from "../migrate/writer.ts";
|
|
@@ -70,7 +72,7 @@ function projectFixture(): GSDProject {
|
|
|
70
72
|
};
|
|
71
73
|
}
|
|
72
74
|
|
|
73
|
-
test("migration auto-check
|
|
75
|
+
test("migration auto-check preserves empty DB and reports explicit recovery", async () => {
|
|
74
76
|
const base = makeBase();
|
|
75
77
|
try {
|
|
76
78
|
await writeGSDDirectory(projectFixture(), base);
|
|
@@ -79,32 +81,35 @@ test("migration auto-check imports markdown hierarchy when DB is empty", async (
|
|
|
79
81
|
assert.equal(await ensureDbOpen(base), true);
|
|
80
82
|
assert.equal(getAllMilestones().length, 0, "fresh authoritative DB starts empty");
|
|
81
83
|
|
|
82
|
-
const result = await
|
|
83
|
-
assert.equal(result.action, "
|
|
84
|
+
const result = await checkMarkdownHierarchyAgainstDb(base);
|
|
85
|
+
assert.equal(result.action, "recovery-required");
|
|
84
86
|
assert.equal(result.reason, "db-empty");
|
|
85
|
-
assert.deepEqual(result.afterDb, { milestones:
|
|
86
|
-
assert.equal(
|
|
87
|
-
assert.
|
|
87
|
+
assert.deepEqual(result.afterDb, { milestones: 0, slices: 0, tasks: 0 });
|
|
88
|
+
assert.equal(result.recoveryCommand, "gsd recover");
|
|
89
|
+
assert.match(result.message ?? "", /will not import markdown automatically/);
|
|
90
|
+
assert.equal(getAllMilestones().length, 0);
|
|
91
|
+
assert.equal(getSliceTasks("M001", "S01").length, 0);
|
|
88
92
|
} finally {
|
|
89
93
|
cleanup(base);
|
|
90
94
|
}
|
|
91
95
|
});
|
|
92
96
|
|
|
93
|
-
test("migration auto-check
|
|
97
|
+
test("migration auto-check preserves DB on hierarchy count mismatch", async () => {
|
|
94
98
|
const base = makeBase();
|
|
95
99
|
try {
|
|
96
100
|
await writeGSDDirectory(projectFixture(), base);
|
|
97
|
-
await
|
|
98
|
-
|
|
99
|
-
|
|
101
|
+
assert.equal(await ensureDbOpen(base), true);
|
|
102
|
+
insertMilestone({ id: "M001", title: "Legacy Milestone", status: "active" });
|
|
103
|
+
insertSlice({ id: "S01", milestoneId: "M001", title: "Legacy Slice", status: "pending", risk: "medium", depends: [], demo: "Legacy slice demo", sequence: 1 });
|
|
100
104
|
assert.equal(getSliceTasks("M001", "S01").length, 0, "test fixture simulates stale DB task count");
|
|
101
105
|
|
|
102
|
-
const result = await
|
|
103
|
-
assert.equal(result.action, "
|
|
106
|
+
const result = await checkMarkdownHierarchyAgainstDb(base);
|
|
107
|
+
assert.equal(result.action, "recovery-required");
|
|
104
108
|
assert.equal(result.reason, "count-mismatch");
|
|
105
109
|
assert.deepEqual(result.beforeDb, { milestones: 1, slices: 1, tasks: 0 });
|
|
106
|
-
assert.deepEqual(result.afterDb, { milestones: 1, slices: 1, tasks:
|
|
107
|
-
assert.equal(
|
|
110
|
+
assert.deepEqual(result.afterDb, { milestones: 1, slices: 1, tasks: 0 });
|
|
111
|
+
assert.equal(result.recoveryCommand, "gsd recover");
|
|
112
|
+
assert.equal(getSliceTasks("M001", "S01").length, 0);
|
|
108
113
|
} finally {
|
|
109
114
|
cleanup(base);
|
|
110
115
|
}
|
|
@@ -114,9 +119,12 @@ test("migration auto-check leaves matching DB hierarchy alone", async () => {
|
|
|
114
119
|
const base = makeBase();
|
|
115
120
|
try {
|
|
116
121
|
await writeGSDDirectory(projectFixture(), base);
|
|
117
|
-
await
|
|
122
|
+
assert.equal(await ensureDbOpen(base), true);
|
|
123
|
+
insertMilestone({ id: "M001", title: "Legacy Milestone", status: "active" });
|
|
124
|
+
insertSlice({ id: "S01", milestoneId: "M001", title: "Legacy Slice", status: "pending", risk: "medium", depends: [], demo: "Legacy slice demo", sequence: 1 });
|
|
125
|
+
insertTask({ id: "T01", sliceId: "S01", milestoneId: "M001", title: "Legacy Task", status: "pending" });
|
|
118
126
|
|
|
119
|
-
const result = await
|
|
127
|
+
const result = await checkMarkdownHierarchyAgainstDb(base);
|
|
120
128
|
assert.equal(result.action, "none");
|
|
121
129
|
assert.equal(result.reason, "in-sync");
|
|
122
130
|
assert.deepEqual(result.markdown, { milestones: 1, slices: 1, tasks: 1 });
|
|
@@ -13,17 +13,19 @@
|
|
|
13
13
|
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
14
14
|
import assert from "node:assert/strict";
|
|
15
15
|
import { chmodSync, existsSync, mkdtempSync, writeFileSync, readFileSync, rmSync } from "node:fs";
|
|
16
|
-
import { join } from "node:path";
|
|
16
|
+
import { delimiter, join } from "node:path";
|
|
17
17
|
import { tmpdir } from "node:os";
|
|
18
18
|
import { execFileSync } from "node:child_process";
|
|
19
19
|
import {
|
|
20
20
|
assertWorktreeMaterialized,
|
|
21
21
|
nativeBranchDelete,
|
|
22
22
|
nativeCommit,
|
|
23
|
+
nativeGetCurrentBranch,
|
|
23
24
|
nativeIsRepo,
|
|
24
25
|
nativeResetHard,
|
|
25
26
|
nativeWorktreeAdd,
|
|
26
27
|
} from "../native-git-bridge.js";
|
|
28
|
+
import { GIT_NO_PROMPT_ENV } from "../git-constants.js";
|
|
27
29
|
|
|
28
30
|
// Note: prior static-analysis tests that scanned native-git-bridge.ts for
|
|
29
31
|
// the raw shell-spawn pattern were removed under #4827 — the integration
|
|
@@ -78,6 +80,55 @@ describe("native-git-bridge #4180: fallback runtime behaviour", () => {
|
|
|
78
80
|
assert.equal(subject, "test: regression commit #4180");
|
|
79
81
|
});
|
|
80
82
|
|
|
83
|
+
test("nativeCommit retries once after transient ENOBUFS from git", (t) => {
|
|
84
|
+
const bin = mkdtempSync(join(tmpdir(), "ngb-enobufs-bin-"));
|
|
85
|
+
t.after(() => rmSync(bin, { recursive: true, force: true }));
|
|
86
|
+
|
|
87
|
+
const realGit = execFileSync("git", ["--exec-path"], { encoding: "utf-8" }).trim();
|
|
88
|
+
const attempts = join(bin, "attempts.txt");
|
|
89
|
+
const fakeGit = join(bin, "fake-git.cjs");
|
|
90
|
+
writeFileSync(fakeGit, `
|
|
91
|
+
const { appendFileSync, readFileSync } = require("node:fs");
|
|
92
|
+
const { spawnSync } = require("node:child_process");
|
|
93
|
+
const attempts = ${JSON.stringify(attempts)};
|
|
94
|
+
const realGit = ${JSON.stringify(join(realGit, process.platform === "win32" ? "git.exe" : "git"))};
|
|
95
|
+
appendFileSync(attempts, "1");
|
|
96
|
+
if (process.argv[2] === "commit" && readFileSync(attempts, "utf-8").length === 1) {
|
|
97
|
+
console.error("spawnSync git ENOBUFS");
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
const result = spawnSync(realGit, process.argv.slice(2), { stdio: "inherit" });
|
|
101
|
+
process.exit(result.status ?? 1);
|
|
102
|
+
`, "utf-8");
|
|
103
|
+
|
|
104
|
+
if (process.platform === "win32") {
|
|
105
|
+
writeFileSync(join(bin, "git.cmd"), `@echo off\r\nnode "${fakeGit}" %*\r\n`, "utf-8");
|
|
106
|
+
} else {
|
|
107
|
+
const shim = join(bin, "git");
|
|
108
|
+
writeFileSync(shim, `#!/bin/sh\nexec node "${fakeGit}" "$@"\n`, "utf-8");
|
|
109
|
+
chmodSync(shim, 0o755);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
writeFileSync(join(repo, "file.txt"), "retry commit\n");
|
|
113
|
+
git(["add", "."], repo);
|
|
114
|
+
|
|
115
|
+
const originalPath = process.env.PATH ?? "";
|
|
116
|
+
const gitEnv = GIT_NO_PROMPT_ENV as NodeJS.ProcessEnv;
|
|
117
|
+
const originalGitEnvPath = gitEnv.PATH;
|
|
118
|
+
try {
|
|
119
|
+
process.env.PATH = `${bin}${delimiter}${originalPath}`;
|
|
120
|
+
gitEnv.PATH = process.env.PATH;
|
|
121
|
+
const result = nativeCommit(repo, "test: retry ENOBUFS commit");
|
|
122
|
+
assert.ok(result !== null, "commit should succeed after retry");
|
|
123
|
+
} finally {
|
|
124
|
+
process.env.PATH = originalPath;
|
|
125
|
+
gitEnv.PATH = originalGitEnvPath;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
assert.equal(readFileSync(attempts, "utf-8").length, 2);
|
|
129
|
+
assert.equal(git(["log", "-1", "--format=%s"], repo), "test: retry ENOBUFS commit");
|
|
130
|
+
});
|
|
131
|
+
|
|
81
132
|
test("nativeCommit runs commit hooks", () => {
|
|
82
133
|
const hookPath = join(repo, ".git", "hooks", "commit-msg");
|
|
83
134
|
const marker = join(repo, "hook-ran.txt");
|
|
@@ -119,7 +170,17 @@ describe("native-git-bridge #4180: fallback runtime behaviour", () => {
|
|
|
119
170
|
test("nativeBranchDelete throws when git cannot delete the branch", () => {
|
|
120
171
|
assert.throws(
|
|
121
172
|
() => nativeBranchDelete(repo, "does-not-exist"),
|
|
122
|
-
/
|
|
173
|
+
/git branch -D does-not-exist failed[\s\S]*does-not-exist/,
|
|
174
|
+
);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("nativeGetCurrentBranch preserves git stderr in fallback errors", (t) => {
|
|
178
|
+
const dir = mkdtempSync(join(tmpdir(), "ngb-stderr-notrepo-"));
|
|
179
|
+
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
|
180
|
+
|
|
181
|
+
assert.throws(
|
|
182
|
+
() => nativeGetCurrentBranch(dir),
|
|
183
|
+
/git branch --show-current failed[\s\S]*(not a git repository|not a git repo|fatal:)/,
|
|
123
184
|
);
|
|
124
185
|
});
|
|
125
186
|
|
|
@@ -243,7 +243,7 @@ describe("auditOrphanedMilestoneBranches", () => {
|
|
|
243
243
|
);
|
|
244
244
|
});
|
|
245
245
|
|
|
246
|
-
test("
|
|
246
|
+
test("milestone in DB, no branch, no worktree dir → no-op", () => {
|
|
247
247
|
insertMilestone({ id: "M001", title: "Test", status: "complete" });
|
|
248
248
|
|
|
249
249
|
const result = auditOrphanedMilestoneBranches(dir, "worktree");
|
|
@@ -251,4 +251,124 @@ describe("auditOrphanedMilestoneBranches", () => {
|
|
|
251
251
|
assert.deepStrictEqual(result.recovered, []);
|
|
252
252
|
assert.deepStrictEqual(result.warnings, []);
|
|
253
253
|
});
|
|
254
|
+
|
|
255
|
+
test("#5879 — cleans orphaned worktree dir for complete milestone whose branch was already deleted", () => {
|
|
256
|
+
// Reproduces the postflight-stash-restore-failed scenario:
|
|
257
|
+
// 1. An earlier audit deleted milestone/M001 (merged + complete).
|
|
258
|
+
// 2. The worktree dir cleanup failed silently (logWarning only).
|
|
259
|
+
// 3. On the next startup the branch is gone, so the existing branch-keyed
|
|
260
|
+
// loop is invisible to the orphan dir. Without the second pass, the
|
|
261
|
+
// directory lives forever.
|
|
262
|
+
insertMilestone({ id: "M001", title: "Test", status: "complete" });
|
|
263
|
+
|
|
264
|
+
const wtDir = join(dir, ".gsd", "worktrees", "M001");
|
|
265
|
+
mkdirSync(wtDir, { recursive: true });
|
|
266
|
+
writeFileSync(join(wtDir, "leftover.txt"), "stranded from a prior session\n");
|
|
267
|
+
|
|
268
|
+
// No milestone/M001 branch — already deleted on a previous run.
|
|
269
|
+
const branches = run("git branch --list milestone/M001", dir);
|
|
270
|
+
assert.equal(branches, "", "test fixture: branch should not exist");
|
|
271
|
+
|
|
272
|
+
const result = auditOrphanedMilestoneBranches(dir, "worktree");
|
|
273
|
+
|
|
274
|
+
assert.ok(
|
|
275
|
+
result.recovered.some((r) => r.includes("M001") && r.includes("branch already deleted")),
|
|
276
|
+
`should report branch-less orphan cleanup; got: ${JSON.stringify(result.recovered)}`,
|
|
277
|
+
);
|
|
278
|
+
assert.ok(!existsSync(wtDir), "branch-less orphan worktree dir should be removed");
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
test("#5879 — branch list failure still cleans complete orphan only after branch absence is verified", () => {
|
|
282
|
+
insertMilestone({ id: "M001", title: "Test", status: "complete" });
|
|
283
|
+
|
|
284
|
+
const wtDir = join(dir, ".gsd", "worktrees", "M001");
|
|
285
|
+
mkdirSync(wtDir, { recursive: true });
|
|
286
|
+
writeFileSync(join(wtDir, "leftover.txt"), "stranded from a prior session\n");
|
|
287
|
+
|
|
288
|
+
const result = auditOrphanedMilestoneBranches(dir, "worktree", {
|
|
289
|
+
branchList: () => {
|
|
290
|
+
throw new Error("branch list failed");
|
|
291
|
+
},
|
|
292
|
+
branchExists: (_basePath, branch) => {
|
|
293
|
+
assert.equal(branch, "milestone/M001");
|
|
294
|
+
return false;
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
assert.ok(
|
|
299
|
+
result.recovered.some((r) => r.includes("M001") && r.includes("branch already deleted")),
|
|
300
|
+
`should report verified branch-less orphan cleanup; got: ${JSON.stringify(result.recovered)}`,
|
|
301
|
+
);
|
|
302
|
+
assert.ok(!existsSync(wtDir), "verified branch-less orphan worktree dir should be removed");
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
test("#5879 — branch list failure preserves complete worktree when branch still exists", () => {
|
|
306
|
+
insertMilestone({ id: "M001", title: "Test", status: "complete" });
|
|
307
|
+
|
|
308
|
+
const wtDir = join(dir, ".gsd", "worktrees", "M001");
|
|
309
|
+
mkdirSync(wtDir, { recursive: true });
|
|
310
|
+
writeFileSync(join(wtDir, "live-work.txt"), "do not delete\n");
|
|
311
|
+
|
|
312
|
+
const result = auditOrphanedMilestoneBranches(dir, "worktree", {
|
|
313
|
+
branchList: () => {
|
|
314
|
+
throw new Error("branch list failed");
|
|
315
|
+
},
|
|
316
|
+
branchExists: (_basePath, branch) => {
|
|
317
|
+
assert.equal(branch, "milestone/M001");
|
|
318
|
+
return true;
|
|
319
|
+
},
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
assert.deepStrictEqual(result.recovered, []);
|
|
323
|
+
assert.ok(existsSync(wtDir), "worktree dir must be preserved when branch existence is verified");
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
test("#5879 — skips branch-less orphan for milestone that is not complete", () => {
|
|
327
|
+
// Defensive: only `complete` milestones get the branch-less cleanup. An
|
|
328
|
+
// `active` milestone with no branch but a worktree dir is a different
|
|
329
|
+
// state (probably mid-recovery) and should not be silently wiped.
|
|
330
|
+
insertMilestone({ id: "M001", title: "Test", status: "active" });
|
|
331
|
+
|
|
332
|
+
const wtDir = join(dir, ".gsd", "worktrees", "M001");
|
|
333
|
+
mkdirSync(wtDir, { recursive: true });
|
|
334
|
+
writeFileSync(join(wtDir, "live-work.txt"), "do not delete\n");
|
|
335
|
+
|
|
336
|
+
const result = auditOrphanedMilestoneBranches(dir, "worktree");
|
|
337
|
+
|
|
338
|
+
assert.deepStrictEqual(result.recovered, []);
|
|
339
|
+
assert.ok(existsSync(wtDir), "active milestone worktree dir must be preserved");
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
test("#5879 — branch list failure does not delete worktree for milestone that is not complete", () => {
|
|
343
|
+
insertMilestone({ id: "M001", title: "Test", status: "active" });
|
|
344
|
+
|
|
345
|
+
const wtDir = join(dir, ".gsd", "worktrees", "M001");
|
|
346
|
+
mkdirSync(wtDir, { recursive: true });
|
|
347
|
+
writeFileSync(join(wtDir, "live-work.txt"), "do not delete\n");
|
|
348
|
+
|
|
349
|
+
const result = auditOrphanedMilestoneBranches(dir, "worktree", {
|
|
350
|
+
branchList: () => {
|
|
351
|
+
throw new Error("branch list failed");
|
|
352
|
+
},
|
|
353
|
+
branchExists: () => {
|
|
354
|
+
throw new Error("branchExists should not be called for active milestones");
|
|
355
|
+
},
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
assert.deepStrictEqual(result.recovered, []);
|
|
359
|
+
assert.ok(existsSync(wtDir), "active milestone worktree dir must be preserved");
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
test("#5879 — skips branch-less orphan in 'none' isolation mode", () => {
|
|
363
|
+
insertMilestone({ id: "M001", title: "Test", status: "complete" });
|
|
364
|
+
|
|
365
|
+
const wtDir = join(dir, ".gsd", "worktrees", "M001");
|
|
366
|
+
mkdirSync(wtDir, { recursive: true });
|
|
367
|
+
writeFileSync(join(wtDir, "leftover.txt"), "stranded\n");
|
|
368
|
+
|
|
369
|
+
const result = auditOrphanedMilestoneBranches(dir, "none");
|
|
370
|
+
|
|
371
|
+
assert.deepStrictEqual(result.recovered, []);
|
|
372
|
+
assert.ok(existsSync(wtDir), "'none' mode must not touch worktree dirs");
|
|
373
|
+
});
|
|
254
374
|
});
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { test } from "node:test";
|
|
9
9
|
import assert from "node:assert/strict";
|
|
10
|
-
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
10
|
+
import { existsSync, mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
import { tmpdir } from "node:os";
|
|
13
13
|
|
|
@@ -48,6 +48,60 @@ test("parkMilestone updates DB status to 'parked' (#2694)", () => {
|
|
|
48
48
|
}
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
+
test("parkMilestone ignores blocked SUMMARY.md when DB milestone is active (#5828)", () => {
|
|
52
|
+
const base = createBase();
|
|
53
|
+
try {
|
|
54
|
+
openDatabase(":memory:");
|
|
55
|
+
insertMilestone({ id: "M001", title: "Test", status: "active" });
|
|
56
|
+
writeFileSync(
|
|
57
|
+
join(base, ".gsd", "milestones", "M001", "M001-SUMMARY.md"),
|
|
58
|
+
[
|
|
59
|
+
"---",
|
|
60
|
+
"status: closeout_blocked",
|
|
61
|
+
"---",
|
|
62
|
+
"",
|
|
63
|
+
"# M001 Summary",
|
|
64
|
+
"",
|
|
65
|
+
"Completion was not persisted.",
|
|
66
|
+
].join("\n"),
|
|
67
|
+
"utf-8",
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const parked = parkMilestone(base, "M001", "test");
|
|
71
|
+
|
|
72
|
+
assert.ok(parked, "active DB row should allow parking despite a blocked SUMMARY.md");
|
|
73
|
+
assert.ok(
|
|
74
|
+
existsSync(join(base, ".gsd", "milestones", "M001", "M001-PARKED.md")),
|
|
75
|
+
"PARKED.md should be written",
|
|
76
|
+
);
|
|
77
|
+
assert.equal(getMilestone("M001")!.status, "parked", "DB status should be parked");
|
|
78
|
+
} finally {
|
|
79
|
+
closeDatabase();
|
|
80
|
+
rmSync(base, { recursive: true, force: true });
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("parkMilestone refuses DB-complete milestones (#5828)", () => {
|
|
85
|
+
const base = createBase();
|
|
86
|
+
try {
|
|
87
|
+
openDatabase(":memory:");
|
|
88
|
+
insertMilestone({ id: "M001", title: "Test", status: "complete" });
|
|
89
|
+
|
|
90
|
+
const parked = parkMilestone(base, "M001", "test");
|
|
91
|
+
|
|
92
|
+
assert.equal(parked, false, "complete DB row should not be parkable");
|
|
93
|
+
assert.equal(
|
|
94
|
+
existsSync(join(base, ".gsd", "milestones", "M001", "M001-PARKED.md")),
|
|
95
|
+
false,
|
|
96
|
+
"PARKED.md should not be written",
|
|
97
|
+
);
|
|
98
|
+
assert.equal(getMilestone("M001")!.status, "complete", "DB status should remain complete");
|
|
99
|
+
} finally {
|
|
100
|
+
closeDatabase();
|
|
101
|
+
rmSync(base, { recursive: true, force: true });
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
51
105
|
test("unparkMilestone updates DB status to 'active' (#2694)", () => {
|
|
52
106
|
const base = createBase();
|
|
53
107
|
try {
|
|
@@ -116,6 +116,32 @@ test('handlePlanMilestone rejects invalid payloads', async () => {
|
|
|
116
116
|
}
|
|
117
117
|
});
|
|
118
118
|
|
|
119
|
+
test('handlePlanMilestone rejects delimiter characters in milestone and slice titles', async () => {
|
|
120
|
+
const base = makeTmpBase();
|
|
121
|
+
const dbPath = join(base, '.gsd', 'gsd.db');
|
|
122
|
+
openDatabase(dbPath);
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const milestoneResult = await handlePlanMilestone({ ...validParams(), title: 'Client/Server split' }, base);
|
|
126
|
+
assert.ok('error' in milestoneResult);
|
|
127
|
+
assert.match(milestoneResult.error, /validation failed: title is invalid: .*forward slash/);
|
|
128
|
+
assert.equal(getMilestone('M001'), null, 'invalid milestone title must not persist');
|
|
129
|
+
|
|
130
|
+
const sliceResult = await handlePlanMilestone({
|
|
131
|
+
...validParams(),
|
|
132
|
+
slices: [
|
|
133
|
+
validParams().slices[0],
|
|
134
|
+
{ ...validParams().slices[1], title: 'Client/Server migration' },
|
|
135
|
+
],
|
|
136
|
+
}, base);
|
|
137
|
+
assert.ok('error' in sliceResult);
|
|
138
|
+
assert.match(sliceResult.error, /validation failed: slices\[1\]\.title is invalid: .*forward slash/);
|
|
139
|
+
assert.equal(getMilestoneSlices('M001').length, 0, 'invalid slice title must not persist partial roadmap state');
|
|
140
|
+
} finally {
|
|
141
|
+
cleanup(base);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
119
145
|
test('handlePlanMilestone surfaces render failures and does not clear parse-visible state on failure', async () => {
|
|
120
146
|
const base = makeTmpBase();
|
|
121
147
|
const dbPath = join(base, '.gsd', 'gsd.db');
|
|
@@ -65,7 +65,9 @@ test("plan-slice prompt: DB-backed tool names survive template substitution", ()
|
|
|
65
65
|
const result = loadPrompt("plan-slice", { ...BASE_VARS, commitInstruction: "Do not commit." });
|
|
66
66
|
assert.ok(result.includes("gsd_plan_slice"), "gsd_plan_slice should appear in rendered prompt");
|
|
67
67
|
assert.ok(result.includes("gsd_plan_task"), "gsd_plan_task should appear in rendered prompt");
|
|
68
|
+
assert.ok(result.includes("gsd_decision_save"), "structural decisions should use DB-backed decision tool");
|
|
68
69
|
assert.ok(result.includes("canonical write path"), "canonical write path language should survive substitution");
|
|
70
|
+
assert.doesNotMatch(result, /append them to `.gsd\/DECISIONS\.md`/);
|
|
69
71
|
});
|
|
70
72
|
|
|
71
73
|
test("plan-slice prompt: compact planning gates survive template substitution", () => {
|