gsd-pi 2.82.0-dev.725028083 → 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 +4 -3
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/GSD-WORKFLOW.md +10 -1
- package/dist/resources/extensions/claude-code-cli/partial-builder.js +2 -1
- package/dist/resources/extensions/cmux/index.js +5 -0
- 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 +124 -6
- 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 +233 -127
- package/dist/resources/extensions/gsd/auto-prompts.js +2 -2
- 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-verification.js +28 -22
- package/dist/resources/extensions/gsd/auto-worktree.js +111 -1
- package/dist/resources/extensions/gsd/auto.js +158 -55
- 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 +21 -9
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +16 -2
- package/dist/resources/extensions/gsd/clean-root-preflight.js +170 -8
- package/dist/resources/extensions/gsd/commands/catalog.js +4 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +37 -0
- package/dist/resources/extensions/gsd/commands-bootstrap.js +5 -0
- 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/md-importer.js +1 -1
- package/dist/resources/extensions/gsd/migrate/command.js +5 -0
- package/dist/resources/extensions/gsd/migrate/parsers.js +10 -0
- package/dist/resources/extensions/gsd/migrate/preview.js +9 -0
- package/dist/resources/extensions/gsd/migrate/transformer.js +51 -4
- package/dist/resources/extensions/gsd/migrate/writer.js +11 -1
- 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/tools/workflow-tool-executors.js +119 -0
- package/dist/resources/extensions/gsd/unit-context-manifest.js +32 -10
- package/dist/resources/extensions/gsd/validation.js +23 -1
- package/dist/resources/extensions/gsd/verification-gate.js +68 -7
- package/dist/resources/extensions/gsd/verification-verdict.js +26 -0
- package/dist/resources/extensions/gsd/workflow-projections.js +6 -8
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +54 -10
- package/dist/resources/extensions/shared/html-shell.js +388 -0
- package/dist/resources/extensions/subagent/index.js +448 -78
- package/dist/resources/extensions/subagent/launch.js +77 -0
- package/dist/resources/extensions/subagent/run-store.js +148 -0
- package/dist/resources/extensions/visual-brief/artifact-policy.js +29 -0
- package/dist/resources/extensions/visual-brief/extension-manifest.json +8 -0
- package/dist/resources/extensions/visual-brief/index.js +5 -0
- package/dist/resources/extensions/visual-brief/page-contract.js +124 -0
- package/dist/resources/extensions/visual-brief/prompts.js +140 -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 +14 -14
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +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 +14 -14
- 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/middleware-react-loadable-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/2973.33f26573894b6153.js +2 -0
- package/dist/web/standalone/.next/static/chunks/{8359.e059d86b255fce1c.js → 8359.7eb3bb8f8ecf4c01.js} +2 -2
- package/dist/web/standalone/.next/static/chunks/app/layout-8c10ec293ae0f1d5.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-de742b64187e13fe.js → webpack-9a4db269f9ed63ad.js} +1 -1
- package/dist/web/standalone/.next/static/css/746ee28c929d1880.css +1 -0
- package/package.json +4 -4
- package/packages/contracts/dist/rpc.test.js +7 -0
- package/packages/contracts/dist/rpc.test.js.map +1 -1
- package/packages/contracts/dist/workflow.d.ts +21 -0
- package/packages/contracts/dist/workflow.d.ts.map +1 -1
- package/packages/contracts/dist/workflow.js +24 -0
- package/packages/contracts/dist/workflow.js.map +1 -1
- package/packages/contracts/src/rpc.test.ts +8 -0
- package/packages/contracts/src/workflow.ts +24 -0
- package/packages/mcp-server/README.md +13 -4
- package/packages/mcp-server/dist/workflow-tools.d.ts +0 -3
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +80 -0
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/workflow-tools.test.ts +23 -1
- package/packages/mcp-server/src/workflow-tools.ts +168 -0
- package/packages/mcp-server/tsconfig.tsbuildinfo +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/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +5 -0
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/src/tui.ts +6 -0
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
- package/src/resources/GSD-WORKFLOW.md +10 -1
- 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/cmux/index.ts +6 -0
- package/src/resources/extensions/gsd/auto/contracts.ts +59 -16
- 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 +129 -6
- 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 +266 -139
- package/src/resources/extensions/gsd/auto-prompts.ts +2 -2
- 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-verification.ts +36 -34
- package/src/resources/extensions/gsd/auto-worktree.ts +119 -1
- package/src/resources/extensions/gsd/auto.ts +167 -53
- 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 +19 -7
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +19 -3
- package/src/resources/extensions/gsd/clean-root-preflight.ts +174 -8
- package/src/resources/extensions/gsd/commands/catalog.ts +4 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +40 -0
- package/src/resources/extensions/gsd/commands-bootstrap.ts +10 -0
- 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/md-importer.ts +1 -1
- package/src/resources/extensions/gsd/migrate/command.ts +5 -0
- package/src/resources/extensions/gsd/migrate/parsers.ts +11 -0
- package/src/resources/extensions/gsd/migrate/preview.ts +10 -0
- package/src/resources/extensions/gsd/migrate/transformer.ts +58 -4
- package/src/resources/extensions/gsd/migrate/writer.ts +14 -1
- 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 +487 -4
- package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +12 -11
- 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-runtime-state.test.ts +4 -4
- 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/brief-command.test.ts +89 -0
- package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +107 -2
- package/src/resources/extensions/gsd/tests/closeout-git-deferral.test.ts +16 -0
- 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/evidence-cross-ref.test.ts +38 -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/migrate-command.test.ts +48 -3
- package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +5 -1
- package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +24 -1
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +6 -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-exec-retry-bypass.test.ts +79 -1
- 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-corruption-2945.test.ts +6 -0
- 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 +86 -7
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +110 -1
- package/src/resources/extensions/gsd/tests/verification-verdict.test.ts +78 -0
- 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/worktree-lifecycle.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +54 -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/tools/workflow-tool-executors.ts +135 -0
- package/src/resources/extensions/gsd/types.ts +1 -1
- package/src/resources/extensions/gsd/unit-context-manifest.ts +47 -11
- package/src/resources/extensions/gsd/validation.ts +23 -1
- package/src/resources/extensions/gsd/verification-gate.ts +78 -6
- package/src/resources/extensions/gsd/verification-verdict.ts +47 -0
- package/src/resources/extensions/gsd/workflow-projections.ts +6 -8
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +61 -10
- package/src/resources/extensions/shared/html-shell.ts +412 -0
- package/src/resources/extensions/subagent/index.ts +567 -103
- package/src/resources/extensions/subagent/launch.ts +131 -0
- package/src/resources/extensions/subagent/run-store.ts +218 -0
- package/src/resources/extensions/subagent/tests/launch.test.ts +115 -0
- package/src/resources/extensions/subagent/tests/run-store.test.ts +111 -0
- package/src/resources/extensions/visual-brief/artifact-policy.ts +41 -0
- package/src/resources/extensions/visual-brief/extension-manifest.json +8 -0
- package/src/resources/extensions/visual-brief/index.ts +8 -0
- package/src/resources/extensions/visual-brief/page-contract.ts +136 -0
- package/src/resources/extensions/visual-brief/prompts.ts +183 -0
- package/src/resources/extensions/visual-brief/tests/visual-brief.test.ts +212 -0
- package/dist/web/standalone/.next/server/chunks/5822.js +0 -2
- package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js +0 -1
- package/dist/web/standalone/.next/static/css/54ec2745c1da488b.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/{KDRTXR-22LPCsa80X9dey → euQ0CLP_v8V4e76Tu3odJ}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{KDRTXR-22LPCsa80X9dey → euQ0CLP_v8V4e76Tu3odJ}/_ssgManifest.js +0 -0
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* 2. File path consistency — files exist vs prior expected_output
|
|
10
10
|
* 3. Task ordering — detect impossible read-before-create
|
|
11
11
|
* 4. Interface contracts — contradictory function signatures
|
|
12
|
+
* 5. Verify commands — reject unsafe or non-runnable task verification
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
15
|
import { describe, test, mock } from "node:test";
|
|
@@ -22,6 +23,7 @@ import {
|
|
|
22
23
|
checkFilePathConsistency,
|
|
23
24
|
checkTaskOrdering,
|
|
24
25
|
checkInterfaceContracts,
|
|
26
|
+
checkVerificationCommands,
|
|
25
27
|
runPreExecutionChecks,
|
|
26
28
|
normalizeFilePath,
|
|
27
29
|
type PreExecutionResult,
|
|
@@ -812,6 +814,33 @@ function process(a: number): number
|
|
|
812
814
|
});
|
|
813
815
|
});
|
|
814
816
|
|
|
817
|
+
describe("checkVerificationCommands", () => {
|
|
818
|
+
test("accepts pipe-free pytest Verify command", () => {
|
|
819
|
+
const results = checkVerificationCommands([
|
|
820
|
+
createTask({
|
|
821
|
+
id: "T01",
|
|
822
|
+
verify: "python3 -m pytest tests/ -q --tb=short",
|
|
823
|
+
}),
|
|
824
|
+
]);
|
|
825
|
+
|
|
826
|
+
assert.deepEqual(results, []);
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
test("rejects piped pytest Verify command", () => {
|
|
830
|
+
const results = checkVerificationCommands([
|
|
831
|
+
createTask({
|
|
832
|
+
id: "T01",
|
|
833
|
+
verify: "python3 -m pytest tests/ -q --tb=short 2>&1 | tail -5",
|
|
834
|
+
}),
|
|
835
|
+
]);
|
|
836
|
+
|
|
837
|
+
assert.equal(results.length, 1);
|
|
838
|
+
assert.equal(results[0]?.category, "tool");
|
|
839
|
+
assert.equal(results[0]?.blocking, true);
|
|
840
|
+
assert.match(results[0]?.message ?? "", /shell control syntax/);
|
|
841
|
+
});
|
|
842
|
+
});
|
|
843
|
+
|
|
815
844
|
// ─── runPreExecutionChecks Integration Tests ─────────────────────────────────
|
|
816
845
|
|
|
817
846
|
describe("runPreExecutionChecks", () => {
|
|
@@ -847,6 +876,30 @@ describe("runPreExecutionChecks", () => {
|
|
|
847
876
|
}
|
|
848
877
|
});
|
|
849
878
|
|
|
879
|
+
test("returns fail status for unsafe Verify command before execution", async () => {
|
|
880
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
881
|
+
mkdirSync(tempDir, { recursive: true });
|
|
882
|
+
|
|
883
|
+
try {
|
|
884
|
+
const tasks = [
|
|
885
|
+
createTask({
|
|
886
|
+
id: "T01",
|
|
887
|
+
verify: "python3 -m pytest tests/ -q --tb=short 2>&1 | tail -5",
|
|
888
|
+
}),
|
|
889
|
+
];
|
|
890
|
+
|
|
891
|
+
const result = await runPreExecutionChecks(tasks, tempDir);
|
|
892
|
+
|
|
893
|
+
assert.equal(result.status, "fail");
|
|
894
|
+
assert.equal(result.checks.length, 1);
|
|
895
|
+
assert.equal(result.checks[0]?.category, "tool");
|
|
896
|
+
assert.equal(result.checks[0]?.blocking, true);
|
|
897
|
+
assert.match(result.checks[0]?.message ?? "", /Unsafe or non-runnable Verify command/);
|
|
898
|
+
} finally {
|
|
899
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
900
|
+
}
|
|
901
|
+
});
|
|
902
|
+
|
|
850
903
|
test("returns fail status when blocking failure exists", async () => {
|
|
851
904
|
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
852
905
|
mkdirSync(tempDir, { recursive: true });
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
import { loadPrompt } from "../prompt-loader.ts";
|
|
5
|
+
|
|
6
|
+
test("loadPrompt reports missing template variables with balanced braces", () => {
|
|
7
|
+
assert.throws(
|
|
8
|
+
() => loadPrompt("guided-discuss-milestone", {
|
|
9
|
+
milestoneId: "M001",
|
|
10
|
+
milestoneTitle: "Missing working directory",
|
|
11
|
+
structuredQuestionsAvailable: "false",
|
|
12
|
+
fastPathInstruction: "",
|
|
13
|
+
inlinedTemplates: "context template",
|
|
14
|
+
commitInstruction: "Do not commit during this test.",
|
|
15
|
+
}),
|
|
16
|
+
(error) => {
|
|
17
|
+
assert.ok(error instanceof Error);
|
|
18
|
+
assert.match(error.message, /template declares \{\{workingDirectory\}\} but no value was provided/);
|
|
19
|
+
assert.doesNotMatch(error.message, /\{\{workingDirectory\}\}\}/);
|
|
20
|
+
return true;
|
|
21
|
+
},
|
|
22
|
+
);
|
|
23
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Regression
|
|
3
|
-
* block completion when VALIDATION
|
|
2
|
+
* Regression tests for non-passing VALIDATION verdicts: completing-milestone
|
|
3
|
+
* dispatch must block completion when VALIDATION needs remediation or attention.
|
|
4
4
|
*
|
|
5
5
|
* Without this guard, needs-remediation + allSlicesDone causes a loop:
|
|
6
6
|
* complete-milestone dispatched → agent refuses (correct) → no SUMMARY
|
|
@@ -66,6 +66,50 @@ test("completing-milestone blocks when VALIDATION verdict is needs-remediation (
|
|
|
66
66
|
}
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
+
test("completing-milestone blocks when VALIDATION verdict is needs-attention (#5747)", async () => {
|
|
70
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-attention-"));
|
|
71
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
writeFileSync(
|
|
75
|
+
join(base, ".gsd", "milestones", "M001", "M001-VALIDATION.md"),
|
|
76
|
+
[
|
|
77
|
+
"---",
|
|
78
|
+
"verdict: needs-attention",
|
|
79
|
+
"remediation_round: 0",
|
|
80
|
+
"---",
|
|
81
|
+
"",
|
|
82
|
+
"# Validation Report",
|
|
83
|
+
"",
|
|
84
|
+
"Acceptance proof is incomplete and needs human attention.",
|
|
85
|
+
].join("\n"),
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const ctx = {
|
|
89
|
+
mid: "M001",
|
|
90
|
+
midTitle: "Test Milestone",
|
|
91
|
+
basePath: base,
|
|
92
|
+
state: { phase: "completing-milestone" } as any,
|
|
93
|
+
prefs: {} as any,
|
|
94
|
+
session: undefined,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const result = await completingRule!.match(ctx);
|
|
98
|
+
|
|
99
|
+
assert.ok(result !== null, "rule should match");
|
|
100
|
+
assert.equal(result!.action, "stop", "should return stop action");
|
|
101
|
+
if (result!.action === "stop") {
|
|
102
|
+
assert.equal(result!.level, "warning", "should be warning level (pausable)");
|
|
103
|
+
assert.ok(
|
|
104
|
+
result!.reason.includes("needs-attention"),
|
|
105
|
+
"reason should mention needs-attention",
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
} finally {
|
|
109
|
+
rmSync(base, { recursive: true, force: true });
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
69
113
|
test("completing-milestone proceeds normally when VALIDATION verdict is pass (#2675 guard)", async () => {
|
|
70
114
|
const base = mkdtempSync(join(tmpdir(), "gsd-remediation-"));
|
|
71
115
|
mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
|
|
@@ -5,6 +5,7 @@ import test from "node:test";
|
|
|
5
5
|
import assert from "node:assert/strict";
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
+
_hasEmptyAgentEndContent,
|
|
8
9
|
_handleSessionSwitchAgentEnd,
|
|
9
10
|
isBareClaudeCodeStreamAbortPlaceholder,
|
|
10
11
|
isClaudeCodeSessionSwitchAbortMessage,
|
|
@@ -188,6 +189,15 @@ test("empty-content aborted during session-switch is silently ignored", () => {
|
|
|
188
189
|
assert.equal(cancelledWith, null);
|
|
189
190
|
});
|
|
190
191
|
|
|
192
|
+
test("missing agent_end content is classified as empty abort content", () => {
|
|
193
|
+
// Providers may omit content entirely for a late aborted agent_end. That is
|
|
194
|
+
// equivalent to empty content and must not pause/cancel the next unit.
|
|
195
|
+
assert.equal(_hasEmptyAgentEndContent(undefined), true);
|
|
196
|
+
assert.equal(_hasEmptyAgentEndContent(null), true);
|
|
197
|
+
assert.equal(_hasEmptyAgentEndContent([]), true);
|
|
198
|
+
assert.equal(_hasEmptyAgentEndContent([{ type: "text", text: "partial" }]), false);
|
|
199
|
+
});
|
|
200
|
+
|
|
191
201
|
test("completed assistant content with aborted stopReason during session-switch is ignored", () => {
|
|
192
202
|
// newSession() can abort the just-finished provider stream while the last
|
|
193
203
|
// assistant message still carries the completed unit summary. That is a
|
|
@@ -99,19 +99,49 @@ test("auto bootstrap validates blocked directories before touching .gsd migratio
|
|
|
99
99
|
|
|
100
100
|
test("fresh start registers the auto worker before bootstrap enters worktree flow (#5405)", () => {
|
|
101
101
|
const autoSrc = readGsdFile("auto.ts");
|
|
102
|
+
const autoStartSrc = readGsdFile("auto-start.ts");
|
|
102
103
|
const startAutoIdx = autoSrc.indexOf("export async function startAuto(");
|
|
103
104
|
const startAutoBody = autoSrc.slice(startAutoIdx);
|
|
105
|
+
const bootstrapIdx = autoStartSrc.indexOf("export async function bootstrapAutoSession(");
|
|
106
|
+
const bootstrapBody = autoStartSrc.slice(bootstrapIdx);
|
|
104
107
|
|
|
105
|
-
const preBootstrapRegisterIdx = startAutoBody.indexOf("registerAutoWorkerForSession(s, base);");
|
|
106
108
|
const bootstrapCallIdx = startAutoBody.indexOf("const ready = await bootstrapAutoSession(");
|
|
109
|
+
const preBootstrapBody = startAutoBody.slice(0, bootstrapCallIdx);
|
|
110
|
+
const preBootstrapRegisterIdx = preBootstrapBody.lastIndexOf("registerAutoWorkerForSession(s, base);");
|
|
111
|
+
const resumeSectionIdx = startAutoBody.indexOf("if (s.paused) {");
|
|
112
|
+
const freshStartSectionIdx = startAutoBody.indexOf("// ── Fresh start path — delegated to auto-start.ts ──");
|
|
113
|
+
const resumeBody = startAutoBody.slice(resumeSectionIdx, freshStartSectionIdx);
|
|
114
|
+
const resumeDbOpenIdx = resumeBody.indexOf("await openProjectDbIfPresent(base);");
|
|
115
|
+
const resumeRegisterIdx = resumeBody.indexOf("registerAutoWorkerForSession(s, base);");
|
|
116
|
+
const resumeEnterMilestoneIdx = resumeBody.indexOf("buildLifecycle().enterMilestone");
|
|
117
|
+
const dbOpenIdx = bootstrapBody.indexOf("await openProjectDbIfPresent(base);");
|
|
118
|
+
const bootstrapRegisterIdx = bootstrapBody.indexOf("registerAutoWorkerForSession(base);");
|
|
119
|
+
const enterMilestoneIdx = bootstrapBody.indexOf("buildLifecycle().enterMilestone");
|
|
107
120
|
|
|
108
121
|
assert.ok(startAutoIdx > -1, "startAuto should exist");
|
|
109
122
|
assert.ok(preBootstrapRegisterIdx > -1, "startAuto should register worker before bootstrap");
|
|
110
123
|
assert.ok(bootstrapCallIdx > -1, "startAuto should call bootstrapAutoSession");
|
|
124
|
+
assert.ok(resumeSectionIdx > -1, "startAuto should have resume milestone entry flow");
|
|
125
|
+
assert.ok(freshStartSectionIdx > resumeSectionIdx, "resume assertions should be scoped before fresh start");
|
|
126
|
+
assert.ok(resumeDbOpenIdx > -1, "resume should open DB before milestone entry");
|
|
127
|
+
assert.ok(resumeRegisterIdx > -1, "resume should register worker before milestone entry");
|
|
128
|
+
assert.ok(resumeEnterMilestoneIdx > -1, "resume should enter milestones through lifecycle");
|
|
129
|
+
assert.ok(bootstrapIdx > -1, "bootstrapAutoSession should exist");
|
|
130
|
+
assert.ok(dbOpenIdx > -1, "bootstrap should open the project DB");
|
|
131
|
+
assert.ok(bootstrapRegisterIdx > -1, "bootstrap should register worker after DB open");
|
|
132
|
+
assert.ok(enterMilestoneIdx > -1, "bootstrap should enter milestones through lifecycle");
|
|
111
133
|
assert.ok(
|
|
112
134
|
preBootstrapRegisterIdx < bootstrapCallIdx,
|
|
113
135
|
"worker registration must happen before bootstrap so enterMilestone can claim milestone leases on first entry",
|
|
114
136
|
);
|
|
137
|
+
assert.ok(
|
|
138
|
+
dbOpenIdx < bootstrapRegisterIdx && bootstrapRegisterIdx < enterMilestoneIdx,
|
|
139
|
+
"bootstrap must open DB and register worker before first enterMilestone",
|
|
140
|
+
);
|
|
141
|
+
assert.ok(
|
|
142
|
+
resumeDbOpenIdx < resumeRegisterIdx && resumeRegisterIdx < resumeEnterMilestoneIdx,
|
|
143
|
+
"resume must open DB and register worker before first enterMilestone",
|
|
144
|
+
);
|
|
115
145
|
});
|
|
116
146
|
|
|
117
147
|
test("startAutoDetached reports failures asynchronously (#3733)", () => {
|
|
@@ -271,6 +271,11 @@ describe("#2945 Bug 3: mergeAndExit must teardown worktree after successful merg
|
|
|
271
271
|
// a real git fixture and verify the worktree directory is removed
|
|
272
272
|
// from disk after the merge.
|
|
273
273
|
const tmpBase = realpathSync(mkdtempSync(join(tmpdir(), "gsd-2945-bug3-")));
|
|
274
|
+
// ADR-016 phase 3 (#5693): Lifecycle.restoreToProjectRoot now chdirs to
|
|
275
|
+
// s.originalBasePath. Save cwd before the test so we can restore it
|
|
276
|
+
// before rmSync removes tmpBase — otherwise the next test in this file
|
|
277
|
+
// inherits a deleted cwd and process.cwd() throws ENOENT (uv_cwd).
|
|
278
|
+
const prevCwd = process.cwd();
|
|
274
279
|
try {
|
|
275
280
|
const git = (args: string[]): void => {
|
|
276
281
|
execFileSync("git", args, { cwd: tmpBase, stdio: "pipe" });
|
|
@@ -325,6 +330,7 @@ describe("#2945 Bug 3: mergeAndExit must teardown worktree after successful merg
|
|
|
325
330
|
`teardownAutoWorktree must be called after successful merge — worktree directory at ${wt} should be removed`,
|
|
326
331
|
);
|
|
327
332
|
} finally {
|
|
333
|
+
try { process.chdir(prevCwd); } catch { /* noop */ }
|
|
328
334
|
try { rmSync(tmpBase, { recursive: true, force: true }); } catch { /* noop */ }
|
|
329
335
|
}
|
|
330
336
|
});
|
|
@@ -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
|
|