gsd-pi 2.82.0-dev.c22380fc3 → 2.82.0-dev.e7a7f1ed5
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 +5 -4
- 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/claude-code-cli/stream-adapter.js +1 -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 +11 -0
- package/dist/resources/extensions/gsd/auto/phases.js +81 -31
- package/dist/resources/extensions/gsd/auto/workflow-memory-pressure.js +12 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +66 -1
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +1 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +18 -17
- 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 +71 -14
- package/dist/resources/extensions/gsd/auto-start.js +87 -14
- package/dist/resources/extensions/gsd/auto-verification.js +45 -26
- package/dist/resources/extensions/gsd/auto-worktree.js +176 -10
- package/dist/resources/extensions/gsd/auto.js +37 -5
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +31 -7
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +9 -8
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +4 -2
- 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/commands-prefs-wizard.js +7 -2
- package/dist/resources/extensions/gsd/crash-recovery.js +43 -5
- package/dist/resources/extensions/gsd/db/milestone-leases.js +24 -0
- 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-git-checks.js +46 -1
- 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 +45 -3
- package/dist/resources/extensions/gsd/gsd-db.js +21 -6
- package/dist/resources/extensions/gsd/guided-flow-queue.js +4 -3
- package/dist/resources/extensions/gsd/guided-flow.js +101 -116
- package/dist/resources/extensions/gsd/guided-unit-context.js +23 -0
- 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/pending-auto-start.js +52 -0
- 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/queue-reorder-ui.js +30 -13
- package/dist/resources/extensions/gsd/smart-entry-routing.js +36 -0
- 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 +11 -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 +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-mcp.js +17 -1
- 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/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 +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/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/browse-directories/route.js +1 -1
- 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/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/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/google-gemini-cli.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/google-gemini-cli.js +5 -0
- package/packages/pi-ai/dist/providers/google-gemini-cli.js.map +1 -1
- package/packages/pi-ai/dist/providers/google-gemini-cli.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/google-gemini-cli.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/google-gemini-cli.test.js +41 -0
- package/packages/pi-ai/dist/providers/google-gemini-cli.test.js.map +1 -0
- 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/google-gemini-cli.test.ts +49 -0
- package/packages/pi-ai/src/providers/google-gemini-cli.ts +7 -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/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +24 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.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/src/modes/interactive/components/footer.ts +23 -7
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/__tests__/terminal.test.d.ts +2 -0
- package/packages/pi-tui/dist/__tests__/terminal.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/__tests__/terminal.test.js +103 -0
- package/packages/pi-tui/dist/__tests__/terminal.test.js.map +1 -0
- package/packages/pi-tui/dist/terminal.d.ts +2 -0
- package/packages/pi-tui/dist/terminal.d.ts.map +1 -1
- package/packages/pi-tui/dist/terminal.js +12 -0
- package/packages/pi-tui/dist/terminal.js.map +1 -1
- package/packages/pi-tui/src/__tests__/terminal.test.ts +121 -0
- package/packages/pi-tui/src/terminal.ts +11 -0
- package/packages/pi-tui/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/stream-adapter.ts +1 -1
- package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +19 -2
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +9 -0
- package/src/resources/extensions/cmux/index.ts +6 -0
- 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 +90 -38
- package/src/resources/extensions/gsd/auto/workflow-memory-pressure.ts +13 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +72 -1
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +1 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +19 -17
- 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 +74 -11
- package/src/resources/extensions/gsd/auto-start.ts +94 -12
- package/src/resources/extensions/gsd/auto-verification.ts +58 -36
- package/src/resources/extensions/gsd/auto-worktree.ts +193 -10
- package/src/resources/extensions/gsd/auto.ts +40 -5
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +42 -7
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +9 -8
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +4 -2
- 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/commands-prefs-wizard.ts +8 -3
- package/src/resources/extensions/gsd/crash-recovery.ts +44 -4
- package/src/resources/extensions/gsd/db/milestone-leases.ts +26 -0
- 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-git-checks.ts +45 -1
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +25 -13
- package/src/resources/extensions/gsd/doctor-types.ts +1 -0
- 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 +51 -4
- package/src/resources/extensions/gsd/gsd-db.ts +21 -6
- package/src/resources/extensions/gsd/guided-flow-queue.ts +4 -3
- package/src/resources/extensions/gsd/guided-flow.ts +134 -133
- package/src/resources/extensions/gsd/guided-unit-context.ts +30 -0
- 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/pending-auto-start.ts +79 -0
- 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/queue-reorder-ui.ts +31 -13
- package/src/resources/extensions/gsd/smart-entry-routing.ts +77 -0
- 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 +13 -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-dashboard.test.ts +71 -0
- 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 +56 -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 +35 -7
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +53 -2
- 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 +91 -6
- package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/auto-stop-notification.test.ts +20 -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/checkout-branch-stash-guard.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +107 -2
- package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +11 -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 +86 -2
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +2 -0
- 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 +66 -0
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/doctor-empty-worktree.test.ts +65 -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/gsd-db.test.ts +11 -0
- package/src/resources/extensions/gsd/tests/guided-discuss-project-prompt-rendering.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +106 -0
- package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +59 -11
- package/src/resources/extensions/gsd/tests/guided-flow.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/guided-tool-contract.test.ts +65 -0
- package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +7 -7
- 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 +112 -1
- package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +179 -0
- 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/pending-autostart-scope.test.ts +29 -5
- 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/prefs-wizard-coverage.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/prompt-loader.test.ts +23 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +37 -1
- package/src/resources/extensions/gsd/tests/queue-reorder-ui.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +89 -2
- package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +2 -3
- package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/smart-entry-routing.test.ts +113 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +53 -2
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +119 -23
- package/src/resources/extensions/gsd/tests/status-guards.test.ts +13 -1
- 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/validate-milestone-stuck-guard.test.ts +29 -2
- 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 +19 -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 +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/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-mcp.ts +18 -1
- 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/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/{Wop3A7KRGyR06H3rla_1- → 4dSwdrs__8NwCZggxP9KF}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{Wop3A7KRGyR06H3rla_1- → 4dSwdrs__8NwCZggxP9KF}/_ssgManifest.js +0 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { existsSync, rmSync } from "node:fs";
|
|
2
|
+
import { join, relative } from "node:path";
|
|
1
3
|
import { clearParseCache } from "../files.js";
|
|
2
4
|
import { isClosedStatus, isDeferredStatus } from "../status-guards.js";
|
|
3
|
-
import { isNonEmptyString } from "../validation.js";
|
|
4
|
-
import { transaction, getMilestone, getSlice, insertTask, upsertSlicePlanning, upsertTaskPlanning, insertGateRow, updateSliceStatus, } from "../gsd-db.js";
|
|
5
|
+
import { isNonEmptyString, validateStringArray } from "../validation.js";
|
|
6
|
+
import { transaction, getMilestone, getSlice, getSliceTasks, insertTask, upsertSlicePlanning, upsertTaskPlanning, insertGateRow, updateSliceStatus, setSliceSketchFlag, deleteTask, deleteArtifactByPath, } from "../gsd-db.js";
|
|
5
7
|
import { invalidateStateCache } from "../state.js";
|
|
6
8
|
import { renderPlanFromDb } from "../markdown-renderer.js";
|
|
7
9
|
import { renderAllProjections } from "../workflow-projections.js";
|
|
@@ -9,6 +11,8 @@ import { writeManifest } from "../workflow-manifest.js";
|
|
|
9
11
|
import { appendEvent } from "../workflow-events.js";
|
|
10
12
|
import { logWarning } from "../workflow-logger.js";
|
|
11
13
|
import { validatePlanningPathScope } from "../planning-path-scope.js";
|
|
14
|
+
import { checkFilePathConsistency, checkTaskOrdering } from "../pre-execution-checks.js";
|
|
15
|
+
import { buildTaskFileName, gsdRoot, resolveTasksDir } from "../paths.js";
|
|
12
16
|
function validateTasks(value) {
|
|
13
17
|
if (!Array.isArray(value) || value.length === 0) {
|
|
14
18
|
throw new Error("tasks must be a non-empty array");
|
|
@@ -39,17 +43,11 @@ function validateTasks(value) {
|
|
|
39
43
|
throw new Error(`tasks[${index}].description must be a non-empty string`);
|
|
40
44
|
if (!isNonEmptyString(estimate))
|
|
41
45
|
throw new Error(`tasks[${index}].estimate must be a non-empty string`);
|
|
42
|
-
|
|
43
|
-
throw new Error(`tasks[${index}].files must be an array of non-empty strings`);
|
|
44
|
-
}
|
|
46
|
+
const validatedFiles = validateStringArray(files, `tasks[${index}].files`);
|
|
45
47
|
if (!isNonEmptyString(verify))
|
|
46
48
|
throw new Error(`tasks[${index}].verify must be a non-empty string`);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
if (!Array.isArray(expectedOutput) || expectedOutput.some((item) => !isNonEmptyString(item))) {
|
|
51
|
-
throw new Error(`tasks[${index}].expectedOutput must be an array of non-empty strings`);
|
|
52
|
-
}
|
|
49
|
+
const validatedInputs = validateStringArray(inputs, `tasks[${index}].inputs`);
|
|
50
|
+
const validatedExpectedOutput = validateStringArray(expectedOutput, `tasks[${index}].expectedOutput`);
|
|
53
51
|
if (observabilityImpact !== undefined && !isNonEmptyString(observabilityImpact)) {
|
|
54
52
|
throw new Error(`tasks[${index}].observabilityImpact must be a non-empty string when provided`);
|
|
55
53
|
}
|
|
@@ -58,10 +56,10 @@ function validateTasks(value) {
|
|
|
58
56
|
title,
|
|
59
57
|
description,
|
|
60
58
|
estimate,
|
|
61
|
-
files,
|
|
59
|
+
files: validatedFiles,
|
|
62
60
|
verify,
|
|
63
|
-
inputs,
|
|
64
|
-
expectedOutput,
|
|
61
|
+
inputs: validatedInputs,
|
|
62
|
+
expectedOutput: validatedExpectedOutput,
|
|
65
63
|
observabilityImpact: typeof observabilityImpact === "string" ? observabilityImpact : "",
|
|
66
64
|
};
|
|
67
65
|
});
|
|
@@ -84,6 +82,53 @@ function validateParams(params) {
|
|
|
84
82
|
tasks: validateTasks(params.tasks),
|
|
85
83
|
};
|
|
86
84
|
}
|
|
85
|
+
function toTaskRows(params) {
|
|
86
|
+
return params.tasks.map((task, index) => ({
|
|
87
|
+
milestone_id: params.milestoneId,
|
|
88
|
+
slice_id: params.sliceId,
|
|
89
|
+
id: task.taskId,
|
|
90
|
+
title: task.title,
|
|
91
|
+
status: "pending",
|
|
92
|
+
one_liner: "",
|
|
93
|
+
narrative: "",
|
|
94
|
+
verification_result: "",
|
|
95
|
+
duration: "",
|
|
96
|
+
completed_at: null,
|
|
97
|
+
blocker_discovered: false,
|
|
98
|
+
deviations: "",
|
|
99
|
+
known_issues: "",
|
|
100
|
+
key_files: [],
|
|
101
|
+
key_decisions: [],
|
|
102
|
+
full_summary_md: "",
|
|
103
|
+
description: task.description,
|
|
104
|
+
estimate: task.estimate,
|
|
105
|
+
files: task.files,
|
|
106
|
+
verify: task.verify,
|
|
107
|
+
inputs: task.inputs,
|
|
108
|
+
expected_output: task.expectedOutput,
|
|
109
|
+
observability_impact: task.observabilityImpact ?? "",
|
|
110
|
+
full_plan_md: task.fullPlanMd ?? "",
|
|
111
|
+
sequence: index + 1,
|
|
112
|
+
blocker_source: "",
|
|
113
|
+
escalation_pending: 0,
|
|
114
|
+
escalation_awaiting_review: 0,
|
|
115
|
+
escalation_artifact_path: null,
|
|
116
|
+
escalation_override_applied_at: null,
|
|
117
|
+
}));
|
|
118
|
+
}
|
|
119
|
+
function validateTaskPathsBeforePersist(params, basePath) {
|
|
120
|
+
const taskRows = toTaskRows(params);
|
|
121
|
+
const checks = [
|
|
122
|
+
...checkFilePathConsistency(taskRows, basePath),
|
|
123
|
+
...checkTaskOrdering(taskRows, basePath),
|
|
124
|
+
];
|
|
125
|
+
const blocking = checks.filter((check) => !check.passed && check.blocking);
|
|
126
|
+
if (blocking.length === 0)
|
|
127
|
+
return null;
|
|
128
|
+
return blocking
|
|
129
|
+
.map((check) => `[${check.category}] ${check.target}: ${check.message}`)
|
|
130
|
+
.join("\n");
|
|
131
|
+
}
|
|
87
132
|
export async function handlePlanSlice(rawParams, basePath) {
|
|
88
133
|
let params;
|
|
89
134
|
try {
|
|
@@ -100,10 +145,15 @@ export async function handlePlanSlice(rawParams, basePath) {
|
|
|
100
145
|
if (pathScopeError) {
|
|
101
146
|
return { error: `validation failed: ${pathScopeError}` };
|
|
102
147
|
}
|
|
148
|
+
const pathError = validateTaskPathsBeforePersist(params, basePath);
|
|
149
|
+
if (pathError) {
|
|
150
|
+
return { error: `pre-execution validation failed:\n${pathError}` };
|
|
151
|
+
}
|
|
103
152
|
// ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
|
|
104
153
|
// Guards must be inside the transaction so the state they check cannot
|
|
105
154
|
// change between the read and the write (#2723).
|
|
106
155
|
let guardError = null;
|
|
156
|
+
let omittedTaskIds = [];
|
|
107
157
|
try {
|
|
108
158
|
transaction(() => {
|
|
109
159
|
const parentMilestone = getMilestone(params.milestoneId);
|
|
@@ -124,9 +174,21 @@ export async function handlePlanSlice(rawParams, basePath) {
|
|
|
124
174
|
guardError = `cannot re-plan slice ${params.sliceId}: it is already complete — use gsd_slice_reopen first`;
|
|
125
175
|
return;
|
|
126
176
|
}
|
|
177
|
+
const newTaskIds = new Set(params.tasks.map((task) => task.taskId));
|
|
178
|
+
const existingTasks = getSliceTasks(params.milestoneId, params.sliceId);
|
|
179
|
+
omittedTaskIds = existingTasks
|
|
180
|
+
.filter((task) => !newTaskIds.has(task.id))
|
|
181
|
+
.map((task) => task.id);
|
|
182
|
+
for (const task of existingTasks) {
|
|
183
|
+
if (!newTaskIds.has(task.id) && isClosedStatus(task.status)) {
|
|
184
|
+
guardError = `cannot remove completed task ${task.id}`;
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
127
188
|
if (isDeferredStatus(parentSlice.status)) {
|
|
128
189
|
updateSliceStatus(params.milestoneId, params.sliceId, "pending");
|
|
129
190
|
}
|
|
191
|
+
setSliceSketchFlag(params.milestoneId, params.sliceId, false);
|
|
130
192
|
upsertSlicePlanning(params.milestoneId, params.sliceId, {
|
|
131
193
|
goal: params.goal,
|
|
132
194
|
successCriteria: params.successCriteria,
|
|
@@ -134,6 +196,9 @@ export async function handlePlanSlice(rawParams, basePath) {
|
|
|
134
196
|
integrationClosure: params.integrationClosure,
|
|
135
197
|
observabilityImpact: params.observabilityImpact,
|
|
136
198
|
});
|
|
199
|
+
for (const taskId of omittedTaskIds) {
|
|
200
|
+
deleteTask(params.milestoneId, params.sliceId, taskId);
|
|
201
|
+
}
|
|
137
202
|
for (const task of params.tasks) {
|
|
138
203
|
insertTask({
|
|
139
204
|
id: task.taskId,
|
|
@@ -176,6 +241,16 @@ export async function handlePlanSlice(rawParams, basePath) {
|
|
|
176
241
|
return { error: guardError };
|
|
177
242
|
}
|
|
178
243
|
try {
|
|
244
|
+
const tasksDir = resolveTasksDir(basePath, params.milestoneId, params.sliceId);
|
|
245
|
+
for (const taskId of omittedTaskIds) {
|
|
246
|
+
if (!tasksDir)
|
|
247
|
+
continue;
|
|
248
|
+
const taskPlanPath = join(tasksDir, buildTaskFileName(taskId, "PLAN"));
|
|
249
|
+
if (existsSync(taskPlanPath))
|
|
250
|
+
rmSync(taskPlanPath, { force: true });
|
|
251
|
+
const artifactPath = relative(gsdRoot(basePath), taskPlanPath).replace(/\\/g, "/");
|
|
252
|
+
deleteArtifactByPath(artifactPath);
|
|
253
|
+
}
|
|
179
254
|
const renderResult = await renderPlanFromDb(basePath, params.milestoneId, params.sliceId);
|
|
180
255
|
invalidateStateCache();
|
|
181
256
|
clearParseCache();
|
|
@@ -73,6 +73,7 @@ const COMMON_BUDGET_SMALL = 250_000; // ~65K tokens
|
|
|
73
73
|
// allowed-path set for the docs policy lives in one reviewable place.
|
|
74
74
|
const TOOLS_ALL = { mode: "all" };
|
|
75
75
|
const TOOLS_PLANNING = { mode: "planning" };
|
|
76
|
+
const TOOLS_VERIFICATION = { mode: "verification" };
|
|
76
77
|
// Like TOOLS_PLANNING but permits dispatch to read-only recon/planning
|
|
77
78
|
// specialists. Runtime-enforced by write-gate.ts before the subagent tool runs.
|
|
78
79
|
const TOOLS_PLANNING_DISPATCH_RECON = {
|
|
@@ -187,8 +188,8 @@ export const UNIT_MANIFESTS = {
|
|
|
187
188
|
contextMode: "verification",
|
|
188
189
|
// planning-dispatch: validation is a verification-fan-out unit. It reads
|
|
189
190
|
// the milestone surface and dispatches reviewer/security/tester subagents
|
|
190
|
-
// to report findings without touching user source.
|
|
191
|
-
//
|
|
191
|
+
// to report findings without touching user source. Write isolation to
|
|
192
|
+
// .gsd/ is preserved.
|
|
192
193
|
tools: TOOLS_PLANNING_DISPATCH_REVIEW,
|
|
193
194
|
artifacts: {
|
|
194
195
|
inline: ["roadmap", "slice-summary", "slice-uat", "requirements", "decisions", "templates"],
|
|
@@ -204,11 +205,9 @@ export const UNIT_MANIFESTS = {
|
|
|
204
205
|
codebaseMap: false,
|
|
205
206
|
preferences: "active-only",
|
|
206
207
|
contextMode: "verification",
|
|
207
|
-
//
|
|
208
|
-
//
|
|
209
|
-
|
|
210
|
-
// preserved.
|
|
211
|
-
tools: TOOLS_PLANNING_DISPATCH_REVIEW,
|
|
208
|
+
// Milestone closeout must run unrestricted shell verification commands
|
|
209
|
+
// against the final diff before recording completion.
|
|
210
|
+
tools: TOOLS_ALL,
|
|
212
211
|
artifacts: {
|
|
213
212
|
// #4780 landed slice-summary as excerpt for this unit; phase 2 of
|
|
214
213
|
// the architecture will read this manifest as the source of truth
|
|
@@ -227,7 +226,9 @@ export const UNIT_MANIFESTS = {
|
|
|
227
226
|
codebaseMap: true,
|
|
228
227
|
preferences: "active-only",
|
|
229
228
|
contextMode: "research",
|
|
230
|
-
|
|
229
|
+
// Multi-slice research dispatches use the research-slice unit contract to
|
|
230
|
+
// fan out scout subagents that write .gsd research artifacts.
|
|
231
|
+
tools: TOOLS_PLANNING_DISPATCH_RECON,
|
|
231
232
|
artifacts: {
|
|
232
233
|
inline: ["roadmap", "milestone-research", "dependency-summaries", "templates"],
|
|
233
234
|
excerpt: [],
|
|
@@ -365,7 +366,7 @@ export const UNIT_MANIFESTS = {
|
|
|
365
366
|
codebaseMap: false,
|
|
366
367
|
preferences: "active-only",
|
|
367
368
|
contextMode: "verification",
|
|
368
|
-
tools:
|
|
369
|
+
tools: TOOLS_VERIFICATION,
|
|
369
370
|
artifacts: {
|
|
370
371
|
// Phase 3 migration (#4782): manifest matches today's actual
|
|
371
372
|
// buildRunUatPrompt inlining. Prior phase-1 entry listed
|
|
@@ -384,7 +385,9 @@ export const UNIT_MANIFESTS = {
|
|
|
384
385
|
codebaseMap: false,
|
|
385
386
|
preferences: "active-only",
|
|
386
387
|
contextMode: "verification",
|
|
387
|
-
|
|
388
|
+
// Gate evaluation fans out tester-style subagents, which read the slice
|
|
389
|
+
// plan and report via the DB-backed gate-result tool.
|
|
390
|
+
tools: TOOLS_PLANNING_DISPATCH_REVIEW,
|
|
388
391
|
artifacts: {
|
|
389
392
|
inline: ["slice-plan", "prior-task-summaries"],
|
|
390
393
|
excerpt: [],
|
|
@@ -509,3 +512,22 @@ export const UNIT_MANIFESTS = {
|
|
|
509
512
|
export function resolveManifest(unitType) {
|
|
510
513
|
return UNIT_MANIFESTS[unitType] ?? null;
|
|
511
514
|
}
|
|
515
|
+
export function compileSubagentPermissionContract(policy) {
|
|
516
|
+
if (!policy) {
|
|
517
|
+
return { allowed: false, allowedSubagents: [], toolsMode: "unknown" };
|
|
518
|
+
}
|
|
519
|
+
if (policy.mode === "all") {
|
|
520
|
+
return { allowed: true, allowedSubagents: ["*"], toolsMode: policy.mode };
|
|
521
|
+
}
|
|
522
|
+
if (policy.mode === "planning-dispatch") {
|
|
523
|
+
return {
|
|
524
|
+
allowed: true,
|
|
525
|
+
allowedSubagents: [...policy.allowedSubagents],
|
|
526
|
+
toolsMode: policy.mode,
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
return { allowed: false, allowedSubagents: [], toolsMode: policy.mode };
|
|
530
|
+
}
|
|
531
|
+
export function resolveSubagentPermissionContract(unitType) {
|
|
532
|
+
return compileSubagentPermissionContract(resolveManifest(unitType)?.tools);
|
|
533
|
+
}
|
|
@@ -5,6 +5,27 @@
|
|
|
5
5
|
export function isNonEmptyString(value) {
|
|
6
6
|
return typeof value === "string" && value.trim().length > 0;
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Characters that are used as delimiters in GSD state management documents
|
|
10
|
+
* and should not appear in milestone or slice titles.
|
|
11
|
+
*/
|
|
12
|
+
const TITLE_DELIMITER_RE = /[\u2014\u2013\/]/; // em dash, en dash, forward slash
|
|
13
|
+
/**
|
|
14
|
+
* Check whether a milestone or slice title contains characters that conflict
|
|
15
|
+
* with GSD's state document delimiter conventions.
|
|
16
|
+
* Returns a human-readable description of the problem, or null if the title is safe.
|
|
17
|
+
*/
|
|
18
|
+
export function validateTitle(title) {
|
|
19
|
+
if (TITLE_DELIMITER_RE.test(title)) {
|
|
20
|
+
const found = [];
|
|
21
|
+
if (/[\u2014\u2013]/.test(title))
|
|
22
|
+
found.push("em/en dash (\u2014 or \u2013)");
|
|
23
|
+
if (/\//.test(title))
|
|
24
|
+
found.push("forward slash (/)");
|
|
25
|
+
return `title contains ${found.join(" and ")}, which conflict with GSD state document delimiters`;
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
8
29
|
/**
|
|
9
30
|
* Validate that `value` is an array of non-empty strings.
|
|
10
31
|
* Throws with a message referencing `field` on failure.
|
|
@@ -12,7 +33,8 @@ export function isNonEmptyString(value) {
|
|
|
12
33
|
*/
|
|
13
34
|
export function validateStringArray(value, field) {
|
|
14
35
|
if (!Array.isArray(value)) {
|
|
15
|
-
|
|
36
|
+
const received = value === null ? "null" : typeof value;
|
|
37
|
+
throw new Error(`${field} must be an array of strings, not ${received}`);
|
|
16
38
|
}
|
|
17
39
|
if (value.some((item) => !isNonEmptyString(item))) {
|
|
18
40
|
throw new Error(`${field} must contain only non-empty strings`);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Discovery order (D003): preference → task plan verify → package.json scripts.
|
|
4
4
|
// First non-empty source wins.
|
|
5
5
|
import { spawnSync } from "node:child_process";
|
|
6
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
7
7
|
import { join, basename } from "node:path";
|
|
8
8
|
import { DEFAULT_COMMAND_TIMEOUT_MS } from "./constants.js";
|
|
9
9
|
import { rewriteCommandWithRtk } from "../shared/rtk.js";
|
|
@@ -27,7 +27,8 @@ const PACKAGE_SCRIPT_KEYS = ["typecheck", "lint", "test"];
|
|
|
27
27
|
* 1. Explicit preference commands
|
|
28
28
|
* 2. Task plan verify field (split on &&)
|
|
29
29
|
* 3. package.json scripts (typecheck, lint, test)
|
|
30
|
-
* 4.
|
|
30
|
+
* 4. Python pytest project markers
|
|
31
|
+
* 5. None found
|
|
31
32
|
*/
|
|
32
33
|
export function discoverCommands(options) {
|
|
33
34
|
// 1. Preference commands
|
|
@@ -72,9 +73,57 @@ export function discoverCommands(options) {
|
|
|
72
73
|
// Malformed package.json — fall through to "none"
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
|
-
|
|
76
|
+
const pythonCommand = discoverPythonPytestCommand(options.cwd);
|
|
77
|
+
if (pythonCommand) {
|
|
78
|
+
return { commands: [pythonCommand], source: "python-project" };
|
|
79
|
+
}
|
|
80
|
+
// 5. Nothing found
|
|
76
81
|
return { commands: [], source: "none" };
|
|
77
82
|
}
|
|
83
|
+
function discoverPythonPytestCommand(cwd) {
|
|
84
|
+
const hasPythonTestFiles = hasPythonTests(join(cwd, "tests"));
|
|
85
|
+
const hasPytestConfig = existsSync(join(cwd, "pytest.ini"));
|
|
86
|
+
const pyprojectPath = join(cwd, "pyproject.toml");
|
|
87
|
+
const hasPyproject = existsSync(pyprojectPath);
|
|
88
|
+
if (!hasPythonTestFiles && !hasPytestConfig && !hasPyproject) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
if (hasPytestConfig || hasPythonTestFiles) {
|
|
92
|
+
return "python3 -m pytest";
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const pyproject = readFileSync(pyprojectPath, "utf-8");
|
|
96
|
+
if (pyproject.includes("[tool.pytest]") ||
|
|
97
|
+
pyproject.includes("[tool.pytest.") ||
|
|
98
|
+
pyproject.includes("[pytest]") ||
|
|
99
|
+
pyproject.includes("[tool:pytest]")) {
|
|
100
|
+
return "python3 -m pytest";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// Ignore unreadable pyproject.toml and fall through.
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
function hasPythonTests(dir) {
|
|
109
|
+
let entries;
|
|
110
|
+
try {
|
|
111
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
for (const entry of entries) {
|
|
117
|
+
const path = join(dir, entry.name);
|
|
118
|
+
if (entry.isDirectory() && hasPythonTests(path)) {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
if (entry.isFile() && /^test_.*\.py$|^.*_test\.py$/.test(entry.name)) {
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
78
127
|
// ─── Failure Context Formatting ──────────────────────────────────────────────
|
|
79
128
|
/** Maximum chars of stderr to include per failed check in failure context. */
|
|
80
129
|
const MAX_STDERR_PER_CHECK = 2_000;
|
|
@@ -112,7 +161,7 @@ export function formatFailureContext(result) {
|
|
|
112
161
|
}
|
|
113
162
|
// ─── Gate Execution ─────────────────────────────────────────────────────────
|
|
114
163
|
/** Characters that indicate shell injection when found in a command string. */
|
|
115
|
-
const SHELL_INJECTION_PATTERN = /[
|
|
164
|
+
const SHELL_INJECTION_PATTERN = /[;|`<>]|\$\(/;
|
|
116
165
|
/**
|
|
117
166
|
* Known executable first-tokens that are safe to run.
|
|
118
167
|
* Lowercase commands, common build/test tools, and npm/yarn/pnpm invocations.
|
|
@@ -148,6 +197,7 @@ const KNOWN_COMMAND_PREFIXES = new Set([
|
|
|
148
197
|
* Heuristics (any true → prose-like):
|
|
149
198
|
* 1. First token starts with an uppercase letter and the string has 4+ words
|
|
150
199
|
* 2. String contains commas followed by spaces (prose clause structure)
|
|
200
|
+
* 3. First token has no ASCII letters or digits and the string has 4+ words
|
|
151
201
|
*/
|
|
152
202
|
export function isLikelyCommand(cmd) {
|
|
153
203
|
const trimmed = cmd.trim();
|
|
@@ -173,16 +223,27 @@ export function isLikelyCommand(cmd) {
|
|
|
173
223
|
// First token has uppercase letters and no path separators → prose
|
|
174
224
|
if (/[A-Z]/.test(firstToken) && !firstToken.includes("/"))
|
|
175
225
|
return false;
|
|
226
|
+
// Non-ASCII prose with multiple words should not be executed as a command.
|
|
227
|
+
if (!/[A-Za-z0-9]/.test(firstToken) && tokens.length >= 4)
|
|
228
|
+
return false;
|
|
176
229
|
return true;
|
|
177
230
|
}
|
|
178
231
|
/**
|
|
179
232
|
* Validate a command string for obvious shell injection patterns.
|
|
180
233
|
* Returns the command unchanged if safe, or null if suspicious.
|
|
181
234
|
*/
|
|
235
|
+
export function validateVerificationCommand(cmd) {
|
|
236
|
+
if (SHELL_INJECTION_PATTERN.test(cmd)) {
|
|
237
|
+
return { ok: false, reason: "contains shell control syntax such as pipes, redirects, semicolons, backticks, or command substitution" };
|
|
238
|
+
}
|
|
239
|
+
if (!isLikelyCommand(cmd)) {
|
|
240
|
+
return { ok: false, reason: "does not look like a runnable command" };
|
|
241
|
+
}
|
|
242
|
+
return { ok: true };
|
|
243
|
+
}
|
|
182
244
|
function sanitizeCommand(cmd) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if (!isLikelyCommand(cmd))
|
|
245
|
+
const validation = validateVerificationCommand(cmd);
|
|
246
|
+
if (!validation.ok)
|
|
186
247
|
return null;
|
|
187
248
|
return cmd;
|
|
188
249
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Project/App: GSD-2
|
|
2
|
+
// File Purpose: Host-owned verification verdict policy for auto-mode units.
|
|
3
|
+
export function decideVerificationVerdict(unitType, result) {
|
|
4
|
+
if (unitType === "execute-task" && result.discoverySource === "none" && result.checks.length === 0) {
|
|
5
|
+
return {
|
|
6
|
+
passed: false,
|
|
7
|
+
reason: "no-host-checks",
|
|
8
|
+
retryable: false,
|
|
9
|
+
failureContext: "No runnable host-owned verification command was discovered. Add project verification_commands or a runnable task-plan Verify command before completing this execute-task.",
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
if (!result.passed) {
|
|
13
|
+
return {
|
|
14
|
+
passed: false,
|
|
15
|
+
reason: "checks-failed",
|
|
16
|
+
retryable: true,
|
|
17
|
+
failureContext: "",
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
passed: true,
|
|
22
|
+
reason: "passed",
|
|
23
|
+
retryable: false,
|
|
24
|
+
failureContext: "",
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -322,6 +322,16 @@ function hasAskUserQuestionsTool(activeTools) {
|
|
|
322
322
|
return toolSeparator >= 0 && toolName.slice(toolSeparator + 2) === "ask_user_questions";
|
|
323
323
|
});
|
|
324
324
|
}
|
|
325
|
+
function hasRequiredTool(requiredTool, activeTools) {
|
|
326
|
+
return activeTools.some((toolName) => {
|
|
327
|
+
if (toolName === requiredTool)
|
|
328
|
+
return true;
|
|
329
|
+
if (!toolName.startsWith("mcp__"))
|
|
330
|
+
return false;
|
|
331
|
+
const toolSeparator = toolName.indexOf("__", "mcp__".length);
|
|
332
|
+
return toolSeparator >= 0 && toolName.slice(toolSeparator + 2) === requiredTool;
|
|
333
|
+
});
|
|
334
|
+
}
|
|
325
335
|
function workflowMcpStructuredQuestionsOptIn(env = process.env) {
|
|
326
336
|
const value = env.GSD_WORKFLOW_MCP_STRUCTURED_QUESTIONS;
|
|
327
337
|
return value === "1" || value === "true";
|
|
@@ -352,8 +362,14 @@ export function getWorkflowTransportSupportError(provider, requiredTools, option
|
|
|
352
362
|
if (!launch) {
|
|
353
363
|
return `Provider ${providerLabel} cannot run ${surface}${unitLabel}: the GSD workflow MCP server is not configured or discoverable. Detected Claude Code model but no workflow MCP. Please run /gsd mcp init . from your project root. You can also configure GSD_WORKFLOW_MCP_COMMAND, build packages/mcp-server/dist/cli.js, or install gsd-mcp-server on PATH.`;
|
|
354
364
|
}
|
|
355
|
-
const
|
|
365
|
+
const uniqueRequired = [...new Set(requiredTools)];
|
|
366
|
+
const missing = (options.activeTools && options.activeTools.length > 0)
|
|
367
|
+
? uniqueRequired.filter((tool) => !hasRequiredTool(tool, options.activeTools))
|
|
368
|
+
: uniqueRequired.filter((tool) => !MCP_WORKFLOW_TOOL_SURFACE.has(tool));
|
|
356
369
|
if (missing.length === 0)
|
|
357
370
|
return null;
|
|
371
|
+
if (options.activeTools && options.activeTools.length > 0) {
|
|
372
|
+
return `Provider ${providerLabel} cannot run ${surface}${unitLabel}: this unit requires ${missing.join(", ")}, but the active runtime toolset currently exposes only ${options.activeTools.slice().sort().join(", ")}.`;
|
|
373
|
+
}
|
|
358
374
|
return `Provider ${providerLabel} cannot run ${surface}${unitLabel}: this unit requires ${missing.join(", ")}, but the workflow MCP transport currently exposes only ${Array.from(MCP_WORKFLOW_TOOL_SURFACE).sort().join(", ")}.`;
|
|
359
375
|
}
|
|
@@ -150,11 +150,11 @@ export function renderSummaryContent(taskRow, sliceId, milestoneId, evidence) {
|
|
|
150
150
|
}
|
|
151
151
|
// ── Frontmatter (YAML list format, matches parseSummary() expectations) ──
|
|
152
152
|
const keyFilesYaml = taskRow.key_files && taskRow.key_files.length > 0
|
|
153
|
-
? taskRow.key_files.map(f => ` - ${f}`).join("\n")
|
|
154
|
-
: "
|
|
153
|
+
? `\n${taskRow.key_files.map(f => ` - ${f}`).join("\n")}`
|
|
154
|
+
: " []";
|
|
155
155
|
const keyDecisionsYaml = taskRow.key_decisions && taskRow.key_decisions.length > 0
|
|
156
|
-
? taskRow.key_decisions.map(d => ` - ${d}`).join("\n")
|
|
157
|
-
: "
|
|
156
|
+
? `\n${taskRow.key_decisions.map(d => ` - ${d}`).join("\n")}`
|
|
157
|
+
: " []";
|
|
158
158
|
// Derive verification_result from evidence if available
|
|
159
159
|
const evidenceList = evidence ?? [];
|
|
160
160
|
const allPassed = evidenceList.length > 0 &&
|
|
@@ -182,10 +182,8 @@ export function renderSummaryContent(taskRow, sliceId, milestoneId, evidence) {
|
|
|
182
182
|
id: ${taskRow.id}
|
|
183
183
|
parent: ${sliceId}
|
|
184
184
|
milestone: ${milestoneId}
|
|
185
|
-
key_files
|
|
186
|
-
|
|
187
|
-
key_decisions:
|
|
188
|
-
${keyDecisionsYaml}
|
|
185
|
+
key_files:${keyFilesYaml}
|
|
186
|
+
key_decisions:${keyDecisionsYaml}
|
|
189
187
|
duration: ${taskRow.duration || ""}
|
|
190
188
|
verification_result: ${verificationResult}
|
|
191
189
|
completed_at: ${taskRow.completed_at || ""}
|
|
@@ -20,6 +20,7 @@ import { existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
|
20
20
|
import { randomUUID } from "node:crypto";
|
|
21
21
|
import { join } from "node:path";
|
|
22
22
|
import { debugLog } from "./debug-logger.js";
|
|
23
|
+
import { logWarning } from "./workflow-logger.js";
|
|
23
24
|
import { emitJournalEvent } from "./journal.js";
|
|
24
25
|
import { emitWorktreeCreated, emitWorktreeMerged } from "./worktree-telemetry.js";
|
|
25
26
|
import { resolveWorktreeProjectRoot, normalizeWorktreePathForCompare, } from "./worktree-root.js";
|
|
@@ -48,6 +49,11 @@ import { nativeCheckoutBranch } from "./native-git-bridge.js";
|
|
|
48
49
|
// is the only Module that calls them, and they live alongside the Module's
|
|
49
50
|
// other primitives in `auto-worktree.ts`.
|
|
50
51
|
import { autoWorktreeBranch, createAutoWorktree, enterAutoWorktree, enterBranchModeForMilestone, getAutoWorktreePath, isInAutoWorktree, teardownAutoWorktree, } from "./auto-worktree.js";
|
|
52
|
+
const recentWorktreeMergeFailures = new Map();
|
|
53
|
+
const MERGE_FAILURE_DEDUPE_MS = 60_000;
|
|
54
|
+
export function resetRecentWorktreeMergeFailuresForTest() {
|
|
55
|
+
recentWorktreeMergeFailures.clear();
|
|
56
|
+
}
|
|
51
57
|
/**
|
|
52
58
|
* Internal sentinel — thrown by `_mergeBranchMode` when it has already
|
|
53
59
|
* emitted a user-visible error. The outer `mergeAndExit` catches the type
|
|
@@ -208,7 +214,7 @@ export function _enterMilestoneCore(s, deps, milestoneId, ctx) {
|
|
|
208
214
|
}
|
|
209
215
|
// Phase B: claim a milestone lease before any worktree mutation. Two
|
|
210
216
|
// workers cannot enter the same milestone concurrently. Best-effort:
|
|
211
|
-
//
|
|
217
|
+
// warn if no worker registered (single-worker fallback) or skip if DB
|
|
212
218
|
// unavailable; reuse existing lease if we already hold it on this
|
|
213
219
|
// milestone (re-entry within the same session).
|
|
214
220
|
if (s.workerId) {
|
|
@@ -288,6 +294,9 @@ export function _enterMilestoneCore(s, deps, milestoneId, ctx) {
|
|
|
288
294
|
}
|
|
289
295
|
}
|
|
290
296
|
}
|
|
297
|
+
else {
|
|
298
|
+
logWarning("worktree", `enterMilestone(${milestoneId}) ran before auto worker registration; milestone lease was not claimed.`);
|
|
299
|
+
}
|
|
291
300
|
// Resolve the project root for worktree operations via shared helper.
|
|
292
301
|
// Handles the case where originalBasePath is falsy and basePath is itself
|
|
293
302
|
// a worktree path — prevents double-nested worktree paths (#3729).
|
|
@@ -477,6 +486,28 @@ function rebuildGitService(s, deps) {
|
|
|
477
486
|
// GitService cast.
|
|
478
487
|
s.gitService = deps.gitServiceFactory(s.basePath);
|
|
479
488
|
}
|
|
489
|
+
function emitWorktreeMergeFailedOnce(basePath, milestoneId, err) {
|
|
490
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
491
|
+
const errorCategory = err instanceof Error ? err.name : "Error";
|
|
492
|
+
const now = Date.now();
|
|
493
|
+
const key = `${basePath}\0${milestoneId}\0${errorCategory}`;
|
|
494
|
+
const previous = recentWorktreeMergeFailures.get(key);
|
|
495
|
+
if (previous && now - previous < MERGE_FAILURE_DEDUPE_MS)
|
|
496
|
+
return;
|
|
497
|
+
for (const [candidate, ts] of recentWorktreeMergeFailures) {
|
|
498
|
+
if (now - ts >= MERGE_FAILURE_DEDUPE_MS) {
|
|
499
|
+
recentWorktreeMergeFailures.delete(candidate);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
emitJournalEvent(basePath, {
|
|
503
|
+
ts: new Date().toISOString(),
|
|
504
|
+
flowId: randomUUID(),
|
|
505
|
+
seq: 0,
|
|
506
|
+
eventType: "worktree-merge-failed",
|
|
507
|
+
data: { milestoneId, error: msg },
|
|
508
|
+
});
|
|
509
|
+
recentWorktreeMergeFailures.set(key, now);
|
|
510
|
+
}
|
|
480
511
|
// ─── Session-less merge entry (ADR-016 phase 2 / A1) ─────────────────────
|
|
481
512
|
/**
|
|
482
513
|
* Worktree-mode merge body. Session-less — operates on a `MergeContext`.
|
|
@@ -590,13 +621,7 @@ function _mergeWorktreeModeImpl(deps, mctx) {
|
|
|
590
621
|
error: msg,
|
|
591
622
|
fallback: "chdir-to-project-root",
|
|
592
623
|
});
|
|
593
|
-
|
|
594
|
-
ts: new Date().toISOString(),
|
|
595
|
-
flowId: randomUUID(),
|
|
596
|
-
seq: 0,
|
|
597
|
-
eventType: "worktree-merge-failed",
|
|
598
|
-
data: { milestoneId, error: msg },
|
|
599
|
-
});
|
|
624
|
+
emitWorktreeMergeFailedOnce(originalBasePath || worktreeBasePath, milestoneId, err);
|
|
600
625
|
// Surface a clear, actionable error. Worktree and milestone branch
|
|
601
626
|
// are intentionally preserved — nothing has been deleted. User can
|
|
602
627
|
// retry /gsd dispatch complete-milestone or merge manually once the
|