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
|
@@ -544,7 +544,7 @@ test("ADR-017 (#5703): live worker lock is not cleared", async (t) => {
|
|
|
544
544
|
|
|
545
545
|
// ─── #5704: unregistered-milestone drift ────────────────────────────────────
|
|
546
546
|
|
|
547
|
-
test("ADR-017 (#5704): unregistered-milestone drift
|
|
547
|
+
test("ADR-017 (#5704): unregistered-milestone drift fails closed without importing markdown", async (t) => {
|
|
548
548
|
const base = mkdtempSync(join(tmpdir(), "gsd-adr017-projmd-"));
|
|
549
549
|
const milestoneDir = join(base, ".gsd", "milestones", "M042");
|
|
550
550
|
mkdirSync(milestoneDir, { recursive: true });
|
|
@@ -571,20 +571,22 @@ test("ADR-017 (#5704): unregistered-milestone drift detected and DB row inserted
|
|
|
571
571
|
// Pre-condition: filesystem has the milestone, DB does NOT.
|
|
572
572
|
assert.equal(getMilestone("M042"), null, "pre: DB has no row for M042");
|
|
573
573
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
574
|
+
await assert.rejects(
|
|
575
|
+
reconcileBeforeDispatch(base, {
|
|
576
|
+
invalidateStateCache: () => {},
|
|
577
|
+
deriveState: async () => makeState(),
|
|
578
|
+
}),
|
|
579
|
+
(err: unknown) => {
|
|
580
|
+
assert.ok(err instanceof ReconciliationFailedError);
|
|
581
|
+
assert.match(String(err.message), /unregistered-milestone/);
|
|
582
|
+
assert.equal(err.failures[0]?.drift.kind, "unregistered-milestone");
|
|
583
|
+
assert.match(String(err.failures[0]?.cause), /M042/);
|
|
584
|
+
assert.match(String(err.failures[0]?.cause), /markdown projection/);
|
|
585
|
+
assert.match(String(err.failures[0]?.cause), /recovery\/migration/);
|
|
586
|
+
return true;
|
|
587
|
+
},
|
|
583
588
|
);
|
|
584
|
-
assert.
|
|
585
|
-
if (milestoneRepaired?.kind === "unregistered-milestone") {
|
|
586
|
-
assert.equal(milestoneRepaired.milestoneId, "M042");
|
|
587
|
-
}
|
|
589
|
+
assert.equal(getMilestone("M042"), null, "post: DB still has no row for M042");
|
|
588
590
|
});
|
|
589
591
|
|
|
590
592
|
test("ADR-017 (#5704): registered milestone (DB row present) → no drift", async (t) => {
|
|
@@ -627,13 +629,14 @@ test("ADR-017 (#5704): registered milestone (DB row present) → no drift", asyn
|
|
|
627
629
|
|
|
628
630
|
// ─── #5705: roadmap-divergence drift ─────────────────────────────────────────
|
|
629
631
|
|
|
630
|
-
test("ADR-017 (#5705): roadmap-divergence
|
|
632
|
+
test("ADR-017 (#5705): roadmap-divergence re-renders projection without syncing depends into DB", async (t) => {
|
|
631
633
|
const base = mkdtempSync(join(tmpdir(), "gsd-adr017-roadmap-"));
|
|
632
634
|
const milestoneDir = join(base, ".gsd", "milestones", "M001");
|
|
635
|
+
const roadmapPath = join(milestoneDir, "M001-ROADMAP.md");
|
|
633
636
|
mkdirSync(milestoneDir, { recursive: true });
|
|
634
637
|
// ROADMAP.md declares S02 depends on [S01]
|
|
635
638
|
writeFileSync(
|
|
636
|
-
|
|
639
|
+
roadmapPath,
|
|
637
640
|
[
|
|
638
641
|
"# M001: Test",
|
|
639
642
|
"",
|
|
@@ -665,7 +668,12 @@ test("ADR-017 (#5705): roadmap-divergence drift detected and DB depends synced",
|
|
|
665
668
|
});
|
|
666
669
|
|
|
667
670
|
assert.equal(result.ok, true);
|
|
668
|
-
assert.deepEqual(getSlice("M001", "S02")?.depends, [
|
|
671
|
+
assert.deepEqual(getSlice("M001", "S02")?.depends, [], "post: DB depends remains authoritative");
|
|
672
|
+
assert.match(
|
|
673
|
+
readFileSync(roadmapPath, "utf-8"),
|
|
674
|
+
/- \[ \] \*\*S02: Feature\*\* `risk:medium` `depends:\[\]`/,
|
|
675
|
+
"post: ROADMAP projection is regenerated from DB depends",
|
|
676
|
+
);
|
|
669
677
|
const roadmapRepaired = result.repaired.find((d) => d.kind === "roadmap-divergence");
|
|
670
678
|
assert.ok(roadmapRepaired, "repaired list should include the roadmap-divergence drift");
|
|
671
679
|
if (roadmapRepaired?.kind === "roadmap-divergence") {
|
|
@@ -673,13 +681,14 @@ test("ADR-017 (#5705): roadmap-divergence drift detected and DB depends synced",
|
|
|
673
681
|
}
|
|
674
682
|
});
|
|
675
683
|
|
|
676
|
-
test("ADR-017 (#5705): ROADMAP
|
|
684
|
+
test("ADR-017 (#5705): ROADMAP-only slice is removed from projection and not inserted into DB", async (t) => {
|
|
677
685
|
const base = mkdtempSync(join(tmpdir(), "gsd-adr017-roadmap-newslice-"));
|
|
678
686
|
const milestoneDir = join(base, ".gsd", "milestones", "M001");
|
|
687
|
+
const roadmapPath = join(milestoneDir, "M001-ROADMAP.md");
|
|
679
688
|
mkdirSync(milestoneDir, { recursive: true });
|
|
680
689
|
// ROADMAP.md declares S01 and S02; DB will only have S01.
|
|
681
690
|
writeFileSync(
|
|
682
|
-
|
|
691
|
+
roadmapPath,
|
|
683
692
|
[
|
|
684
693
|
"# M001: Test",
|
|
685
694
|
"",
|
|
@@ -710,10 +719,10 @@ test("ADR-017 (#5705): ROADMAP declares slice missing from DB → slice inserted
|
|
|
710
719
|
});
|
|
711
720
|
|
|
712
721
|
assert.equal(result.ok, true);
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
assert.
|
|
716
|
-
assert.
|
|
722
|
+
assert.equal(getSlice("M001", "S02"), null, "post: S02 still has no DB row");
|
|
723
|
+
const rendered = readFileSync(roadmapPath, "utf-8");
|
|
724
|
+
assert.match(rendered, /- \[ \] \*\*S01: Foundation\*\*/);
|
|
725
|
+
assert.doesNotMatch(rendered, /S02: Feature/, "post: ROADMAP-only S02 removed from projection");
|
|
717
726
|
const roadmapRepaired = result.repaired.find((d) => d.kind === "roadmap-divergence");
|
|
718
727
|
assert.ok(roadmapRepaired, "repaired list should include the roadmap-divergence drift");
|
|
719
728
|
if (roadmapRepaired?.kind === "roadmap-divergence") {
|
|
@@ -721,6 +730,93 @@ test("ADR-017 (#5705): ROADMAP declares slice missing from DB → slice inserted
|
|
|
721
730
|
}
|
|
722
731
|
});
|
|
723
732
|
|
|
733
|
+
test("ADR-017 (#5705): ROADMAP sequence drift re-renders from DB order without mutating DB", async (t) => {
|
|
734
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-adr017-roadmap-sequence-"));
|
|
735
|
+
const milestoneDir = join(base, ".gsd", "milestones", "M001");
|
|
736
|
+
const roadmapPath = join(milestoneDir, "M001-ROADMAP.md");
|
|
737
|
+
mkdirSync(milestoneDir, { recursive: true });
|
|
738
|
+
writeFileSync(
|
|
739
|
+
roadmapPath,
|
|
740
|
+
[
|
|
741
|
+
"# M001: Test",
|
|
742
|
+
"",
|
|
743
|
+
"**Vision:** Verify sequence drift",
|
|
744
|
+
"",
|
|
745
|
+
"## Slices",
|
|
746
|
+
"",
|
|
747
|
+
"- [ ] **S02: Feature** `risk:medium` `depends:[]`",
|
|
748
|
+
"- [ ] **S01: Foundation** `risk:medium` `depends:[]`",
|
|
749
|
+
"",
|
|
750
|
+
].join("\n"),
|
|
751
|
+
);
|
|
752
|
+
t.after(() => {
|
|
753
|
+
try { closeDatabase(); } catch { /* noop */ }
|
|
754
|
+
rmSync(base, { recursive: true, force: true });
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
758
|
+
insertMilestone({ id: "M001", title: "Test", status: "active" });
|
|
759
|
+
insertSlice({ id: "S01", milestoneId: "M001", title: "Foundation", status: "pending", risk: "medium", depends: [], demo: "", sequence: 1 });
|
|
760
|
+
insertSlice({ id: "S02", milestoneId: "M001", title: "Feature", status: "pending", risk: "medium", depends: [], demo: "", sequence: 2 });
|
|
761
|
+
|
|
762
|
+
const result = await reconcileBeforeDispatch(base, {
|
|
763
|
+
invalidateStateCache: () => {},
|
|
764
|
+
deriveState: async () => makeState(),
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
assert.equal(result.ok, true);
|
|
768
|
+
assert.equal(getSlice("M001", "S01")?.sequence, 1, "post: S01 DB sequence remains authoritative");
|
|
769
|
+
assert.equal(getSlice("M001", "S02")?.sequence, 2, "post: S02 DB sequence remains authoritative");
|
|
770
|
+
const rendered = readFileSync(roadmapPath, "utf-8");
|
|
771
|
+
assert.ok(
|
|
772
|
+
rendered.indexOf("S01: Foundation") < rendered.indexOf("S02: Feature"),
|
|
773
|
+
"post: ROADMAP projection follows DB sequence",
|
|
774
|
+
);
|
|
775
|
+
assert.ok(result.repaired.some((d) => d.kind === "roadmap-divergence"));
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
test("ADR-017 (#5705): ROADMAP checkbox drift re-renders from DB status without mutating DB", async (t) => {
|
|
779
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-adr017-roadmap-checkbox-"));
|
|
780
|
+
const milestoneDir = join(base, ".gsd", "milestones", "M001");
|
|
781
|
+
const roadmapPath = join(milestoneDir, "M001-ROADMAP.md");
|
|
782
|
+
mkdirSync(milestoneDir, { recursive: true });
|
|
783
|
+
writeFileSync(
|
|
784
|
+
roadmapPath,
|
|
785
|
+
[
|
|
786
|
+
"# M001: Test",
|
|
787
|
+
"",
|
|
788
|
+
"**Vision:** Verify checkbox drift",
|
|
789
|
+
"",
|
|
790
|
+
"## Slices",
|
|
791
|
+
"",
|
|
792
|
+
"- [x] **S01: Foundation** `risk:medium` `depends:[]`",
|
|
793
|
+
"",
|
|
794
|
+
].join("\n"),
|
|
795
|
+
);
|
|
796
|
+
t.after(() => {
|
|
797
|
+
try { closeDatabase(); } catch { /* noop */ }
|
|
798
|
+
rmSync(base, { recursive: true, force: true });
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
802
|
+
insertMilestone({ id: "M001", title: "Test", status: "active" });
|
|
803
|
+
insertSlice({ id: "S01", milestoneId: "M001", title: "Foundation", status: "pending", risk: "medium", depends: [], demo: "", sequence: 1 });
|
|
804
|
+
|
|
805
|
+
const result = await reconcileBeforeDispatch(base, {
|
|
806
|
+
invalidateStateCache: () => {},
|
|
807
|
+
deriveState: async () => makeState(),
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
assert.equal(result.ok, true);
|
|
811
|
+
assert.equal(getSlice("M001", "S01")?.status, "pending", "post: DB status remains authoritative");
|
|
812
|
+
assert.match(
|
|
813
|
+
readFileSync(roadmapPath, "utf-8"),
|
|
814
|
+
/- \[ \] \*\*S01: Foundation\*\*/,
|
|
815
|
+
"post: ROADMAP checkbox reflects DB status",
|
|
816
|
+
);
|
|
817
|
+
assert.ok(result.repaired.some((d) => d.kind === "roadmap-divergence"));
|
|
818
|
+
});
|
|
819
|
+
|
|
724
820
|
test("ADR-017 (#5705): in-sync ROADMAP and DB → no roadmap-divergence drift", async (t) => {
|
|
725
821
|
const base = mkdtempSync(join(tmpdir(), "gsd-adr017-roadmap-clean-"));
|
|
726
822
|
const milestoneDir = join(base, ".gsd", "milestones", "M001");
|
|
@@ -19,13 +19,17 @@ import {
|
|
|
19
19
|
closeDatabase,
|
|
20
20
|
insertMilestone,
|
|
21
21
|
} from "../gsd-db.ts";
|
|
22
|
-
import { registerAutoWorker } from "../db/auto-workers.ts";
|
|
22
|
+
import { registerAutoWorker, markWorkerCrashed } from "../db/auto-workers.ts";
|
|
23
23
|
import { claimMilestoneLease } from "../db/milestone-leases.ts";
|
|
24
24
|
import {
|
|
25
25
|
recordDispatchClaim,
|
|
26
|
+
markFailed,
|
|
27
|
+
markCanceled,
|
|
26
28
|
getRecentUnitKeysForWorker,
|
|
29
|
+
getRecentUnitKeysForProjectRoot,
|
|
27
30
|
} from "../db/unit-dispatches.ts";
|
|
28
31
|
import { setRuntimeKv, getRuntimeKv } from "../db/runtime-kv.ts";
|
|
32
|
+
import { detectStuck } from "../auto/detect-stuck.ts";
|
|
29
33
|
|
|
30
34
|
function makeBase(): string {
|
|
31
35
|
const base = mkdtempSync(join(tmpdir(), "gsd-stuck-state-db-"));
|
|
@@ -67,6 +71,65 @@ test("getRecentUnitKeysForWorker reconstructs the recentUnits sliding window", (
|
|
|
67
71
|
assert.deepEqual(window.map(w => w.key), ["U1", "U2", "U3"]);
|
|
68
72
|
});
|
|
69
73
|
|
|
74
|
+
test("getRecentUnitKeysForProjectRoot restores compound keys used by stuck detection", (t) => {
|
|
75
|
+
const base = makeBase();
|
|
76
|
+
t.after(() => cleanup(base));
|
|
77
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
78
|
+
insertMilestone({ id: "M001", title: "T", status: "active" });
|
|
79
|
+
insertMilestone({ id: "M002", title: "Crashed", status: "active" });
|
|
80
|
+
const worker = registerAutoWorker({ projectRootRealpath: base });
|
|
81
|
+
const lease = claimMilestoneLease(worker, "M001");
|
|
82
|
+
assert.equal(lease.ok, true);
|
|
83
|
+
if (!lease.ok) return;
|
|
84
|
+
|
|
85
|
+
for (let i = 0; i < 2; i++) {
|
|
86
|
+
const claim = recordDispatchClaim({
|
|
87
|
+
traceId: `t${i}`,
|
|
88
|
+
workerId: worker,
|
|
89
|
+
milestoneLeaseToken: lease.token,
|
|
90
|
+
milestoneId: "M001",
|
|
91
|
+
sliceId: "S01",
|
|
92
|
+
unitType: "complete-slice",
|
|
93
|
+
unitId: "M001/S01",
|
|
94
|
+
});
|
|
95
|
+
assert.equal(claim.ok, true);
|
|
96
|
+
if (!claim.ok) return;
|
|
97
|
+
markCanceled(claim.dispatchId, "pause");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const crashedWorker = registerAutoWorker({ projectRootRealpath: base });
|
|
101
|
+
const crashedLease = claimMilestoneLease(crashedWorker, "M002");
|
|
102
|
+
assert.equal(crashedLease.ok, true);
|
|
103
|
+
if (!crashedLease.ok) return;
|
|
104
|
+
|
|
105
|
+
for (let i = 0; i < 3; i++) {
|
|
106
|
+
const claim = recordDispatchClaim({
|
|
107
|
+
traceId: `crashed-${i}`,
|
|
108
|
+
workerId: crashedWorker,
|
|
109
|
+
milestoneLeaseToken: crashedLease.token,
|
|
110
|
+
milestoneId: "M002",
|
|
111
|
+
sliceId: "S01",
|
|
112
|
+
taskId: "T01",
|
|
113
|
+
unitType: "execute-task",
|
|
114
|
+
unitId: "M002/S01/T01",
|
|
115
|
+
});
|
|
116
|
+
assert.equal(claim.ok, true);
|
|
117
|
+
if (!claim.ok) return;
|
|
118
|
+
markFailed(claim.dispatchId, { errorSummary: "worker crashed" });
|
|
119
|
+
}
|
|
120
|
+
markWorkerCrashed(crashedWorker);
|
|
121
|
+
|
|
122
|
+
const window = getRecentUnitKeysForProjectRoot(base, 3);
|
|
123
|
+
assert.deepEqual(window.map(w => w.key), [
|
|
124
|
+
"complete-slice/M001/S01",
|
|
125
|
+
"complete-slice/M001/S01",
|
|
126
|
+
]);
|
|
127
|
+
|
|
128
|
+
const result = detectStuck([...window, { key: "complete-slice/M001/S01" }]);
|
|
129
|
+
assert.equal(result?.stuck, true);
|
|
130
|
+
assert.match(result?.reason ?? "", /3 consecutive times/);
|
|
131
|
+
});
|
|
132
|
+
|
|
70
133
|
test("getRecentUnitKeysForWorker honors the limit parameter", (t) => {
|
|
71
134
|
const base = makeBase();
|
|
72
135
|
t.after(() => cleanup(base));
|
|
@@ -175,13 +175,17 @@ const verificationEvidence = [
|
|
|
175
175
|
);
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
// Test 11: empty key_files renders YAML
|
|
178
|
+
// Test 11: empty key_files renders an empty YAML list, not a sentinel path
|
|
179
179
|
{
|
|
180
180
|
const noFiles = { ...taskRow, key_files: [] };
|
|
181
181
|
const output = renderSummaryContent(noFiles, SLICE_ID, MILESTONE_ID);
|
|
182
182
|
assertTrue(
|
|
183
|
-
output.includes("key_files
|
|
184
|
-
"empty key_files must render as YAML list
|
|
183
|
+
output.includes("key_files: []"),
|
|
184
|
+
"empty key_files must render as an empty YAML list",
|
|
185
|
+
);
|
|
186
|
+
assertTrue(
|
|
187
|
+
!output.includes("key_files:\n - (none)"),
|
|
188
|
+
"empty key_files must not render (none) as a path-like list item",
|
|
185
189
|
);
|
|
186
190
|
}
|
|
187
191
|
|
|
@@ -14,7 +14,10 @@ import {
|
|
|
14
14
|
type SkillsPolicy,
|
|
15
15
|
type UnitContextManifest,
|
|
16
16
|
} from "../unit-context-manifest.ts";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
ALLOWED_PLANNING_DISPATCH_AGENTS,
|
|
19
|
+
shouldBlockPlanningUnit,
|
|
20
|
+
} from "../bootstrap/write-gate.ts";
|
|
18
21
|
import {
|
|
19
22
|
getRequiredWorkflowToolsForAutoUnit,
|
|
20
23
|
getRequiredWorkflowToolsForGuidedUnit,
|
|
@@ -217,7 +220,7 @@ test("#4934: every manifest declares a tools policy", () => {
|
|
|
217
220
|
});
|
|
218
221
|
|
|
219
222
|
test("#4934: tools.mode is one of the declared policies", () => {
|
|
220
|
-
const validModes = new Set(["all", "read-only", "planning", "planning-dispatch", "docs"]);
|
|
223
|
+
const validModes = new Set(["all", "read-only", "planning", "planning-dispatch", "docs", "verification"]);
|
|
221
224
|
for (const [unitType, manifest] of Object.entries(UNIT_MANIFESTS)) {
|
|
222
225
|
const mode = (manifest as { tools: { mode: string } }).tools.mode;
|
|
223
226
|
assert.ok(
|
|
@@ -227,28 +230,83 @@ test("#4934: tools.mode is one of the declared policies", () => {
|
|
|
227
230
|
}
|
|
228
231
|
});
|
|
229
232
|
|
|
230
|
-
test('#4934: only
|
|
231
|
-
const allowedAllUnits = new Set(["execute-task", "reactive-execute"]);
|
|
233
|
+
test('#4934: only execution units and complete-milestone may use tools.mode "all"', () => {
|
|
234
|
+
const allowedAllUnits = new Set(["execute-task", "reactive-execute", "complete-milestone"]);
|
|
232
235
|
for (const [unitType, manifest] of Object.entries(UNIT_MANIFESTS)) {
|
|
233
236
|
const mode = (manifest as { tools: { mode: string } }).tools.mode;
|
|
234
237
|
if (mode === "all") {
|
|
235
238
|
assert.ok(
|
|
236
239
|
allowedAllUnits.has(unitType),
|
|
237
|
-
`manifest "${unitType}" declares tools.mode = "all" but is not
|
|
238
|
-
'Only execute-task
|
|
240
|
+
`manifest "${unitType}" declares tools.mode = "all" but is not explicitly allowed. ` +
|
|
241
|
+
'Only execute-task, reactive-execute, and complete-milestone should have full source write access; ' +
|
|
239
242
|
'planning/discuss/research units must use "planning" or "planning-dispatch" (or "docs" for rewrite-docs).',
|
|
240
243
|
);
|
|
241
244
|
}
|
|
242
245
|
}
|
|
243
246
|
});
|
|
244
247
|
|
|
248
|
+
test("#5453: complete-milestone uses all tools so bash verification is not planning-dispatch blocked", () => {
|
|
249
|
+
const manifest = UNIT_MANIFESTS["complete-milestone"];
|
|
250
|
+
|
|
251
|
+
assert.strictEqual(manifest.tools.mode, "all");
|
|
252
|
+
assert.deepEqual(resolveSubagentPermissionContract("complete-milestone"), {
|
|
253
|
+
allowed: true,
|
|
254
|
+
allowedSubagents: ["*"],
|
|
255
|
+
toolsMode: "all",
|
|
256
|
+
});
|
|
257
|
+
// Runtime gate-level regression: these verification commands were blocked
|
|
258
|
+
// under planning-dispatch in #5453; complete-milestone must bypass that gate.
|
|
259
|
+
for (const cmd of ["git diff --name-only HEAD~1", "git log -n1 --oneline"]) {
|
|
260
|
+
const result = shouldBlockPlanningUnit(
|
|
261
|
+
"bash",
|
|
262
|
+
cmd,
|
|
263
|
+
process.cwd(),
|
|
264
|
+
"complete-milestone",
|
|
265
|
+
manifest.tools,
|
|
266
|
+
);
|
|
267
|
+
assert.strictEqual(
|
|
268
|
+
result.block,
|
|
269
|
+
false,
|
|
270
|
+
`shouldBlockPlanningUnit must not block ${cmd} for complete-milestone: ${result.reason}`,
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test("#5843: run-uat uses verification tools policy so build/test commands can run", () => {
|
|
276
|
+
const manifest = UNIT_MANIFESTS["run-uat"];
|
|
277
|
+
|
|
278
|
+
assert.strictEqual(manifest.tools.mode, "verification");
|
|
279
|
+
|
|
280
|
+
const buildResult = shouldBlockPlanningUnit(
|
|
281
|
+
"bash",
|
|
282
|
+
"npm run build 2>&1",
|
|
283
|
+
process.cwd(),
|
|
284
|
+
"run-uat",
|
|
285
|
+
manifest.tools,
|
|
286
|
+
);
|
|
287
|
+
assert.strictEqual(
|
|
288
|
+
buildResult.block,
|
|
289
|
+
false,
|
|
290
|
+
`run-uat must allow build verification commands: ${buildResult.reason}`,
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
const sourceWriteResult = shouldBlockPlanningUnit(
|
|
294
|
+
"edit",
|
|
295
|
+
"src/main.ts",
|
|
296
|
+
process.cwd(),
|
|
297
|
+
"run-uat",
|
|
298
|
+
manifest.tools,
|
|
299
|
+
);
|
|
300
|
+
assert.strictEqual(sourceWriteResult.block, true);
|
|
301
|
+
assert.match(sourceWriteResult.reason!, /tools-policy "verification"/);
|
|
302
|
+
});
|
|
303
|
+
|
|
245
304
|
test('planning-dispatch mode is reserved for slice-level decomposition and completion units', () => {
|
|
246
305
|
const allowedDispatchUnits = new Set([
|
|
247
306
|
"plan-slice",
|
|
248
307
|
"research-slice",
|
|
249
308
|
"refine-slice",
|
|
250
309
|
"complete-slice",
|
|
251
|
-
"complete-milestone",
|
|
252
310
|
"gate-evaluate",
|
|
253
311
|
// Deep planning mode: research-project orchestrates 4 parallel research
|
|
254
312
|
// subagents (stack/features/architecture/pitfalls). Subagent dispatch is
|
|
@@ -22,7 +22,7 @@ import { join, dirname } from "node:path";
|
|
|
22
22
|
import { tmpdir } from "node:os";
|
|
23
23
|
import { spawnSync } from "node:child_process";
|
|
24
24
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
25
|
-
import { discoverCommands, runVerificationGate, formatFailureContext, captureRuntimeErrors, runDependencyAudit, isLikelyCommand } from "../verification-gate.ts";
|
|
25
|
+
import { discoverCommands, runVerificationGate, formatFailureContext, captureRuntimeErrors, runDependencyAudit, isLikelyCommand, validateVerificationCommand } from "../verification-gate.ts";
|
|
26
26
|
import type { CaptureRuntimeErrorsOptions, DependencyAuditOptions } from "../verification-gate.ts";
|
|
27
27
|
import { validatePreferences } from "../preferences.ts";
|
|
28
28
|
|
|
@@ -215,6 +215,102 @@ describe("verification-gate: discovery", () => {
|
|
|
215
215
|
assert.equal(result.source, "task-plan");
|
|
216
216
|
assert.deepStrictEqual(result.commands, ["npm run test"]);
|
|
217
217
|
});
|
|
218
|
+
|
|
219
|
+
test("taskPlanVerify rejects piped pytest command", () => {
|
|
220
|
+
const result = discoverCommands({
|
|
221
|
+
taskPlanVerify: "python3 -m pytest tests/ -q --tb=short 2>&1 | tail -5",
|
|
222
|
+
cwd: tmp,
|
|
223
|
+
});
|
|
224
|
+
assert.equal(result.source, "none");
|
|
225
|
+
assert.deepStrictEqual(result.commands, []);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("Python project with tests discovers pytest when package.json is absent", () => {
|
|
229
|
+
mkdirSync(join(tmp, "tests"));
|
|
230
|
+
writeFileSync(join(tmp, "tests", "test_sample.py"), "def test_sample():\n assert True\n");
|
|
231
|
+
writeFileSync(
|
|
232
|
+
join(tmp, "pyproject.toml"),
|
|
233
|
+
`[project]
|
|
234
|
+
name = "sample"
|
|
235
|
+
|
|
236
|
+
[tool.pytest.ini_options]
|
|
237
|
+
pythonpath = ["."]
|
|
238
|
+
`,
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
const result = discoverCommands({ cwd: tmp });
|
|
242
|
+
|
|
243
|
+
assert.equal(result.source, "python-project");
|
|
244
|
+
assert.deepStrictEqual(result.commands, ["python3 -m pytest"]);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("Python project with nested Python test file discovers pytest", () => {
|
|
248
|
+
mkdirSync(join(tmp, "tests", "unit"), { recursive: true });
|
|
249
|
+
writeFileSync(join(tmp, "tests", "unit", "sample_test.py"), "def test_sample():\n assert True\n");
|
|
250
|
+
|
|
251
|
+
const result = discoverCommands({ cwd: tmp });
|
|
252
|
+
|
|
253
|
+
assert.equal(result.source, "python-project");
|
|
254
|
+
assert.deepStrictEqual(result.commands, ["python3 -m pytest"]);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test("Python project with pytest.ini discovers pytest", () => {
|
|
258
|
+
writeFileSync(join(tmp, "pytest.ini"), "[pytest]\npythonpath = .\n");
|
|
259
|
+
|
|
260
|
+
const result = discoverCommands({ cwd: tmp });
|
|
261
|
+
|
|
262
|
+
assert.equal(result.source, "python-project");
|
|
263
|
+
assert.deepStrictEqual(result.commands, ["python3 -m pytest"]);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test("Python project with explicit pyproject pytest marker discovers pytest", () => {
|
|
267
|
+
writeFileSync(
|
|
268
|
+
join(tmp, "pyproject.toml"),
|
|
269
|
+
`[tool.pytest]
|
|
270
|
+
pythonpath = ["."]
|
|
271
|
+
`,
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
const result = discoverCommands({ cwd: tmp });
|
|
275
|
+
|
|
276
|
+
assert.equal(result.source, "python-project");
|
|
277
|
+
assert.deepStrictEqual(result.commands, ["python3 -m pytest"]);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
test("Python project markers without pytest evidence do not discover pytest", () => {
|
|
281
|
+
mkdirSync(join(tmp, "tests"));
|
|
282
|
+
writeFileSync(join(tmp, "tests", "README.md"), "# tests\n");
|
|
283
|
+
writeFileSync(
|
|
284
|
+
join(tmp, "pyproject.toml"),
|
|
285
|
+
`[project]
|
|
286
|
+
name = "sample"
|
|
287
|
+
dependencies = ["pytest-cov"]
|
|
288
|
+
`,
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
const result = discoverCommands({ cwd: tmp });
|
|
292
|
+
|
|
293
|
+
assert.equal(result.source, "none");
|
|
294
|
+
assert.deepStrictEqual(result.commands, []);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
test("Python project with setup.cfg alone does not discover pytest", () => {
|
|
298
|
+
writeFileSync(join(tmp, "setup.cfg"), "[tool:pytest]\npythonpath = .\n");
|
|
299
|
+
|
|
300
|
+
const result = discoverCommands({ cwd: tmp });
|
|
301
|
+
|
|
302
|
+
assert.equal(result.source, "none");
|
|
303
|
+
assert.deepStrictEqual(result.commands, []);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
test("Python project with tox.ini alone does not discover pytest", () => {
|
|
307
|
+
writeFileSync(join(tmp, "tox.ini"), "[pytest]\npythonpath = .\n");
|
|
308
|
+
|
|
309
|
+
const result = discoverCommands({ cwd: tmp });
|
|
310
|
+
|
|
311
|
+
assert.equal(result.source, "none");
|
|
312
|
+
assert.deepStrictEqual(result.commands, []);
|
|
313
|
+
});
|
|
218
314
|
});
|
|
219
315
|
|
|
220
316
|
// ─── Execution Tests ─────────────────────────────────────────────────────────
|
|
@@ -445,6 +541,10 @@ test("isLikelyCommand: prose descriptions are rejected", () => {
|
|
|
445
541
|
assert.equal(isLikelyCommand("Build succeeds without errors or warnings"), false);
|
|
446
542
|
});
|
|
447
543
|
|
|
544
|
+
test("isLikelyCommand: non-ASCII prose descriptions are rejected", () => {
|
|
545
|
+
assert.equal(isLikelyCommand("所有 命令 输出 一行 JSONL go test ./... 通过"), false);
|
|
546
|
+
});
|
|
547
|
+
|
|
448
548
|
test("isLikelyCommand: empty or whitespace-only strings are rejected", () => {
|
|
449
549
|
assert.equal(isLikelyCommand(""), false);
|
|
450
550
|
assert.equal(isLikelyCommand(" "), false);
|
|
@@ -455,6 +555,15 @@ test("isLikelyCommand: short lowercase tokens without flags are accepted (could
|
|
|
455
555
|
assert.equal(isLikelyCommand("mycheck"), true);
|
|
456
556
|
});
|
|
457
557
|
|
|
558
|
+
test("validateVerificationCommand rejects shell control syntax", () => {
|
|
559
|
+
assert.deepEqual(validateVerificationCommand("python3 -m pytest tests/ -q --tb=short").ok, true);
|
|
560
|
+
const result = validateVerificationCommand("python3 -m pytest tests/ -q --tb=short 2>&1 | tail -5");
|
|
561
|
+
assert.equal(result.ok, false);
|
|
562
|
+
if (!result.ok) {
|
|
563
|
+
assert.match(result.reason, /shell control syntax/);
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
|
|
458
567
|
// ─── Additional Preference Validation Tests (T02) ──────────────────────────
|
|
459
568
|
|
|
460
569
|
test("verification-gate: verification_commands produces no unknown-key warnings", () => {
|
|
@@ -375,7 +375,7 @@ test("workflow MCP launch config reaches mutation tools over stdio", async () =>
|
|
|
375
375
|
estimate: "10m",
|
|
376
376
|
files: ["src/resources/extensions/gsd/workflow-mcp.ts"],
|
|
377
377
|
verify: "node --test",
|
|
378
|
-
inputs: ["M001-ROADMAP.md"],
|
|
378
|
+
inputs: [".gsd/milestones/M001/M001-ROADMAP.md"],
|
|
379
379
|
expectedOutput: ["S01-PLAN.md", "T01-PLAN.md"],
|
|
380
380
|
},
|
|
381
381
|
],
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
import assert from "node:assert/strict";
|
|
5
5
|
import test from "node:test";
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
measureMemoryPressure,
|
|
9
|
+
shouldCheckMemoryPressure,
|
|
10
|
+
} from "../auto/workflow-memory-pressure.ts";
|
|
8
11
|
|
|
9
12
|
const mb = 1024 * 1024;
|
|
10
13
|
|
|
@@ -69,3 +72,20 @@ test("measureMemoryPressure falls back when heap limit cannot be read", () => {
|
|
|
69
72
|
pct: 0.25,
|
|
70
73
|
});
|
|
71
74
|
});
|
|
75
|
+
|
|
76
|
+
test("shouldCheckMemoryPressure covers the first auto-mode iteration", () => {
|
|
77
|
+
assert.equal(shouldCheckMemoryPressure(1, 5), true);
|
|
78
|
+
assert.equal(shouldCheckMemoryPressure(2, 5), false);
|
|
79
|
+
assert.equal(shouldCheckMemoryPressure(5, 5), true);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("shouldCheckMemoryPressure rejects invalid intervals", () => {
|
|
83
|
+
assert.throws(
|
|
84
|
+
() => shouldCheckMemoryPressure(1, 0),
|
|
85
|
+
/positive integer/,
|
|
86
|
+
);
|
|
87
|
+
assert.throws(
|
|
88
|
+
() => shouldCheckMemoryPressure(1, 1.5),
|
|
89
|
+
/positive integer/,
|
|
90
|
+
);
|
|
91
|
+
});
|
|
@@ -246,7 +246,7 @@ test("executePlanSlice writes task planning state and rendered plan artifacts",
|
|
|
246
246
|
estimate: "15m",
|
|
247
247
|
files: ["src/resources/extensions/gsd/tools/workflow-tool-executors.ts"],
|
|
248
248
|
verify: "node --test",
|
|
249
|
-
inputs: ["ROADMAP.md"],
|
|
249
|
+
inputs: [".gsd/milestones/M001/M001-ROADMAP.md"],
|
|
250
250
|
expectedOutput: ["S01-PLAN.md", "T01-PLAN.md"],
|
|
251
251
|
},
|
|
252
252
|
],
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { describe, test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync, existsSync, realpathSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { execFileSync } from "node:child_process";
|
|
7
|
+
|
|
8
|
+
import { _gitPathspecForWorktreePath } from "../auto-worktree.ts";
|
|
9
|
+
|
|
10
|
+
function run(cmd: string, args: string[], cwd: string): string {
|
|
11
|
+
return execFileSync(cmd, args, { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe("worktree git pathspec", () => {
|
|
15
|
+
test("skips external GSD bookkeeping directories outside the git work-tree", () => {
|
|
16
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "gsd-pathspec-")));
|
|
17
|
+
const repo = join(root, "project");
|
|
18
|
+
const externalGsd = join(root, ".gsd", "projects", "abc123");
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
mkdirSync(repo, { recursive: true });
|
|
22
|
+
mkdirSync(join(externalGsd, "milestones", "M002-wa00fm"), { recursive: true });
|
|
23
|
+
mkdirSync(join(externalGsd, "runtime", "units"), { recursive: true });
|
|
24
|
+
run("git", ["init"], repo);
|
|
25
|
+
writeFileSync(join(repo, "README.md"), "# test\n");
|
|
26
|
+
|
|
27
|
+
assert.equal(
|
|
28
|
+
_gitPathspecForWorktreePath(repo, join(externalGsd, "milestones", "M002-wa00fm")),
|
|
29
|
+
null,
|
|
30
|
+
);
|
|
31
|
+
assert.equal(
|
|
32
|
+
_gitPathspecForWorktreePath(repo, join(externalGsd, "runtime", "units")),
|
|
33
|
+
null,
|
|
34
|
+
);
|
|
35
|
+
} finally {
|
|
36
|
+
if (existsSync(root)) rmSync(root, { recursive: true, force: true });
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
});
|