@opengsd/gsd-pi 1.2.0-dev.844675c9 → 1.2.0-dev.b1abb545
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/dist/cli-web-branch.d.ts +2 -0
- package/dist/cli-web-branch.js +9 -2
- package/dist/help-text.js +5 -0
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/ask-user-questions.js +78 -23
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +84 -228
- package/dist/resources/extensions/claude-code-cli/turn-assembler.js +224 -0
- package/dist/resources/extensions/github-sync/templates.js +3 -3
- package/dist/resources/extensions/gsd/artifact-projection.js +14 -0
- package/dist/resources/extensions/gsd/auto/loop.js +74 -56
- package/dist/resources/extensions/gsd/auto/orchestrator.js +109 -11
- package/dist/resources/extensions/gsd/auto/phases.js +28 -3
- package/dist/resources/extensions/gsd/auto/run-unit.js +2 -1
- package/dist/resources/extensions/gsd/auto/session.js +3 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +16 -4
- package/dist/resources/extensions/gsd/auto-dispatch.js +6 -5
- package/dist/resources/extensions/gsd/auto-model-selection.js +8 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +4 -3
- package/dist/resources/extensions/gsd/auto-prompts.js +81 -8
- package/dist/resources/extensions/gsd/auto-recovery.js +48 -49
- package/dist/resources/extensions/gsd/auto-runtime-state.js +14 -0
- package/dist/resources/extensions/gsd/auto-start.js +12 -23
- package/dist/resources/extensions/gsd/auto-timers.js +16 -2
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +32 -0
- package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +4 -29
- package/dist/resources/extensions/gsd/auto-verification.js +7 -7
- package/dist/resources/extensions/gsd/auto-worktree.js +21 -19
- package/dist/resources/extensions/gsd/auto.js +11 -7
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +28 -37
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +11 -37
- package/dist/resources/extensions/gsd/bootstrap/query-tools.js +2 -2
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +100 -138
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +63 -4
- package/dist/resources/extensions/gsd/closeout-consistency-gate.js +21 -4
- package/dist/resources/extensions/gsd/codebase-generator.js +8 -4
- package/dist/resources/extensions/gsd/commands/handlers/auto.js +3 -0
- package/dist/resources/extensions/gsd/commands-handlers.js +20 -0
- package/dist/resources/extensions/gsd/commands-inspect.js +4 -8
- package/dist/resources/extensions/gsd/commands-maintenance.js +61 -41
- package/dist/resources/extensions/gsd/commands-ship.js +2 -2
- package/dist/resources/extensions/gsd/commands-verdict.js +12 -2
- package/dist/resources/extensions/gsd/db-workspace.js +103 -0
- package/dist/resources/extensions/gsd/delegation-policy.js +2 -10
- package/dist/resources/extensions/gsd/discussion-handoff.js +218 -0
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +9 -0
- package/dist/resources/extensions/gsd/doctor.js +16 -9
- package/dist/resources/extensions/gsd/error-classifier.js +1 -1
- package/dist/resources/extensions/gsd/git-conflict-state.js +16 -1
- package/dist/resources/extensions/gsd/gsd-db.js +12 -0
- package/dist/resources/extensions/gsd/guided-flow.js +34 -468
- package/dist/resources/extensions/gsd/guided-unit-completion.js +225 -0
- package/dist/resources/extensions/gsd/markdown-renderer.js +2 -1
- package/dist/resources/extensions/gsd/mcp-filter.js +2 -1
- package/dist/resources/extensions/gsd/mcp-tool-name.js +26 -0
- package/dist/resources/extensions/gsd/md-importer.js +4 -3
- package/dist/resources/extensions/gsd/migrate/safety.js +2 -2
- package/dist/resources/extensions/gsd/migration-auto-check.js +3 -2
- package/dist/resources/extensions/gsd/milestone-closeout-proof.js +72 -0
- package/dist/resources/extensions/gsd/milestone-closeout.js +12 -4
- package/dist/resources/extensions/gsd/milestone-merge-transaction.js +10 -0
- package/dist/resources/extensions/gsd/milestone-planning-persistence.js +156 -0
- package/dist/resources/extensions/gsd/milestone-readiness.js +77 -0
- package/dist/resources/extensions/gsd/milestone-settlement.js +50 -0
- package/dist/resources/extensions/gsd/milestone-validation-evidence.js +73 -0
- package/dist/resources/extensions/gsd/milestone-validation-verdict.js +57 -0
- package/dist/resources/extensions/gsd/parallel-eligibility.js +3 -6
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -2
- package/dist/resources/extensions/gsd/preferences-diagnostics.js +67 -0
- package/dist/resources/extensions/gsd/preferences.js +147 -29
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -0
- package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -0
- package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +3 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +2 -0
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -0
- package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -0
- package/dist/resources/extensions/gsd/prompts/system.md +1 -1
- package/dist/resources/extensions/gsd/provider-payload-policy.js +83 -0
- package/dist/resources/extensions/gsd/pull-request-process.js +13 -0
- package/dist/resources/extensions/gsd/quality-gate-closure.js +109 -0
- package/dist/resources/extensions/gsd/question-transport.js +86 -0
- package/dist/resources/extensions/gsd/roadmap-slices.js +8 -2
- package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +3 -2
- package/dist/resources/extensions/gsd/state.js +13 -5
- package/dist/resources/extensions/gsd/templates/plan.md +7 -0
- package/dist/resources/extensions/gsd/templates/project.md +1 -0
- package/dist/resources/extensions/gsd/templates/roadmap.md +1 -1
- package/dist/resources/extensions/gsd/templates/uat.md +5 -1
- package/dist/resources/extensions/gsd/tool-contract.js +52 -8
- package/dist/resources/extensions/gsd/tool-presentation-plan.js +15 -34
- package/dist/resources/extensions/gsd/tool-surface-snapshot.js +17 -0
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +15 -143
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +39 -0
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +15 -78
- package/dist/resources/extensions/gsd/uat-policy.js +16 -10
- package/dist/resources/extensions/gsd/uat-run.js +9 -14
- package/dist/resources/extensions/gsd/unit-context-composer.js +40 -20
- package/dist/resources/extensions/gsd/unit-runtime.js +3 -2
- package/dist/resources/extensions/gsd/unit-tool-contracts.js +2 -1
- package/dist/resources/extensions/gsd/user-input-boundary.js +23 -0
- package/dist/resources/extensions/gsd/validation-block-guard.js +2 -0
- package/dist/resources/extensions/gsd/web-app-uat.js +80 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +15 -102
- package/dist/resources/extensions/gsd/workflow-reconcile.js +4 -3
- package/dist/resources/extensions/gsd/workflow-tool-surface.js +46 -0
- package/dist/resources/extensions/gsd/workspace-git-guard.js +2 -0
- package/dist/resources/extensions/gsd/worktree-state-projection.js +33 -4
- package/dist/resources/extensions/gsd/worktree-telemetry.js +12 -0
- package/dist/resources/extensions/shared/interview-ui.js +2 -2
- package/dist/resources/shared/claude-runtime-floor.js +182 -0
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/update-cmd.js +20 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
- 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 +8 -8
- 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.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
- package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/2659.b7b129ee6a769448.js +1 -0
- package/dist/web/standalone/.next/static/chunks/2772.bfa657f49f955239.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{3616.4113d484a994e411.js → 3616.3c60753b8ffcbd2e.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/4283.e4873b058df143a1.js +2 -0
- package/dist/web/standalone/.next/static/chunks/5826.a46ecdd1cfe8dabc.js +1 -0
- package/dist/web/standalone/.next/static/chunks/796.cf859a427a2cb2ac.js +10 -0
- package/dist/web/standalone/.next/static/chunks/8785.2e5a118797fb2dd2.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-dda80a1ef5587410.js → webpack-fbea77b5f9953368.js} +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/dist/web-mode.d.ts +2 -0
- package/dist/web-mode.js +20 -8
- package/package.json +2 -1
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts +2 -0
- package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-extensions.js +14 -0
- package/packages/gsd-agent-core/dist/session/agent-session-extensions.js.map +1 -1
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +106 -40
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.js +6 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/dist/server.d.ts +10 -0
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +8 -0
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +41 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +2 -1
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +8 -93
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +35 -120
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/dist/components/input.js +1 -1
- package/packages/pi-tui/dist/components/input.js.map +1 -1
- package/packages/pi-tui/dist/keys.d.ts.map +1 -1
- package/packages/pi-tui/dist/keys.js +39 -30
- package/packages/pi-tui/dist/keys.js.map +1 -1
- package/packages/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
- package/packages/pi-tui/dist/stdin-buffer.js +22 -0
- package/packages/pi-tui/dist/stdin-buffer.js.map +1 -1
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/extensions/ask-user-questions.ts +87 -24
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +108 -281
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +240 -0
- package/src/resources/extensions/claude-code-cli/turn-assembler.ts +287 -0
- package/src/resources/extensions/github-sync/templates.ts +3 -3
- package/src/resources/extensions/github-sync/tests/templates.test.ts +2 -2
- package/src/resources/extensions/gsd/artifact-projection.ts +31 -0
- package/src/resources/extensions/gsd/auto/contracts.ts +32 -2
- package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -0
- package/src/resources/extensions/gsd/auto/loop.ts +83 -61
- package/src/resources/extensions/gsd/auto/orchestrator.ts +125 -12
- package/src/resources/extensions/gsd/auto/phases.ts +35 -3
- package/src/resources/extensions/gsd/auto/run-unit.ts +2 -1
- package/src/resources/extensions/gsd/auto/session.ts +4 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +18 -4
- package/src/resources/extensions/gsd/auto-dispatch.ts +20 -7
- package/src/resources/extensions/gsd/auto-model-selection.ts +8 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +4 -3
- package/src/resources/extensions/gsd/auto-prompts.ts +107 -9
- package/src/resources/extensions/gsd/auto-recovery.ts +50 -50
- package/src/resources/extensions/gsd/auto-runtime-state.ts +26 -0
- package/src/resources/extensions/gsd/auto-start.ts +17 -20
- package/src/resources/extensions/gsd/auto-timers.ts +16 -2
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +35 -0
- package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +9 -30
- package/src/resources/extensions/gsd/auto-verification.ts +7 -8
- package/src/resources/extensions/gsd/auto-worktree.ts +33 -26
- package/src/resources/extensions/gsd/auto.ts +15 -8
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +29 -37
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +10 -37
- package/src/resources/extensions/gsd/bootstrap/query-tools.ts +2 -2
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +116 -151
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +107 -3
- package/src/resources/extensions/gsd/closeout-consistency-gate.ts +27 -5
- package/src/resources/extensions/gsd/codebase-generator.ts +9 -5
- package/src/resources/extensions/gsd/commands/handlers/auto.ts +3 -0
- package/src/resources/extensions/gsd/commands-handlers.ts +18 -0
- package/src/resources/extensions/gsd/commands-inspect.ts +7 -8
- package/src/resources/extensions/gsd/commands-maintenance.ts +74 -40
- package/src/resources/extensions/gsd/commands-ship.ts +2 -2
- package/src/resources/extensions/gsd/commands-verdict.ts +19 -2
- package/src/resources/extensions/gsd/db-workspace.ts +170 -0
- package/src/resources/extensions/gsd/delegation-policy.ts +3 -11
- package/src/resources/extensions/gsd/discussion-handoff.ts +276 -0
- package/src/resources/extensions/gsd/docs/preferences-reference.md +9 -0
- package/src/resources/extensions/gsd/doctor.ts +15 -5
- package/src/resources/extensions/gsd/error-classifier.ts +1 -1
- package/src/resources/extensions/gsd/git-conflict-state.ts +17 -1
- package/src/resources/extensions/gsd/gsd-db.ts +12 -0
- package/src/resources/extensions/gsd/guided-flow.ts +47 -558
- package/src/resources/extensions/gsd/guided-unit-completion.ts +275 -0
- package/src/resources/extensions/gsd/markdown-renderer.ts +2 -1
- package/src/resources/extensions/gsd/mcp-filter.ts +2 -1
- package/src/resources/extensions/gsd/mcp-tool-name.ts +35 -0
- package/src/resources/extensions/gsd/md-importer.ts +3 -3
- package/src/resources/extensions/gsd/migrate/safety.ts +2 -2
- package/src/resources/extensions/gsd/migration-auto-check.ts +2 -2
- package/src/resources/extensions/gsd/milestone-closeout-proof.ts +131 -0
- package/src/resources/extensions/gsd/milestone-closeout.ts +12 -4
- package/src/resources/extensions/gsd/milestone-merge-transaction.ts +47 -0
- package/src/resources/extensions/gsd/milestone-planning-persistence.ts +224 -0
- package/src/resources/extensions/gsd/milestone-readiness.ts +125 -0
- package/src/resources/extensions/gsd/milestone-settlement.ts +81 -0
- package/src/resources/extensions/gsd/milestone-validation-evidence.ts +95 -0
- package/src/resources/extensions/gsd/milestone-validation-verdict.ts +80 -0
- package/src/resources/extensions/gsd/parallel-eligibility.ts +4 -5
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +6 -2
- package/src/resources/extensions/gsd/preferences-diagnostics.ts +98 -0
- package/src/resources/extensions/gsd/preferences-types.ts +16 -0
- package/src/resources/extensions/gsd/preferences.ts +173 -28
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -0
- package/src/resources/extensions/gsd/prompts/execute-task.md +2 -0
- package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +3 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +2 -0
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -0
- package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -0
- package/src/resources/extensions/gsd/prompts/system.md +1 -1
- package/src/resources/extensions/gsd/provider-payload-policy.ts +140 -0
- package/src/resources/extensions/gsd/pull-request-process.ts +41 -0
- package/src/resources/extensions/gsd/quality-gate-closure.ts +140 -0
- package/src/resources/extensions/gsd/question-transport.ts +138 -0
- package/src/resources/extensions/gsd/roadmap-slices.ts +8 -2
- package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +6 -2
- package/src/resources/extensions/gsd/state.ts +15 -5
- package/src/resources/extensions/gsd/templates/plan.md +7 -0
- package/src/resources/extensions/gsd/templates/project.md +1 -0
- package/src/resources/extensions/gsd/templates/roadmap.md +1 -1
- package/src/resources/extensions/gsd/templates/uat.md +5 -1
- package/src/resources/extensions/gsd/tests/ask-user-questions-render.test.ts +92 -0
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +29 -1
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +321 -5
- package/src/resources/extensions/gsd/tests/auto-milestone-target.test.ts +23 -0
- package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +133 -4
- package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +11 -0
- package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +38 -1
- package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +34 -3
- package/src/resources/extensions/gsd/tests/dispatch-run-uat-browser-tools.test.ts +88 -0
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/execute-task-rendering.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +1 -5
- package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +1 -5
- package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +48 -1
- package/src/resources/extensions/gsd/tests/mcp-tool-name.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/milestone-closeout-proof.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/milestone-readiness.test.ts +65 -0
- package/src/resources/extensions/gsd/tests/milestone-validation-evidence.test.ts +41 -0
- package/src/resources/extensions/gsd/tests/milestone-validation-verdict.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/planning-crossval.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/preferences-diagnostics.test.ts +67 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +183 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/provider-payload-policy.test.ts +165 -0
- package/src/resources/extensions/gsd/tests/pull-request-process.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +94 -0
- package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +25 -1
- package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +80 -0
- package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +101 -1
- package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/tool-availability-audit.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +35 -42
- package/src/resources/extensions/gsd/tests/uat-policy.test.ts +23 -0
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +86 -1
- package/src/resources/extensions/gsd/tests/validate-milestone-stuck-guard.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/web-app-uat.test.ts +150 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +126 -9
- package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/worktree-projection-writers.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +79 -0
- package/src/resources/extensions/gsd/tool-contract.ts +86 -8
- package/src/resources/extensions/gsd/tool-presentation-plan.ts +16 -33
- package/src/resources/extensions/gsd/tool-surface-snapshot.ts +47 -0
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +19 -160
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +43 -0
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +25 -84
- package/src/resources/extensions/gsd/uat-policy.ts +19 -10
- package/src/resources/extensions/gsd/uat-run.ts +10 -14
- package/src/resources/extensions/gsd/unit-context-composer.ts +85 -20
- package/src/resources/extensions/gsd/unit-runtime.ts +3 -2
- package/src/resources/extensions/gsd/unit-tool-contracts.ts +2 -1
- package/src/resources/extensions/gsd/user-input-boundary.ts +18 -0
- package/src/resources/extensions/gsd/validation-block-guard.ts +2 -0
- package/src/resources/extensions/gsd/web-app-uat.ts +101 -0
- package/src/resources/extensions/gsd/workflow-mcp.ts +22 -110
- package/src/resources/extensions/gsd/workflow-reconcile.ts +3 -3
- package/src/resources/extensions/gsd/workflow-tool-surface.ts +73 -0
- package/src/resources/extensions/gsd/workspace-git-guard.ts +1 -0
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +7 -16
- package/src/resources/extensions/gsd/worktree-state-projection.ts +55 -7
- package/src/resources/extensions/gsd/worktree-telemetry.ts +16 -0
- package/src/resources/extensions/shared/interview-ui.ts +15 -2
- package/src/resources/shared/claude-runtime-floor.ts +248 -0
- package/dist/web/standalone/.next/static/chunks/2659.feb6499ca863ebfc.js +0 -1
- package/dist/web/standalone/.next/static/chunks/2772.151789db0edea835.js +0 -1
- package/dist/web/standalone/.next/static/chunks/4283.10a065467b5340d8.js +0 -2
- package/dist/web/standalone/.next/static/chunks/5826.960dc4634cc9b0d3.js +0 -1
- package/dist/web/standalone/.next/static/chunks/796.46f811c0fac23aab.js +0 -10
- package/dist/web/standalone/.next/static/chunks/8785.d32f7a61f55c1600.js +0 -1
- /package/dist/web/standalone/.next/static/{Qbr81pQ-pbQXP4bq2VXLv → 3PtrU9qGPEXwNLWkIyiqk}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{Qbr81pQ-pbQXP4bq2VXLv → 3PtrU9qGPEXwNLWkIyiqk}/_ssgManifest.js +0 -0
|
@@ -23,11 +23,12 @@ import {
|
|
|
23
23
|
import { runUnit, shouldDeferUnitFailsafeTimeout } from "../auto/run-unit.js";
|
|
24
24
|
import { scheduleAutoWakeup, _resetAutoWakeupsForTest } from "../auto/schedule-wakeup.js";
|
|
25
25
|
import { writeUnitRuntimeRecord, readUnitRuntimeRecord } from "../unit-runtime.js";
|
|
26
|
-
import { autoLoop } from "../auto/loop.js";
|
|
27
|
-
import { runDispatch, runUnitPhase } from "../auto/phases.js";
|
|
26
|
+
import { autoLoop as rawAutoLoop } from "../auto/loop.js";
|
|
27
|
+
import { runPreDispatch, runDispatch, runUnitPhase } from "../auto/phases.js";
|
|
28
28
|
import { detectStuck } from "../auto/detect-stuck.js";
|
|
29
|
-
import type { UnitResult, AgentEndEvent } from "../auto/types.js";
|
|
29
|
+
import type { UnitResult, AgentEndEvent, LoopState } from "../auto/types.js";
|
|
30
30
|
import type { LoopDeps } from "../auto/loop-deps.js";
|
|
31
|
+
import type { AutoAdvanceResult, AutoOrchestrationModule, AutoStatus, UnitRef } from "../auto/contracts.js";
|
|
31
32
|
import { WorktreeStateProjection } from "../worktree-state-projection.js";
|
|
32
33
|
import { ModelPolicyDispatchBlockedError } from "../auto-model-selection.js";
|
|
33
34
|
import type { SessionLockStatus } from "../session-lock.js";
|
|
@@ -40,6 +41,17 @@ import { SourceObservationStore } from "../source-observations.js";
|
|
|
40
41
|
|
|
41
42
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
42
43
|
|
|
44
|
+
const ORCHESTRATION_MISSING_REASON =
|
|
45
|
+
"Auto Orchestration Module is not wired; cannot dispatch built-in GSD Unit.";
|
|
46
|
+
|
|
47
|
+
type CapturedAutoSideEffects<T> = {
|
|
48
|
+
result: T;
|
|
49
|
+
stopped: boolean;
|
|
50
|
+
stoppedReason?: string;
|
|
51
|
+
paused: boolean;
|
|
52
|
+
pausedReason?: string;
|
|
53
|
+
};
|
|
54
|
+
|
|
43
55
|
function makeEvent(
|
|
44
56
|
messages: unknown[] = [{ role: "assistant" }],
|
|
45
57
|
): AgentEndEvent {
|
|
@@ -67,12 +79,203 @@ async function waitForMicrotasks(
|
|
|
67
79
|
assert.fail(`Timed out waiting for ${label}`);
|
|
68
80
|
}
|
|
69
81
|
|
|
82
|
+
function makeLoopState(): LoopState {
|
|
83
|
+
return {
|
|
84
|
+
recentUnits: [],
|
|
85
|
+
stuckRecoveryAttempts: 0,
|
|
86
|
+
consecutiveFinalizeTimeouts: 0,
|
|
87
|
+
consecutiveDispatchCount: new Map<string, number>(),
|
|
88
|
+
lastDispatchedKey: null,
|
|
89
|
+
lastDispatchPhase: null,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function createLoopTestOrchestration(
|
|
94
|
+
ctx: any,
|
|
95
|
+
pi: any,
|
|
96
|
+
s: any,
|
|
97
|
+
deps: LoopDeps,
|
|
98
|
+
): AutoOrchestrationModule {
|
|
99
|
+
// Production auto.ts wires the real Auto Orchestration Module before entering
|
|
100
|
+
// autoLoop. These loop-mechanics tests keep their LoopDeps fixtures by
|
|
101
|
+
// adapting the old phase helpers to the public orchestration Interface.
|
|
102
|
+
const loopState = makeLoopState();
|
|
103
|
+
const status: AutoStatus = { phase: "running", transitionCount: 0 };
|
|
104
|
+
let iteration = 0;
|
|
105
|
+
let seq = 0;
|
|
106
|
+
|
|
107
|
+
function nextSeq(): number {
|
|
108
|
+
return ++seq;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function clearActiveUnit(): void {
|
|
112
|
+
status.activeUnit = undefined;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function captureAutoSideEffects<T>(
|
|
116
|
+
run: () => Promise<T>,
|
|
117
|
+
): Promise<CapturedAutoSideEffects<T>> {
|
|
118
|
+
const originalStopAuto = deps.stopAuto;
|
|
119
|
+
const originalPauseAuto = deps.pauseAuto;
|
|
120
|
+
let stoppedReason: string | undefined;
|
|
121
|
+
let pausedReason: string | undefined;
|
|
122
|
+
let stopped = false;
|
|
123
|
+
let paused = false;
|
|
124
|
+
|
|
125
|
+
(deps as any).stopAuto = async (...args: Parameters<LoopDeps["stopAuto"]>) => {
|
|
126
|
+
stopped = true;
|
|
127
|
+
stoppedReason = args[2];
|
|
128
|
+
return originalStopAuto(...args);
|
|
129
|
+
};
|
|
130
|
+
(deps as any).pauseAuto = async (...args: Parameters<LoopDeps["pauseAuto"]>) => {
|
|
131
|
+
paused = true;
|
|
132
|
+
const context = args[2] as { message?: string } | undefined;
|
|
133
|
+
pausedReason = context?.message;
|
|
134
|
+
return originalPauseAuto(...args);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
const result = await run();
|
|
139
|
+
return { result, stopped, stoppedReason, paused, pausedReason };
|
|
140
|
+
} finally {
|
|
141
|
+
(deps as any).stopAuto = originalStopAuto;
|
|
142
|
+
(deps as any).pauseAuto = originalPauseAuto;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function resultForBreak(
|
|
147
|
+
reason: string,
|
|
148
|
+
sideEffects: CapturedAutoSideEffects<unknown>,
|
|
149
|
+
): AutoAdvanceResult {
|
|
150
|
+
clearActiveUnit();
|
|
151
|
+
status.phase = sideEffects.paused && !sideEffects.stopped ? "paused" : "stopped";
|
|
152
|
+
status.transitionCount += 1;
|
|
153
|
+
if (sideEffects.paused && !sideEffects.stopped) {
|
|
154
|
+
return {
|
|
155
|
+
kind: "blocked",
|
|
156
|
+
reason: sideEffects.pausedReason ?? reason,
|
|
157
|
+
action: "pause",
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
kind: "stopped",
|
|
162
|
+
reason: sideEffects.stoppedReason ?? reason,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
async start() {
|
|
168
|
+
status.phase = "running";
|
|
169
|
+
status.transitionCount += 1;
|
|
170
|
+
return { kind: "started" };
|
|
171
|
+
},
|
|
172
|
+
async advance() {
|
|
173
|
+
iteration += 1;
|
|
174
|
+
seq = 0;
|
|
175
|
+
const prefs = deps.loadEffectiveGSDPreferences()?.preferences;
|
|
176
|
+
const ic = {
|
|
177
|
+
ctx,
|
|
178
|
+
pi,
|
|
179
|
+
s,
|
|
180
|
+
deps,
|
|
181
|
+
prefs,
|
|
182
|
+
iteration,
|
|
183
|
+
flowId: `loop-test-orchestration-${iteration}`,
|
|
184
|
+
nextSeq,
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const preDispatch = await captureAutoSideEffects(() => runPreDispatch(ic, loopState));
|
|
188
|
+
const preDispatchResult = preDispatch.result;
|
|
189
|
+
if (preDispatchResult.action === "break") {
|
|
190
|
+
return resultForBreak(preDispatchResult.reason, preDispatch);
|
|
191
|
+
}
|
|
192
|
+
if (preDispatchResult.action === "continue") {
|
|
193
|
+
return { kind: "skipped", reason: "pre-dispatch-skip" };
|
|
194
|
+
}
|
|
195
|
+
if (preDispatchResult.action === "retry") {
|
|
196
|
+
return { kind: "paused", reason: preDispatchResult.reason };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const dispatch = await captureAutoSideEffects(() =>
|
|
200
|
+
runDispatch(ic, preDispatchResult.data, loopState),
|
|
201
|
+
);
|
|
202
|
+
if (dispatch.result.action === "break") {
|
|
203
|
+
return resultForBreak(dispatch.result.reason, dispatch);
|
|
204
|
+
}
|
|
205
|
+
if (dispatch.result.action === "continue") {
|
|
206
|
+
return {
|
|
207
|
+
kind: "skipped",
|
|
208
|
+
reason: "dispatch-skip",
|
|
209
|
+
stateSnapshot: preDispatchResult.data.state,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
if (dispatch.result.action === "retry") {
|
|
213
|
+
return { kind: "paused", reason: dispatch.result.reason };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const data = dispatch.result.data;
|
|
217
|
+
const unit: UnitRef = { unitType: data.unitType, unitId: data.unitId };
|
|
218
|
+
s.pendingOrchestrationDispatch = {
|
|
219
|
+
unitType: data.unitType,
|
|
220
|
+
unitId: data.unitId,
|
|
221
|
+
prompt: data.prompt,
|
|
222
|
+
pauseAfterUatDispatch: data.pauseAfterUatDispatch,
|
|
223
|
+
state: data.state,
|
|
224
|
+
mid: data.mid,
|
|
225
|
+
midTitle: data.midTitle,
|
|
226
|
+
};
|
|
227
|
+
status.phase = "running";
|
|
228
|
+
status.activeUnit = unit;
|
|
229
|
+
status.transitionCount += 1;
|
|
230
|
+
return { kind: "advanced", unit, stateSnapshot: data.state };
|
|
231
|
+
},
|
|
232
|
+
async completeActiveUnit() {
|
|
233
|
+
clearActiveUnit();
|
|
234
|
+
},
|
|
235
|
+
async retryActiveUnit() {
|
|
236
|
+
clearActiveUnit();
|
|
237
|
+
},
|
|
238
|
+
async resume() {
|
|
239
|
+
status.phase = "running";
|
|
240
|
+
status.transitionCount += 1;
|
|
241
|
+
return { kind: "resumed" };
|
|
242
|
+
},
|
|
243
|
+
async stop(reason: string) {
|
|
244
|
+
status.phase = "stopped";
|
|
245
|
+
clearActiveUnit();
|
|
246
|
+
status.transitionCount += 1;
|
|
247
|
+
return { kind: "stopped", reason };
|
|
248
|
+
},
|
|
249
|
+
getStatus() {
|
|
250
|
+
return {
|
|
251
|
+
...status,
|
|
252
|
+
activeUnit: status.activeUnit ? { ...status.activeUnit } : undefined,
|
|
253
|
+
};
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async function autoLoop(
|
|
259
|
+
ctx: any,
|
|
260
|
+
pi: any,
|
|
261
|
+
s: any,
|
|
262
|
+
deps: LoopDeps,
|
|
263
|
+
options?: Parameters<typeof rawAutoLoop>[4],
|
|
264
|
+
): Promise<void> {
|
|
265
|
+
if (!s.orchestration) {
|
|
266
|
+
s.orchestration = createLoopTestOrchestration(ctx, pi, s, deps);
|
|
267
|
+
}
|
|
268
|
+
await rawAutoLoop(ctx, pi, s, deps, options);
|
|
269
|
+
}
|
|
270
|
+
|
|
70
271
|
/**
|
|
71
272
|
* Build a minimal mock AutoSession with controllable newSession behavior.
|
|
72
273
|
*/
|
|
73
274
|
function makeMockSession(opts?: {
|
|
74
275
|
newSessionResult?: { cancelled: boolean };
|
|
75
276
|
newSessionThrows?: string;
|
|
277
|
+
/** Reject newSession() with a specific Error instance (e.g. TypeError). */
|
|
278
|
+
newSessionThrowsError?: Error;
|
|
76
279
|
newSessionDelayMs?: number;
|
|
77
280
|
onNewSessionStart?: (session: any) => void;
|
|
78
281
|
onNewSessionSettle?: (session: any) => void;
|
|
@@ -87,6 +290,9 @@ function makeMockSession(opts?: {
|
|
|
87
290
|
cmdCtx: {
|
|
88
291
|
newSession: (options?: { abortSignal?: AbortSignal; workspaceRoot?: string }) => {
|
|
89
292
|
opts?.onNewSessionStart?.(session);
|
|
293
|
+
if (opts?.newSessionThrowsError) {
|
|
294
|
+
return Promise.reject(opts.newSessionThrowsError);
|
|
295
|
+
}
|
|
90
296
|
if (opts?.newSessionThrows) {
|
|
91
297
|
return Promise.reject(new Error(opts.newSessionThrows));
|
|
92
298
|
}
|
|
@@ -497,6 +703,73 @@ test("runUnit returns cancelled when session creation fails", async () => {
|
|
|
497
703
|
assert.equal(pi.calls.length, 0);
|
|
498
704
|
});
|
|
499
705
|
|
|
706
|
+
test("runUnit: TypeError from newSession is classified as structural (isTransient: false)", async () => {
|
|
707
|
+
// Regression for #572: a TypeError thrown from newSession (e.g. "something is
|
|
708
|
+
// not a function") indicates a programming error, not a transient provider
|
|
709
|
+
// blip. Before the fix it was always classified isTransient: true, causing
|
|
710
|
+
// auto-mode to retry indefinitely instead of surfacing the real problem.
|
|
711
|
+
_resetPendingResolve();
|
|
712
|
+
|
|
713
|
+
const baseCtx = {
|
|
714
|
+
...makeMockCtx(),
|
|
715
|
+
ui: { notify: () => {}, setStatus: () => {}, setWorkingMessage: () => {} },
|
|
716
|
+
sessionManager: { getEntries: () => [] },
|
|
717
|
+
modelRegistry: { getProviderAuthMode: () => undefined, isProviderRequestReady: () => true },
|
|
718
|
+
} as any;
|
|
719
|
+
const pi = makeMockPi();
|
|
720
|
+
const s = makeMockSession({
|
|
721
|
+
newSessionThrowsError: new TypeError("pi.sendMessage is not a function"),
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
const result = await runUnit(baseCtx, pi, s, "task", "T01", "prompt");
|
|
725
|
+
|
|
726
|
+
assert.equal(result.status, "cancelled");
|
|
727
|
+
assert.equal(result.errorContext?.category, "session-failed");
|
|
728
|
+
assert.equal(result.errorContext?.isTransient, false, "TypeError must be non-transient");
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
test("runUnit: 'is not a function' message from newSession is classified as structural", async () => {
|
|
732
|
+
// Regression for #572: the pattern also catches errors where the thrown
|
|
733
|
+
// object is not a TypeError instance but the message contains "is not a function".
|
|
734
|
+
_resetPendingResolve();
|
|
735
|
+
|
|
736
|
+
const baseCtx = {
|
|
737
|
+
...makeMockCtx(),
|
|
738
|
+
ui: { notify: () => {}, setStatus: () => {}, setWorkingMessage: () => {} },
|
|
739
|
+
sessionManager: { getEntries: () => [] },
|
|
740
|
+
modelRegistry: { getProviderAuthMode: () => undefined, isProviderRequestReady: () => true },
|
|
741
|
+
} as any;
|
|
742
|
+
const pi = makeMockPi();
|
|
743
|
+
const s = makeMockSession({ newSessionThrows: "pi.sendMessage is not a function" });
|
|
744
|
+
|
|
745
|
+
const result = await runUnit(baseCtx, pi, s, "task", "T01", "prompt");
|
|
746
|
+
|
|
747
|
+
assert.equal(result.status, "cancelled");
|
|
748
|
+
assert.equal(result.errorContext?.category, "session-failed");
|
|
749
|
+
assert.equal(result.errorContext?.isTransient, false, "'is not a function' errors must be non-transient");
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
test("runUnit: generic network error from newSession remains transient", async () => {
|
|
753
|
+
// Confirm that non-structural session errors (e.g. 429, ECONNREFUSED) are
|
|
754
|
+
// still classified as transient so auto-mode can retry them.
|
|
755
|
+
_resetPendingResolve();
|
|
756
|
+
|
|
757
|
+
const baseCtx = {
|
|
758
|
+
...makeMockCtx(),
|
|
759
|
+
ui: { notify: () => {}, setStatus: () => {}, setWorkingMessage: () => {} },
|
|
760
|
+
sessionManager: { getEntries: () => [] },
|
|
761
|
+
modelRegistry: { getProviderAuthMode: () => undefined, isProviderRequestReady: () => true },
|
|
762
|
+
} as any;
|
|
763
|
+
const pi = makeMockPi();
|
|
764
|
+
const s = makeMockSession({ newSessionThrows: "connection refused" });
|
|
765
|
+
|
|
766
|
+
const result = await runUnit(baseCtx, pi, s, "task", "T01", "prompt");
|
|
767
|
+
|
|
768
|
+
assert.equal(result.status, "cancelled");
|
|
769
|
+
assert.equal(result.errorContext?.category, "session-failed");
|
|
770
|
+
assert.equal(result.errorContext?.isTransient, true, "network errors must remain transient");
|
|
771
|
+
});
|
|
772
|
+
|
|
500
773
|
test("runUnit clears queued switch cancellation when session creation fails", async () => {
|
|
501
774
|
_resetPendingResolve();
|
|
502
775
|
|
|
@@ -1164,6 +1437,34 @@ test("autoLoop exits when s.active is set to false", async (t) => {
|
|
|
1164
1437
|
);
|
|
1165
1438
|
});
|
|
1166
1439
|
|
|
1440
|
+
test("autoLoop pauses visibly when Auto Orchestration Module is not wired", async () => {
|
|
1441
|
+
_resetPendingResolve();
|
|
1442
|
+
|
|
1443
|
+
const ctx = makeMockCtx();
|
|
1444
|
+
ctx.ui.setStatus = () => {};
|
|
1445
|
+
const pi = makeMockPi();
|
|
1446
|
+
const s = makeLoopSession();
|
|
1447
|
+
let pauseContext: unknown;
|
|
1448
|
+
|
|
1449
|
+
const deps = makeMockDeps({
|
|
1450
|
+
pauseAuto: async (_ctx, _pi, errorContext) => {
|
|
1451
|
+
pauseContext = errorContext;
|
|
1452
|
+
deps.callLog.push("pauseAuto");
|
|
1453
|
+
},
|
|
1454
|
+
});
|
|
1455
|
+
|
|
1456
|
+
await rawAutoLoop(ctx, pi, s, deps);
|
|
1457
|
+
|
|
1458
|
+
assert.ok(deps.callLog.includes("pauseAuto"), "missing orchestration should pause auto-mode");
|
|
1459
|
+
assert.equal(
|
|
1460
|
+
(pauseContext as { message?: string } | undefined)?.message,
|
|
1461
|
+
ORCHESTRATION_MISSING_REASON,
|
|
1462
|
+
);
|
|
1463
|
+
assert.equal(deps.callLog.includes("deriveState"), false);
|
|
1464
|
+
assert.equal(deps.callLog.includes("resolveDispatch"), false);
|
|
1465
|
+
assert.equal(s.pendingOrchestrationDispatch, null);
|
|
1466
|
+
});
|
|
1467
|
+
|
|
1167
1468
|
test("autoLoop exits on terminal complete state", async (t) => {
|
|
1168
1469
|
_resetPendingResolve();
|
|
1169
1470
|
|
|
@@ -1990,11 +2291,15 @@ test("autoLoop retries next iteration when orchestration reports paused", async
|
|
|
1990
2291
|
},
|
|
1991
2292
|
});
|
|
1992
2293
|
|
|
2294
|
+
const journalEvents: Array<{ eventType: string; data?: any }> = [];
|
|
1993
2295
|
const deps = makeMockDeps({
|
|
1994
2296
|
resolveDispatch: async () => {
|
|
1995
2297
|
deps.callLog.push("resolveDispatch");
|
|
1996
2298
|
throw new Error("legacy resolveDispatch must not run after orchestration paused");
|
|
1997
2299
|
},
|
|
2300
|
+
emitJournalEvent: (entry: any) => {
|
|
2301
|
+
journalEvents.push(entry);
|
|
2302
|
+
},
|
|
1998
2303
|
});
|
|
1999
2304
|
|
|
2000
2305
|
await autoLoop(ctx, pi, s, deps);
|
|
@@ -2007,6 +2312,11 @@ test("autoLoop retries next iteration when orchestration reports paused", async
|
|
|
2007
2312
|
"orchestration paused must not fall back to legacy dispatch",
|
|
2008
2313
|
);
|
|
2009
2314
|
assert.equal(s.pendingOrchestrationDispatch, null, "no orchestration dispatch should remain pending");
|
|
2315
|
+
|
|
2316
|
+
const pausedIterationEnd = journalEvents.find(
|
|
2317
|
+
(e) => e.eventType === "iteration-end" && e.data?.status === "paused",
|
|
2318
|
+
);
|
|
2319
|
+
assert.ok(pausedIterationEnd, "orchestration paused must emit iteration-end to close the iteration journal");
|
|
2010
2320
|
});
|
|
2011
2321
|
|
|
2012
2322
|
test("autoLoop consumes pending orchestration dispatch without advancing twice", async () => {
|
|
@@ -2094,8 +2404,13 @@ test("autoLoop stops orchestrator complete state through completion surface", as
|
|
|
2094
2404
|
start: async () => ({ kind: "stopped" as const, reason: "unused" }),
|
|
2095
2405
|
advance: async () => ({
|
|
2096
2406
|
kind: "stopped" as const,
|
|
2097
|
-
reason: "
|
|
2407
|
+
reason: "legacy text not used",
|
|
2098
2408
|
stateSnapshot,
|
|
2409
|
+
terminalOutcome: {
|
|
2410
|
+
code: "all-complete" as const,
|
|
2411
|
+
displayReason: "All milestones complete",
|
|
2412
|
+
allMilestonesComplete: true as const,
|
|
2413
|
+
},
|
|
2099
2414
|
}),
|
|
2100
2415
|
completeActiveUnit: async () => {},
|
|
2101
2416
|
retryActiveUnit: async () => {},
|
|
@@ -2121,6 +2436,7 @@ test("autoLoop stops orchestrator complete state through completion surface", as
|
|
|
2121
2436
|
milestoneTitle: "Priority Levels",
|
|
2122
2437
|
allMilestonesComplete: true,
|
|
2123
2438
|
});
|
|
2439
|
+
assert.equal((stopCalls[0]?.options as any)?.terminalOutcome?.code, "all-complete");
|
|
2124
2440
|
assert.equal(
|
|
2125
2441
|
deps.callLog.includes("resolveDispatch"),
|
|
2126
2442
|
false,
|
|
@@ -2960,7 +3276,7 @@ test("autoLoop closes journal iteration on pre-dispatch health-gate break", asyn
|
|
|
2960
3276
|
assert.equal(deps.callLog.includes("pauseAuto"), true);
|
|
2961
3277
|
assert.deepEqual(pauseOptions, { expectedCurrentUnit: null });
|
|
2962
3278
|
assert.ok(
|
|
2963
|
-
journalEvents.some((event) => event.eventType === "iteration-end" && event.data?.reason === "
|
|
3279
|
+
journalEvents.some((event) => event.eventType === "iteration-end" && event.data?.reason === "health-gate-failed"),
|
|
2964
3280
|
"pre-dispatch break must close the started iteration",
|
|
2965
3281
|
);
|
|
2966
3282
|
});
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { describe, it } from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
3
6
|
|
|
4
7
|
import { parseMilestoneTarget, parseModelFlag } from "../commands/handlers/auto.js";
|
|
5
8
|
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
|
|
6
11
|
describe("parseMilestoneTarget", () => {
|
|
7
12
|
it("extracts a simple milestone ID", () => {
|
|
8
13
|
const result = parseMilestoneTarget("auto M016");
|
|
@@ -60,6 +65,24 @@ describe("parseMilestoneTarget", () => {
|
|
|
60
65
|
});
|
|
61
66
|
});
|
|
62
67
|
|
|
68
|
+
describe("auto preference diagnostics", () => {
|
|
69
|
+
it("notifies preference diagnostics before launching auto-mode", () => {
|
|
70
|
+
const source = readFileSync(join(__dirname, "..", "commands", "handlers", "auto.ts"), "utf-8");
|
|
71
|
+
const autoBlockIndex = source.indexOf('if (trimmed === "auto" || trimmed.startsWith("auto "))');
|
|
72
|
+
assert.ok(autoBlockIndex >= 0, "auto command block should exist");
|
|
73
|
+
const nextBlockIndex = source.indexOf('if (trimmed === "stop")', autoBlockIndex);
|
|
74
|
+
const autoBlock = source.slice(autoBlockIndex, nextBlockIndex);
|
|
75
|
+
const notifyIndex = autoBlock.indexOf("notifyPreferenceDiagnostics");
|
|
76
|
+
assert.ok(notifyIndex >= 0, "auto command should notify preference diagnostics");
|
|
77
|
+
for (const match of autoBlock.matchAll(/startAutoDetached/g)) {
|
|
78
|
+
assert.ok(
|
|
79
|
+
notifyIndex < match.index!,
|
|
80
|
+
"preference diagnostics should be notified before each auto-mode launch",
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
63
86
|
describe("parseModelFlag", () => {
|
|
64
87
|
it("extracts provider/model from --model", () => {
|
|
65
88
|
const result = parseModelFlag("auto --model openrouter/openai/gpt-5.4");
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
selectAndApplyModel,
|
|
34
34
|
ModelPolicyDispatchBlockedError,
|
|
35
35
|
clearToolBaseline,
|
|
36
|
+
getRegisteredToolSnapshot,
|
|
36
37
|
getToolBaselineSnapshot,
|
|
37
38
|
} from "../auto-model-selection.js";
|
|
38
39
|
import { applyModelPolicyFilter } from "../uok/model-policy.js";
|
|
@@ -773,3 +774,20 @@ test("baseline union (#477): getToolBaselineSnapshot includes live tools not pre
|
|
|
773
774
|
env.cleanup();
|
|
774
775
|
}
|
|
775
776
|
});
|
|
777
|
+
|
|
778
|
+
test("registered tool snapshot includes tools hidden from the active surface", () => {
|
|
779
|
+
const pi = {
|
|
780
|
+
getActiveTools: () => ["read"],
|
|
781
|
+
getAllTools: () => [
|
|
782
|
+
{ name: "read" },
|
|
783
|
+
{ name: "browser_navigate" },
|
|
784
|
+
{ name: "mcp__gsd-browser__*" },
|
|
785
|
+
],
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
assert.deepEqual(getRegisteredToolSnapshot(pi as any), [
|
|
789
|
+
"read",
|
|
790
|
+
"browser_navigate",
|
|
791
|
+
"mcp__gsd-browser__*",
|
|
792
|
+
]);
|
|
793
|
+
});
|
|
@@ -33,6 +33,8 @@ import type { UnifiedRule } from "../rule-types.js";
|
|
|
33
33
|
import { supportsStructuredQuestions } from "../workflow-mcp.js";
|
|
34
34
|
import {
|
|
35
35
|
closeDatabase,
|
|
36
|
+
insertAssessment,
|
|
37
|
+
insertGateRow,
|
|
36
38
|
insertMilestone,
|
|
37
39
|
insertSlice,
|
|
38
40
|
insertTask,
|
|
@@ -302,6 +304,46 @@ test("advance() sets active unit and is reflected in status", async (t) => {
|
|
|
302
304
|
});
|
|
303
305
|
});
|
|
304
306
|
|
|
307
|
+
test("advance() blocks source dispatch when an earlier slice is incomplete", async (t) => {
|
|
308
|
+
const f = makeFixture({
|
|
309
|
+
dispatch: () => ({
|
|
310
|
+
action: "dispatch",
|
|
311
|
+
unitType: "execute-task",
|
|
312
|
+
unitId: "M001/S02/T01",
|
|
313
|
+
prompt: "fixture-prompt",
|
|
314
|
+
}),
|
|
315
|
+
});
|
|
316
|
+
t.after(() => f.cleanup());
|
|
317
|
+
|
|
318
|
+
insertSlice({
|
|
319
|
+
id: "S02",
|
|
320
|
+
milestoneId: "M001",
|
|
321
|
+
title: "Second slice",
|
|
322
|
+
status: "active",
|
|
323
|
+
risk: "low",
|
|
324
|
+
depends: [],
|
|
325
|
+
demo: "",
|
|
326
|
+
sequence: 2,
|
|
327
|
+
});
|
|
328
|
+
insertTask({
|
|
329
|
+
id: "T01",
|
|
330
|
+
sliceId: "S02",
|
|
331
|
+
milestoneId: "M001",
|
|
332
|
+
title: "Second task",
|
|
333
|
+
status: "active",
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
const result = await f.orchestrator.advance();
|
|
337
|
+
|
|
338
|
+
assert.equal(result.kind, "blocked");
|
|
339
|
+
if (result.kind !== "blocked") return;
|
|
340
|
+
assert.equal(result.action, "stop");
|
|
341
|
+
assert.match(result.reason, /earlier slice M001\/S01 is not complete/);
|
|
342
|
+
assert.equal(f.session.pendingOrchestrationDispatch, null);
|
|
343
|
+
assert.deepEqual(f.orchestrator.getStatus().activeUnit, undefined);
|
|
344
|
+
assert.ok(f.journalNames().includes("advance-blocked"));
|
|
345
|
+
});
|
|
346
|
+
|
|
305
347
|
test("getStatus() returns defensive copy of activeUnit", async (t) => {
|
|
306
348
|
const f = makeFixture();
|
|
307
349
|
t.after(() => f.cleanup());
|
|
@@ -374,10 +416,65 @@ test("advance() reports completion when complete state has no next unit", async
|
|
|
374
416
|
|
|
375
417
|
assert.equal(result.kind, "stopped");
|
|
376
418
|
if (result.kind !== "stopped") return;
|
|
377
|
-
assert.equal(result.reason, "
|
|
419
|
+
assert.equal(result.reason, "All milestones complete");
|
|
420
|
+
assert.equal(result.terminalOutcome?.code, "all-complete");
|
|
378
421
|
assert.equal(f.orchestrator.getStatus().phase, "stopped");
|
|
379
422
|
});
|
|
380
423
|
|
|
424
|
+
test("advance() blocks all-complete stop when completed milestone is still unmerged in a worktree", async (t) => {
|
|
425
|
+
const f = makeFixture({ complete: true, noTask: true });
|
|
426
|
+
t.after(() => f.cleanup());
|
|
427
|
+
|
|
428
|
+
insertSlice({
|
|
429
|
+
id: "S01",
|
|
430
|
+
milestoneId: "M001",
|
|
431
|
+
title: "Slice",
|
|
432
|
+
status: "complete",
|
|
433
|
+
risk: "low",
|
|
434
|
+
depends: [],
|
|
435
|
+
demo: "",
|
|
436
|
+
sequence: 1,
|
|
437
|
+
});
|
|
438
|
+
insertAssessment({
|
|
439
|
+
path: "milestones/M001/M001-VALIDATION.md",
|
|
440
|
+
milestoneId: "M001",
|
|
441
|
+
status: "pass",
|
|
442
|
+
scope: "milestone-validation",
|
|
443
|
+
fullContent: "verdict: pass",
|
|
444
|
+
});
|
|
445
|
+
insertGateRow({
|
|
446
|
+
milestoneId: "M001",
|
|
447
|
+
sliceId: "S01",
|
|
448
|
+
gateId: "Q3",
|
|
449
|
+
scope: "slice",
|
|
450
|
+
status: "pending",
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
const worktreePath = join(f.base, ".gsd", "worktrees", "M001");
|
|
454
|
+
mkdirSync(join(f.base, ".gsd", "worktrees"), { recursive: true });
|
|
455
|
+
execFileSync("git", ["worktree", "add", "-b", "milestone/M001", worktreePath], { cwd: f.base, stdio: "ignore" });
|
|
456
|
+
mkdirSync(join(worktreePath, ".gsd", "milestones", "M001"), { recursive: true });
|
|
457
|
+
writeFileSync(join(worktreePath, ".gsd", "milestones", "M001", "M001-SUMMARY.md"), "# Milestone Summary\n");
|
|
458
|
+
f.session.basePath = worktreePath;
|
|
459
|
+
f.session.originalBasePath = f.base;
|
|
460
|
+
f.session.currentMilestoneId = "M001";
|
|
461
|
+
f.session.milestoneMergedInPhases = false;
|
|
462
|
+
|
|
463
|
+
const result = await f.orchestrator.advance();
|
|
464
|
+
|
|
465
|
+
assert.equal(result.kind, "blocked");
|
|
466
|
+
if (result.kind !== "blocked") return;
|
|
467
|
+
assert.equal(result.action, "pause");
|
|
468
|
+
assert.equal(result.terminalOutcome?.code, "settlement-blocked");
|
|
469
|
+
assert.match(result.reason, /worktree branch has not been merged to main/);
|
|
470
|
+
assert.doesNotMatch(result.reason, /quality gate Q3 is still pending/);
|
|
471
|
+
assert.equal(f.orchestrator.getStatus().phase, "paused");
|
|
472
|
+
assert.equal(f.session.milestoneSettlement?.ok, false);
|
|
473
|
+
const names = f.journalNames();
|
|
474
|
+
assert.ok(names.includes("advance-blocked"));
|
|
475
|
+
assert.ok(!names.includes("advance-stopped"));
|
|
476
|
+
});
|
|
477
|
+
|
|
381
478
|
test("advance() stopped clears previous activeUnit and resets idempotent lock", async (t) => {
|
|
382
479
|
// First advance dispatches; then we make the milestone resolve to no unit by
|
|
383
480
|
// closing it on disk + DB and re-deriving. Simpler: drive a fixture that
|
|
@@ -696,7 +793,10 @@ test("stuck-loop: start() resets the ring so a fresh saturation cycle is require
|
|
|
696
793
|
assert.equal(next.kind, "advanced");
|
|
697
794
|
});
|
|
698
795
|
|
|
699
|
-
test("stuck-loop: resume()
|
|
796
|
+
test("stuck-loop: resume() preserves ring so detection accumulates across pause/resume", async (t) => {
|
|
797
|
+
// Regression for #572: resume() must NOT reset dispatchKeyWindow. Before the
|
|
798
|
+
// fix, a pause/resume cycle cleared the window, letting a stuck loop silently
|
|
799
|
+
// re-accumulate STUCK_WINDOW_SIZE dispatches before being detected again.
|
|
700
800
|
const f = makeFixture();
|
|
701
801
|
t.after(() => f.cleanup());
|
|
702
802
|
|
|
@@ -707,11 +807,39 @@ test("stuck-loop: resume() resets the ring", async (t) => {
|
|
|
707
807
|
const resumed = await f.orchestrator.resume();
|
|
708
808
|
assert.equal(resumed.kind, "resumed");
|
|
709
809
|
|
|
810
|
+
// The ring is preserved, so the next advance pushes it to STUCK_WINDOW_SIZE
|
|
811
|
+
// and triggers stuck-loop detection — not a fresh dispatch.
|
|
710
812
|
const next = await f.orchestrator.advance();
|
|
711
|
-
assert.equal(next.kind, "
|
|
813
|
+
assert.equal(next.kind, "blocked");
|
|
814
|
+
if (next.kind !== "blocked") return;
|
|
815
|
+
assert.equal(next.action, "stop");
|
|
816
|
+
assert.ok(next.reason.startsWith("stuck-loop:"), `expected stuck-loop reason, got: ${next.reason}`);
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
test("stuck-loop: stop('pause') preserves ring across the stop/resume cycle", async (t) => {
|
|
820
|
+
// Regression for #572: stop("pause") must behave the same as resume() —
|
|
821
|
+
// the window must survive so detection accumulates across pause/resume pairs.
|
|
822
|
+
const f = makeFixture();
|
|
823
|
+
t.after(() => f.cleanup());
|
|
824
|
+
|
|
825
|
+
for (let i = 0; i < STUCK_WINDOW_SIZE - 1; i++) {
|
|
826
|
+
await f.orchestrator.advance();
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
const stopped = await f.orchestrator.stop("pause");
|
|
830
|
+
assert.equal(stopped.kind, "stopped");
|
|
831
|
+
|
|
832
|
+
const resumed = await f.orchestrator.resume();
|
|
833
|
+
assert.equal(resumed.kind, "resumed");
|
|
834
|
+
|
|
835
|
+
const next = await f.orchestrator.advance();
|
|
836
|
+
assert.equal(next.kind, "blocked");
|
|
837
|
+
if (next.kind !== "blocked") return;
|
|
838
|
+
assert.equal(next.action, "stop");
|
|
839
|
+
assert.ok(next.reason.startsWith("stuck-loop:"), `expected stuck-loop reason, got: ${next.reason}`);
|
|
712
840
|
});
|
|
713
841
|
|
|
714
|
-
test("stuck-loop: stop() resets the ring", async (t) => {
|
|
842
|
+
test("stuck-loop: stop('user-request') resets the ring (hard stop)", async (t) => {
|
|
715
843
|
const f = makeFixture();
|
|
716
844
|
t.after(() => f.cleanup());
|
|
717
845
|
|
|
@@ -722,6 +850,7 @@ test("stuck-loop: stop() resets the ring", async (t) => {
|
|
|
722
850
|
const stopped = await f.orchestrator.stop("user-request");
|
|
723
851
|
assert.equal(stopped.kind, "stopped");
|
|
724
852
|
|
|
853
|
+
// Hard stop clears the ring, so the next advance dispatches fresh.
|
|
725
854
|
const next = await f.orchestrator.advance();
|
|
726
855
|
assert.equal(next.kind, "advanced");
|
|
727
856
|
});
|