@opengsd/gsd-pi 1.2.0-dev.955e4da0 → 1.2.0-dev.d6c5343c
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-style.d.ts +17 -0
- package/dist/cli-style.js +28 -0
- package/dist/cli.js +1 -1
- package/dist/headless-events.d.ts +4 -2
- package/dist/headless-events.js +14 -34
- package/dist/models-resolver.d.ts +3 -13
- package/dist/models-resolver.js +3 -22
- package/dist/resource-loader.js +2 -14
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/GSD-WORKFLOW.md +5 -4
- package/dist/resources/extensions/async-jobs/async-bash-tool.js +30 -64
- package/dist/resources/extensions/async-jobs/await-tool.js +80 -12
- package/dist/resources/extensions/async-jobs/index.js +65 -0
- package/dist/resources/extensions/async-jobs/job-manager.js +12 -1
- package/dist/resources/extensions/bg-shell/bg-shell-command.js +6 -6
- package/dist/resources/extensions/bg-shell/bg-shell-tool.js +10 -7
- package/dist/resources/extensions/bg-shell/overlay.js +9 -6
- package/dist/resources/extensions/bg-shell/process-manager.js +54 -25
- package/dist/resources/extensions/bg-shell/readiness-detector.js +11 -0
- package/dist/resources/extensions/bg-shell/utilities.js +3 -0
- package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +209 -88
- package/dist/resources/extensions/browser-tools/engine/selection.js +73 -5
- package/dist/resources/extensions/browser-tools/index.js +69 -12
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +30 -4
- package/dist/resources/extensions/gsd/auto/custom-verify-retry-store.js +17 -2
- package/dist/resources/extensions/gsd/auto/detect-stuck.js +33 -13
- package/dist/resources/extensions/gsd/auto/dispatch-history.js +105 -0
- package/dist/resources/extensions/gsd/auto/dispatch-key.js +37 -0
- package/dist/resources/extensions/gsd/auto/loop.js +4 -1
- package/dist/resources/extensions/gsd/auto/orchestrator.js +61 -44
- package/dist/resources/extensions/gsd/auto/phases.js +2 -2
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +8 -32
- package/dist/resources/extensions/gsd/auto-dispatch.js +40 -57
- package/dist/resources/extensions/gsd/auto-model-selection.js +25 -6
- package/dist/resources/extensions/gsd/auto-post-unit.js +23 -8
- package/dist/resources/extensions/gsd/auto-prompts.js +81 -19
- package/dist/resources/extensions/gsd/auto-start.js +18 -15
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +18 -0
- package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +12 -20
- package/dist/resources/extensions/gsd/auto-verification.js +9 -28
- package/dist/resources/extensions/gsd/auto-worktree.js +30 -90
- package/dist/resources/extensions/gsd/auto.js +4 -13
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +3 -2
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +23 -6
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +19 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +212 -48
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +303 -77
- package/dist/resources/extensions/gsd/branch-patterns.js +2 -0
- package/dist/resources/extensions/gsd/browser-daemon-auto-prep.js +83 -0
- package/dist/resources/extensions/gsd/browser-evidence.js +8 -2
- package/dist/resources/extensions/gsd/captures.js +4 -6
- package/dist/resources/extensions/gsd/consent-question.js +337 -0
- package/dist/resources/extensions/gsd/consent-verdict.js +63 -0
- package/dist/resources/extensions/gsd/constants.js +0 -2
- package/dist/resources/extensions/gsd/crash-recovery.js +4 -12
- package/dist/resources/extensions/gsd/db/queries.js +26 -0
- package/dist/resources/extensions/gsd/dispatch-guard.js +10 -35
- package/dist/resources/extensions/gsd/doctor-environment.js +2 -6
- package/dist/resources/extensions/gsd/doctor-format.js +9 -6
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +13 -15
- package/dist/resources/extensions/gsd/engine-hook-contract.js +70 -0
- package/dist/resources/extensions/gsd/error-classifier.js +9 -0
- package/dist/resources/extensions/gsd/exec-sandbox.js +30 -10
- package/dist/resources/extensions/gsd/files.js +33 -19
- package/dist/resources/extensions/gsd/guidance.js +158 -0
- package/dist/resources/extensions/gsd/guided-flow.js +17 -2
- package/dist/resources/extensions/gsd/markdown-renderer.js +10 -0
- package/dist/resources/extensions/gsd/mcp-filter.js +2 -19
- package/dist/resources/extensions/gsd/mcp-tool-name.js +5 -13
- package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +1 -1
- package/dist/resources/extensions/gsd/migrate/safety.js +4 -1
- package/dist/resources/extensions/gsd/milestone-closeout.js +13 -23
- package/dist/resources/extensions/gsd/notification-store.js +11 -4
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +6 -4
- package/dist/resources/extensions/gsd/parsers-legacy.js +16 -4
- package/dist/resources/extensions/gsd/paths.js +27 -0
- package/dist/resources/extensions/gsd/pre-execution-checks.js +91 -3
- package/dist/resources/extensions/gsd/preferences-models.js +14 -48
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -2
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/run-uat.md +6 -4
- package/dist/resources/extensions/gsd/prompts/system.md +5 -2
- package/dist/resources/extensions/gsd/provider-error-guidance.js +1 -5
- package/dist/resources/extensions/gsd/provider-switch-observer.js +1 -1
- package/dist/resources/extensions/gsd/publication.js +87 -0
- package/dist/resources/extensions/gsd/reactive-graph.js +8 -1
- package/dist/resources/extensions/gsd/recovery-classification.js +37 -94
- package/dist/resources/extensions/gsd/safety/destructive-confirmation.js +108 -0
- package/dist/resources/extensions/gsd/state.js +6 -20
- package/dist/resources/extensions/gsd/stop-notice.js +57 -0
- package/dist/resources/extensions/gsd/tool-presentation-plan.js +4 -4
- package/dist/resources/extensions/gsd/tool-surface-readiness.js +56 -0
- package/dist/resources/extensions/gsd/tools/complete-slice.js +20 -10
- package/dist/resources/extensions/gsd/tools/exec-tool.js +9 -7
- package/dist/resources/extensions/gsd/tools/plan-slice.js +12 -6
- package/dist/resources/extensions/gsd/uat-policy.js +42 -16
- package/dist/resources/extensions/gsd/unit-closeout.js +138 -0
- package/dist/resources/extensions/gsd/unit-context-composer.js +74 -1
- package/dist/resources/extensions/gsd/unit-context-manifest.js +4 -27
- package/dist/resources/extensions/gsd/unit-registry.js +337 -0
- package/dist/resources/extensions/gsd/unit-tool-contracts.js +9 -182
- package/dist/resources/extensions/gsd/verdict-parser.js +1 -1
- package/dist/resources/extensions/gsd/web-app-uat.js +45 -8
- package/dist/resources/extensions/gsd/workflow-tool-surface.js +1 -1
- package/dist/resources/extensions/gsd/worktree-git-recovery.js +15 -9
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +3 -2
- package/dist/resources/extensions/gsd/worktree-root.js +11 -0
- package/dist/resources/extensions/gsd/worktree-session-state.js +4 -5
- package/dist/resources/extensions/search-the-web/native-search.js +5 -3
- package/dist/resources/extensions/shared/browser-contract.js +59 -0
- package/dist/resources/extensions/shared/gsd-browser-cli.js +96 -5
- package/dist/resources/shared/package.json +3 -0
- package/dist/resources/skills/create-skill/references/executable-code.md +1 -1
- package/dist/resources/skills/create-skill/workflows/add-reference.md +8 -3
- package/dist/resources/skills/create-skill/workflows/add-script.md +4 -2
- package/dist/resources/skills/create-skill/workflows/add-template.md +3 -1
- package/dist/resources/skills/create-skill/workflows/add-workflow.md +8 -3
- package/dist/resources/skills/create-skill/workflows/upgrade-to-router.md +10 -5
- package/dist/resources/skills/create-skill/workflows/verify-skill.md +9 -4
- package/dist/resources/skills/spike-wrap-up/SKILL.md +9 -9
- 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 +12 -12
- 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 +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.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/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js.nft.json +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 +12 -12
- package/dist/web/standalone/.next/server/chunks/5124.js +1 -1
- 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/{796.cf859a427a2cb2ac.js → 796.e0bdc932325d7e03.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{webpack-fbea77b5f9953368.js → webpack-f0285ce91d4ec9ef.js} +1 -1
- package/dist/web/standalone/package.json +1 -1
- package/dist/worktree-cli.js +3 -6
- package/dist/worktree-status-banner.js +7 -15
- package/package.json +1 -1
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/dist/rpc.d.ts +1 -0
- package/packages/contracts/dist/rpc.d.ts.map +1 -1
- package/packages/contracts/dist/rpc.js.map +1 -1
- package/packages/contracts/dist/workflow.d.ts +4 -0
- package/packages/contracts/dist/workflow.d.ts.map +1 -1
- package/packages/contracts/dist/workflow.js.map +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +5 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +5 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
- 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 +7 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js +8 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +11 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js +4 -4
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js +3 -1
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/dist/cli.js +6 -3
- package/packages/mcp-server/dist/cli.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +8 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +17 -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/dist/harness/env/nodejs.d.ts +1 -0
- package/packages/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/harness/env/nodejs.js +34 -3
- package/packages/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
- package/packages/pi-agent-core/dist/index.d.ts +1 -0
- package/packages/pi-agent-core/dist/index.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/index.js +3 -0
- package/packages/pi-agent-core/dist/index.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/README.md +1 -0
- package/packages/pi-ai/dist/image-models.generated.d.ts +2 -2
- package/packages/pi-ai/dist/image-models.generated.js +6 -6
- package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +35 -125
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +46 -120
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/package.json +3 -2
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +2 -2
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +19 -13
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/provider-readiness.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/provider-readiness.js +13 -6
- package/packages/pi-coding-agent/dist/core/provider-readiness.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.d.ts +11 -0
- package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.js +53 -11
- package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/utils/shell.d.ts +28 -2
- package/packages/pi-coding-agent/dist/utils/shell.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/utils/shell.js +56 -10
- package/packages/pi-coding-agent/dist/utils/shell.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +9 -0
- package/packages/pi-tui/dist/tui.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/GSD-WORKFLOW.md +5 -4
- package/src/resources/extensions/async-jobs/async-bash-cancel.test.ts +360 -0
- package/src/resources/extensions/async-jobs/async-bash-tool.ts +33 -56
- package/src/resources/extensions/async-jobs/await-tool.test.ts +139 -0
- package/src/resources/extensions/async-jobs/await-tool.ts +82 -12
- package/src/resources/extensions/async-jobs/index.ts +79 -0
- package/src/resources/extensions/async-jobs/job-manager.ts +21 -1
- package/src/resources/extensions/bg-shell/bg-shell-command.ts +6 -6
- package/src/resources/extensions/bg-shell/bg-shell-tool.ts +10 -6
- package/src/resources/extensions/bg-shell/overlay.ts +9 -5
- package/src/resources/extensions/bg-shell/process-manager.ts +50 -25
- package/src/resources/extensions/bg-shell/readiness-detector.ts +12 -0
- package/src/resources/extensions/bg-shell/tests/lifecycle-and-utilities.test.ts +48 -1
- package/src/resources/extensions/bg-shell/utilities.ts +3 -0
- package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +265 -98
- package/src/resources/extensions/browser-tools/engine/selection.ts +90 -4
- package/src/resources/extensions/browser-tools/index.ts +71 -13
- package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +83 -13
- package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +29 -1
- package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +136 -0
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +34 -4
- package/src/resources/extensions/gsd/auto/custom-verify-retry-store.ts +21 -3
- package/src/resources/extensions/gsd/auto/detect-stuck.ts +32 -9
- package/src/resources/extensions/gsd/auto/dispatch-history.ts +152 -0
- package/src/resources/extensions/gsd/auto/dispatch-key.ts +39 -0
- package/src/resources/extensions/gsd/auto/loop.ts +4 -1
- package/src/resources/extensions/gsd/auto/orchestrator.ts +70 -46
- package/src/resources/extensions/gsd/auto/phases.ts +2 -2
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +8 -32
- package/src/resources/extensions/gsd/auto-dispatch.ts +38 -52
- package/src/resources/extensions/gsd/auto-model-selection.ts +25 -5
- package/src/resources/extensions/gsd/auto-post-unit.ts +25 -8
- package/src/resources/extensions/gsd/auto-prompts.ts +118 -35
- package/src/resources/extensions/gsd/auto-start.ts +18 -17
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +19 -0
- package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +14 -21
- package/src/resources/extensions/gsd/auto-verification.ts +8 -26
- package/src/resources/extensions/gsd/auto-worktree.ts +30 -93
- package/src/resources/extensions/gsd/auto.ts +8 -15
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +3 -5
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +23 -6
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +24 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +251 -47
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +352 -84
- package/src/resources/extensions/gsd/branch-patterns.ts +3 -0
- package/src/resources/extensions/gsd/browser-daemon-auto-prep.ts +108 -0
- package/src/resources/extensions/gsd/browser-evidence.ts +18 -2
- package/src/resources/extensions/gsd/captures.ts +4 -6
- package/src/resources/extensions/gsd/consent-question.ts +416 -0
- package/src/resources/extensions/gsd/consent-verdict.ts +86 -0
- package/src/resources/extensions/gsd/constants.ts +0 -3
- package/src/resources/extensions/gsd/crash-recovery.ts +3 -9
- package/src/resources/extensions/gsd/db/queries.ts +37 -0
- package/src/resources/extensions/gsd/dispatch-guard.ts +8 -31
- package/src/resources/extensions/gsd/doctor-environment.ts +2 -7
- package/src/resources/extensions/gsd/doctor-format.ts +12 -7
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +13 -15
- package/src/resources/extensions/gsd/engine-hook-contract.ts +79 -0
- package/src/resources/extensions/gsd/error-classifier.ts +11 -0
- package/src/resources/extensions/gsd/exec-sandbox.ts +49 -9
- package/src/resources/extensions/gsd/files.ts +33 -12
- package/src/resources/extensions/gsd/guidance.ts +217 -0
- package/src/resources/extensions/gsd/guided-flow.ts +16 -2
- package/src/resources/extensions/gsd/markdown-renderer.ts +11 -0
- package/src/resources/extensions/gsd/mcp-filter.ts +2 -23
- package/src/resources/extensions/gsd/mcp-tool-name.ts +6 -11
- package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +1 -1
- package/src/resources/extensions/gsd/migrate/safety.ts +4 -1
- package/src/resources/extensions/gsd/milestone-closeout.ts +13 -23
- package/src/resources/extensions/gsd/notification-store.ts +26 -3
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +6 -4
- package/src/resources/extensions/gsd/parsers-legacy.ts +16 -4
- package/src/resources/extensions/gsd/paths.ts +33 -0
- package/src/resources/extensions/gsd/pre-execution-checks.ts +109 -3
- package/src/resources/extensions/gsd/preferences-models.ts +12 -47
- package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -2
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +6 -4
- package/src/resources/extensions/gsd/prompts/system.md +5 -2
- package/src/resources/extensions/gsd/provider-error-guidance.ts +4 -9
- package/src/resources/extensions/gsd/provider-switch-observer.ts +1 -1
- package/src/resources/extensions/gsd/publication.ts +122 -0
- package/src/resources/extensions/gsd/reactive-graph.ts +11 -1
- package/src/resources/extensions/gsd/recovery-classification.ts +42 -96
- package/src/resources/extensions/gsd/safety/destructive-confirmation.ts +134 -0
- package/src/resources/extensions/gsd/state.ts +9 -21
- package/src/resources/extensions/gsd/stop-notice.ts +75 -0
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +101 -26
- package/src/resources/extensions/gsd/tests/browser-automation-contract-fixture.ts +39 -0
- package/src/resources/extensions/gsd/tests/browser-contract.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/browser-daemon-auto-prep.test.ts +144 -0
- package/src/resources/extensions/gsd/tests/checkout-branch-stash-guard.test.ts +66 -1
- package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +8 -7
- package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/consent-question.test.ts +336 -0
- package/src/resources/extensions/gsd/tests/custom-verify-retry-store.test.ts +67 -0
- package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +10 -10
- package/src/resources/extensions/gsd/tests/destructive-confirmation.test.ts +303 -0
- package/src/resources/extensions/gsd/tests/dispatch-history.test.ts +273 -0
- package/src/resources/extensions/gsd/tests/dispatch-run-uat-browser-tools.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/dynamic-bash-no-cap.test.ts +132 -0
- package/src/resources/extensions/gsd/tests/engine-hook-contract.test.ts +148 -0
- package/src/resources/extensions/gsd/tests/exec-graceful-kill.test.ts +193 -0
- package/src/resources/extensions/gsd/tests/exec-tool.test.ts +29 -1
- package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +35 -1
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/guidance.test.ts +148 -0
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +53 -11
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +73 -58
- package/src/resources/extensions/gsd/tests/integration/gsd-integration-fixture.ts +80 -0
- package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +199 -0
- package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/model-unittype-mapping.test.ts +32 -1
- package/src/resources/extensions/gsd/tests/notification-store.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/oauth-api-model-routing.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +139 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +193 -1
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +124 -6
- package/src/resources/extensions/gsd/tests/provider-error-guidance.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/publication.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +157 -0
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/stop-notice.test.ts +70 -0
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/tool-surface-readiness.test.ts +155 -0
- package/src/resources/extensions/gsd/tests/uat-policy.test.ts +112 -29
- package/src/resources/extensions/gsd/tests/unit-closeout.test.ts +209 -0
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +67 -2
- package/src/resources/extensions/gsd/tests/unit-registry.test.ts +163 -0
- package/src/resources/extensions/gsd/tests/web-app-uat.test.ts +44 -1
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/write-gate-seam.test.ts +358 -0
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +67 -1
- package/src/resources/extensions/gsd/tool-presentation-plan.ts +4 -4
- package/src/resources/extensions/gsd/tool-surface-readiness.ts +76 -0
- package/src/resources/extensions/gsd/tools/complete-slice.ts +20 -10
- package/src/resources/extensions/gsd/tools/exec-tool.ts +8 -7
- package/src/resources/extensions/gsd/tools/plan-slice.ts +12 -6
- package/src/resources/extensions/gsd/uat-policy.ts +62 -16
- package/src/resources/extensions/gsd/unit-closeout.ts +201 -0
- package/src/resources/extensions/gsd/unit-context-composer.ts +111 -1
- package/src/resources/extensions/gsd/unit-context-manifest.ts +4 -28
- package/src/resources/extensions/gsd/unit-registry.ts +412 -0
- package/src/resources/extensions/gsd/unit-tool-contracts.ts +27 -192
- package/src/resources/extensions/gsd/verdict-parser.ts +1 -1
- package/src/resources/extensions/gsd/web-app-uat.ts +51 -8
- package/src/resources/extensions/gsd/workflow-tool-surface.ts +4 -1
- package/src/resources/extensions/gsd/worktree-git-recovery.ts +15 -9
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +3 -8
- package/src/resources/extensions/gsd/worktree-root.ts +12 -0
- package/src/resources/extensions/gsd/worktree-session-state.ts +3 -5
- package/src/resources/extensions/search-the-web/native-search.ts +5 -3
- package/src/resources/extensions/shared/browser-contract.ts +66 -0
- package/src/resources/extensions/shared/gsd-browser-cli.ts +119 -5
- package/src/resources/shared/package.json +3 -0
- package/src/resources/skills/create-skill/references/executable-code.md +1 -1
- package/src/resources/skills/create-skill/workflows/add-reference.md +8 -3
- package/src/resources/skills/create-skill/workflows/add-script.md +4 -2
- package/src/resources/skills/create-skill/workflows/add-template.md +3 -1
- package/src/resources/skills/create-skill/workflows/add-workflow.md +8 -3
- package/src/resources/skills/create-skill/workflows/upgrade-to-router.md +10 -5
- package/src/resources/skills/create-skill/workflows/verify-skill.md +9 -4
- package/src/resources/skills/spike-wrap-up/SKILL.md +9 -9
- package/dist/resources/extensions/gsd/user-input-boundary.js +0 -218
- package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +0 -173
- package/src/resources/extensions/gsd/user-input-boundary.ts +0 -216
- /package/dist/web/standalone/.next/static/{C24pqUd-aru-l0Dp0gLZP → jmTLg6xZmAuq_LIqKOxrH}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{C24pqUd-aru-l0Dp0gLZP → jmTLg6xZmAuq_LIqKOxrH}/_ssgManifest.js +0 -0
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { WindowEntry } from "./types.js";
|
|
8
|
+
import { parseDispatchKey } from "./dispatch-key.js";
|
|
8
9
|
import { summarizeLogs } from "../workflow-logger.js";
|
|
9
10
|
import { getLatestForUnit } from "../db/unit-dispatches.js";
|
|
10
11
|
|
|
@@ -15,6 +16,15 @@ import { getLatestForUnit } from "../db/unit-dispatches.js";
|
|
|
15
16
|
*/
|
|
16
17
|
const ENOENT_PATH_RE = /ENOENT[^']*'([^']+)'/;
|
|
17
18
|
|
|
19
|
+
function rowInsideRetryBudget(row: ReturnType<typeof getLatestForUnit>): boolean {
|
|
20
|
+
if (!row) return false;
|
|
21
|
+
if (row.attempt_n >= row.max_attempts) return false;
|
|
22
|
+
if (!row.next_run_at) return false;
|
|
23
|
+
const nextRun = Date.parse(row.next_run_at);
|
|
24
|
+
if (!Number.isFinite(nextRun)) return false;
|
|
25
|
+
return nextRun > Date.now();
|
|
26
|
+
}
|
|
27
|
+
|
|
18
28
|
/**
|
|
19
29
|
* Phase B / codex review MEDIUM B3 — retry coupling.
|
|
20
30
|
*
|
|
@@ -24,19 +34,26 @@ const ENOENT_PATH_RE = /ENOENT[^']*'([^']+)'/;
|
|
|
24
34
|
* waiting on its own backoff. Suppress the stuck verdict in that case so
|
|
25
35
|
* the retry budget can fully drain before we declare stuck.
|
|
26
36
|
*
|
|
37
|
+
* Window keys are compound (`unitType:unitId`, legacy `unitType/unitId`)
|
|
38
|
+
* while the production dispatch ledger keys rows by the bare unit id with
|
|
39
|
+
* the unit type in its own column. Look the bare unit id up first (with a
|
|
40
|
+
* unit_type match — the production shape), then fall back to the full
|
|
41
|
+
* compound key (test fixtures / legacy rows).
|
|
42
|
+
*
|
|
27
43
|
* Returns true if the dispatch ledger says we should suppress the stuck
|
|
28
44
|
* signal; false (no suppression) when the ledger is unavailable or has
|
|
29
45
|
* no opinion.
|
|
30
46
|
*/
|
|
31
47
|
function retryBudgetSuppresses(unitKey: string): boolean {
|
|
32
48
|
try {
|
|
33
|
-
const
|
|
34
|
-
if (
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
49
|
+
const parsed = parseDispatchKey(unitKey);
|
|
50
|
+
if (parsed) {
|
|
51
|
+
const bare = getLatestForUnit(parsed.unitId);
|
|
52
|
+
if (bare && bare.unit_type === parsed.unitType && rowInsideRetryBudget(bare)) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return rowInsideRetryBudget(getLatestForUnit(unitKey));
|
|
40
57
|
} catch {
|
|
41
58
|
return false;
|
|
42
59
|
}
|
|
@@ -71,6 +88,12 @@ export function detectStuck(
|
|
|
71
88
|
const last = window[window.length - 1];
|
|
72
89
|
const prev = window[window.length - 2];
|
|
73
90
|
|
|
91
|
+
// Rules 2 and 2b share one retry-budget verdict for `last.key` — compute it
|
|
92
|
+
// at most once per invocation (it hits the dispatch ledger).
|
|
93
|
+
let suppressionVerdict: boolean | undefined;
|
|
94
|
+
const suppressed = (): boolean =>
|
|
95
|
+
(suppressionVerdict ??= retryBudgetSuppresses(last.key));
|
|
96
|
+
|
|
74
97
|
// Rule 1: Same error repeated consecutively
|
|
75
98
|
if (last.error && prev.error && last.error === prev.error) {
|
|
76
99
|
return {
|
|
@@ -83,7 +106,7 @@ export function detectStuck(
|
|
|
83
106
|
// says we're inside the retry-backoff window (codex MEDIUM B3).
|
|
84
107
|
if (window.length >= 3) {
|
|
85
108
|
const lastThree = window.slice(-3);
|
|
86
|
-
if (lastThree.every((u) => u.key === last.key) && !
|
|
109
|
+
if (lastThree.every((u) => u.key === last.key) && !suppressed()) {
|
|
87
110
|
return {
|
|
88
111
|
stuck: true,
|
|
89
112
|
reason: `${last.key} derived 3 consecutive times without progress${suffix}`,
|
|
@@ -94,7 +117,7 @@ export function detectStuck(
|
|
|
94
117
|
// Rule 2b: Same unit key 3+ times anywhere in the active window — same
|
|
95
118
|
// retry-budget suppression as Rule 2.
|
|
96
119
|
const countInWindow = window.filter((entry) => entry.key === last.key).length;
|
|
97
|
-
if (countInWindow >= 3 && !
|
|
120
|
+
if (countInWindow >= 3 && !suppressed()) {
|
|
98
121
|
return {
|
|
99
122
|
stuck: true,
|
|
100
123
|
reason: `${last.key} derived ${countInWindow} times in last ${window.length} attempts without progress${suffix}`,
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Dispatch History module — the single home for the auto
|
|
3
|
+
// orchestrator's dispatch-decision window, cross-session rehydration, and
|
|
4
|
+
// stuck detection (#482 / #442 deepening).
|
|
5
|
+
/**
|
|
6
|
+
* auto/dispatch-history.ts — Dispatch History module.
|
|
7
|
+
*
|
|
8
|
+
* Owns the sliding window of recent dispatch decisions that the Auto
|
|
9
|
+
* Orchestration module consults for idempotency and stuck-loop detection.
|
|
10
|
+
*
|
|
11
|
+
* Before this module existed the orchestrator kept a private in-memory
|
|
12
|
+
* `dispatchKeyWindow: string[]` that was reset to `[]` in start()/resume().
|
|
13
|
+
* Because a fresh orchestrator is constructed per session, the window never
|
|
14
|
+
* saw dispatches from a previous session — a unit could be re-dispatched
|
|
15
|
+
* across session restarts indefinitely (issue #482: 146 re-dispatches of the
|
|
16
|
+
* same unit). This module rehydrates the window from the DB dispatch ledger
|
|
17
|
+
* (`unit_dispatches`, via getRecentUnitKeysForProjectRoot) so stuck detection
|
|
18
|
+
* survives process restarts, and it delegates the verdict to the full
|
|
19
|
+
* detect-stuck rule set (repeat-error / consecutive / oscillation / ENOENT,
|
|
20
|
+
* with retry-budget suppression) instead of the bare saturation count.
|
|
21
|
+
*
|
|
22
|
+
* Key format: the canonical dispatch key is `${unitType}:${unitId}`
|
|
23
|
+
* (e.g. "execute-task:M001/S01/T01"). The legacy auto/phases.ts path and the
|
|
24
|
+
* DB rehydration helper use `${unitType}/${unitId}`; normalizeDispatchKey
|
|
25
|
+
* converts those on rehydrate so one format lives in the window. The key
|
|
26
|
+
* grammar itself lives in auto/dispatch-key.ts and is re-exported here for
|
|
27
|
+
* import stability.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import type { WindowEntry } from "./types.js";
|
|
31
|
+
import { buildDispatchKey, normalizeDispatchKey } from "./dispatch-key.js";
|
|
32
|
+
import { detectStuck } from "./detect-stuck.js";
|
|
33
|
+
import {
|
|
34
|
+
getLatestForUnit,
|
|
35
|
+
getRecentUnitKeysForProjectRoot,
|
|
36
|
+
} from "../db/unit-dispatches.js";
|
|
37
|
+
import { debugLog } from "../debug-logger.js";
|
|
38
|
+
|
|
39
|
+
export { buildDispatchKey, normalizeDispatchKey, parseDispatchKey } from "./dispatch-key.js";
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Size of the dispatch-decision ring buffer. Mirrors the legacy
|
|
43
|
+
* `STUCK_WINDOW_SIZE` in auto/phases.ts so behaviour is preserved across the
|
|
44
|
+
* cutover (issue #5791).
|
|
45
|
+
*/
|
|
46
|
+
export const STUCK_WINDOW_SIZE = 6;
|
|
47
|
+
|
|
48
|
+
export interface DispatchHistory {
|
|
49
|
+
/**
|
|
50
|
+
* Record a dispatch decision in the window (evicting oldest-first past the
|
|
51
|
+
* window size). When the window already holds an entry for the same unit,
|
|
52
|
+
* attaches the latest ledger error summary so the repeat-error and ENOENT
|
|
53
|
+
* stuck rules can fire; first-time dispatches (the common case) skip the
|
|
54
|
+
* ledger lookup entirely. Returns the canonical key.
|
|
55
|
+
*/
|
|
56
|
+
recordDispatch(unitType: string, unitId: string): string;
|
|
57
|
+
/** Read-only view of the current window, oldest-first. */
|
|
58
|
+
getRecentWindow(): readonly WindowEntry[];
|
|
59
|
+
/** Number of window entries matching the given canonical key. */
|
|
60
|
+
countMatching(key: string): number;
|
|
61
|
+
/**
|
|
62
|
+
* Run the full detect-stuck rule set over the window (all four rules plus
|
|
63
|
+
* retry-budget suppression via the dispatch ledger).
|
|
64
|
+
*/
|
|
65
|
+
detectStuck(): { stuck: true; reason: string } | null;
|
|
66
|
+
/**
|
|
67
|
+
* Seed the window from the DB dispatch ledger (cross-session continuity,
|
|
68
|
+
* #482). Legacy `${unitType}/${unitId}` keys are normalized. Degrades to a
|
|
69
|
+
* no-op when the ledger is unavailable. Returns the number of entries
|
|
70
|
+
* rehydrated.
|
|
71
|
+
*/
|
|
72
|
+
rehydrate(): number;
|
|
73
|
+
/** Clear the window after a successful stuck recovery (or hard stop). */
|
|
74
|
+
clearOnRecovery(): void;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface DispatchHistoryOptions {
|
|
78
|
+
/**
|
|
79
|
+
* Stable project scope used to rehydrate from the dispatch ledger. Resolved
|
|
80
|
+
* lazily so worktree adoption after construction is respected. Return
|
|
81
|
+
* null/empty to skip rehydration.
|
|
82
|
+
*/
|
|
83
|
+
resolveScopeId: () => string | null;
|
|
84
|
+
windowSize?: number;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function lookupLatestLedgerError(unitType: string, unitId: string): string | undefined {
|
|
88
|
+
try {
|
|
89
|
+
const row = getLatestForUnit(unitId);
|
|
90
|
+
// The ledger keys rows by bare unit id; require a unit_type match so
|
|
91
|
+
// another unit type's error on the same id is never attached (it would
|
|
92
|
+
// trip the repeat-error rule spuriously).
|
|
93
|
+
if (!row || row.unit_type !== unitType) return undefined;
|
|
94
|
+
return row.error_summary ?? undefined;
|
|
95
|
+
} catch {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function createDispatchHistory(options: DispatchHistoryOptions): DispatchHistory {
|
|
101
|
+
const windowSize = options.windowSize ?? STUCK_WINDOW_SIZE;
|
|
102
|
+
let window: WindowEntry[] = [];
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
recordDispatch(unitType: string, unitId: string): string {
|
|
106
|
+
const key = buildDispatchKey(unitType, unitId);
|
|
107
|
+
// Ledger errors only feed the repeat-error/ENOENT rules, which need a
|
|
108
|
+
// prior occurrence of the same unit in the window — first-dispatch
|
|
109
|
+
// advances (the common case) pay zero DB cost.
|
|
110
|
+
const error = window.some((entry) => entry.key === key)
|
|
111
|
+
? lookupLatestLedgerError(unitType, unitId)
|
|
112
|
+
: undefined;
|
|
113
|
+
window.push({ key, error });
|
|
114
|
+
while (window.length > windowSize) window.shift();
|
|
115
|
+
return key;
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
getRecentWindow(): readonly WindowEntry[] {
|
|
119
|
+
return window;
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
countMatching(key: string): number {
|
|
123
|
+
return window.filter((entry) => entry.key === key).length;
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
detectStuck(): { stuck: true; reason: string } | null {
|
|
127
|
+
return detectStuck(window);
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
rehydrate(): number {
|
|
131
|
+
const scopeId = options.resolveScopeId();
|
|
132
|
+
if (!scopeId) return 0;
|
|
133
|
+
try {
|
|
134
|
+
const persisted = getRecentUnitKeysForProjectRoot(scopeId, windowSize);
|
|
135
|
+
if (persisted.length === 0) return 0;
|
|
136
|
+
window = persisted.map(({ key }) => ({ key: normalizeDispatchKey(key) }));
|
|
137
|
+
while (window.length > windowSize) window.shift();
|
|
138
|
+
return window.length;
|
|
139
|
+
} catch (err) {
|
|
140
|
+
debugLog("dispatchHistory", {
|
|
141
|
+
phase: "rehydrate-failed",
|
|
142
|
+
error: err instanceof Error ? err.message : String(err),
|
|
143
|
+
});
|
|
144
|
+
return 0;
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
clearOnRecovery(): void {
|
|
149
|
+
window = [];
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Dispatch-key grammar — the single home for building, parsing,
|
|
3
|
+
// and normalizing the auto orchestrator's dispatch keys.
|
|
4
|
+
/**
|
|
5
|
+
* auto/dispatch-key.ts — Dispatch-key grammar.
|
|
6
|
+
*
|
|
7
|
+
* Canonical key: `${unitType}:${unitId}` (e.g. "execute-task:M001/S01/T01").
|
|
8
|
+
* Legacy key: `${unitType}/${unitId}` (auto/phases.ts, DB rehydration). Unit
|
|
9
|
+
* ids themselves contain "/" (M001/S01/T01) — the first segment is the unit
|
|
10
|
+
* type.
|
|
11
|
+
*
|
|
12
|
+
* Leaf node in the import DAG: both dispatch-history.ts and detect-stuck.ts
|
|
13
|
+
* consume this grammar, so it lives below them.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** Build the canonical dispatch key for a unit. One format, one home. */
|
|
17
|
+
export function buildDispatchKey(unitType: string, unitId: string): string {
|
|
18
|
+
return `${unitType}:${unitId}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Split a canonical or legacy dispatch key into its unit type and id. */
|
|
22
|
+
export function parseDispatchKey(key: string): { unitType: string; unitId: string } | null {
|
|
23
|
+
const colon = key.indexOf(":");
|
|
24
|
+
if (colon > 0) {
|
|
25
|
+
return { unitType: key.slice(0, colon), unitId: key.slice(colon + 1) };
|
|
26
|
+
}
|
|
27
|
+
const slash = key.indexOf("/");
|
|
28
|
+
if (slash > 0) {
|
|
29
|
+
return { unitType: key.slice(0, slash), unitId: key.slice(slash + 1) };
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Normalize a legacy `${unitType}/${unitId}` key to the canonical format. */
|
|
35
|
+
export function normalizeDispatchKey(key: string): string {
|
|
36
|
+
if (key.includes(":")) return key;
|
|
37
|
+
const parsed = parseDispatchKey(key);
|
|
38
|
+
return parsed ? buildDispatchKey(parsed.unitType, parsed.unitId) : key;
|
|
39
|
+
}
|
|
@@ -25,7 +25,8 @@ import {
|
|
|
25
25
|
type IterationData,
|
|
26
26
|
} from "./types.js";
|
|
27
27
|
import { _clearCurrentResolve } from "./resolve.js";
|
|
28
|
-
import { runGuards, runFinalize
|
|
28
|
+
import { runGuards, runFinalize } from "./phases.js";
|
|
29
|
+
import { STUCK_WINDOW_SIZE } from "./dispatch-history.js";
|
|
29
30
|
import { debugLog } from "../debug-logger.js";
|
|
30
31
|
import { isInfrastructureError, isTransientCooldownError, getCooldownRetryAfterMs, COOLDOWN_FALLBACK_WAIT_MS, MAX_COOLDOWN_RETRIES } from "./infra-errors.js";
|
|
31
32
|
import { ModelPolicyDispatchBlockedError } from "../auto-model-selection.js";
|
|
@@ -389,6 +390,8 @@ export async function autoLoop(
|
|
|
389
390
|
const unitDispatchDeps = createExecutionGraphUnitDispatchDeps();
|
|
390
391
|
// Load persisted stuck state so counters survive session restarts (#3704)
|
|
391
392
|
const persisted = loadStuckState(s);
|
|
393
|
+
// Load persisted verification retry state so the exhausted-unit guard fires on restart (#651)
|
|
394
|
+
hydrateCustomVerifyRetryCounts(s, { logFailure: logCustomVerifyRetryLoadFailure });
|
|
392
395
|
const loopState: LoopState = {
|
|
393
396
|
recentUnits: persisted.recentUnits,
|
|
394
397
|
stuckRecoveryAttempts: persisted.stuckRecoveryAttempts,
|
|
@@ -64,6 +64,13 @@ import {
|
|
|
64
64
|
import { refreshWorkflowDatabaseFromDisk } from "../db-workspace.js";
|
|
65
65
|
import { getErrorMessage } from "../error-utils.js";
|
|
66
66
|
import { logWarning } from "../workflow-logger.js";
|
|
67
|
+
import { normalizeRealPath } from "../paths.js";
|
|
68
|
+
import {
|
|
69
|
+
buildDispatchKey,
|
|
70
|
+
createDispatchHistory,
|
|
71
|
+
STUCK_WINDOW_SIZE,
|
|
72
|
+
type DispatchHistory,
|
|
73
|
+
} from "./dispatch-history.js";
|
|
67
74
|
import { existsSync, readFileSync } from "node:fs";
|
|
68
75
|
import { join } from "node:path";
|
|
69
76
|
import { evaluateAllCompleteSettlement } from "../milestone-settlement.js";
|
|
@@ -72,16 +79,6 @@ function now(): number {
|
|
|
72
79
|
return Date.now();
|
|
73
80
|
}
|
|
74
81
|
|
|
75
|
-
/**
|
|
76
|
-
* Size of the dispatch-decision ring buffer used by the Auto Orchestration
|
|
77
|
-
* module's stuck-loop detector. When the same `${unitType}:${unitId}` key
|
|
78
|
-
* fills the window, advance() blocks with `action: "stop"`.
|
|
79
|
-
*
|
|
80
|
-
* Mirrors the legacy `STUCK_WINDOW_SIZE` in auto/phases.ts so behaviour is
|
|
81
|
-
* preserved across the eventual cutover (issue #5791).
|
|
82
|
-
*/
|
|
83
|
-
export const STUCK_WINDOW_SIZE = 6;
|
|
84
|
-
|
|
85
82
|
function noRemainingUnitsOutcome(stateSnapshot: GSDState): AutoTerminalOutcome {
|
|
86
83
|
if (stateSnapshot.phase === "complete") {
|
|
87
84
|
return {
|
|
@@ -330,7 +327,9 @@ export class AutoOrchestrator implements AutoOrchestrationModule {
|
|
|
330
327
|
private seq = 0;
|
|
331
328
|
private lastAdvanceKey: string | null = null;
|
|
332
329
|
private lastFinalizedUnitKey: string | null = null;
|
|
333
|
-
|
|
330
|
+
// Dispatch History module (#482): the dispatch-decision window with
|
|
331
|
+
// cross-session DB rehydration and full detect-stuck rules.
|
|
332
|
+
private readonly dispatchHistory: DispatchHistory;
|
|
334
333
|
// ADR-030 Phase Transition Invariant: the prior advance's reconciled Phase,
|
|
335
334
|
// the "from" endpoint of the edge check. In-memory; reset on start/resume/stop
|
|
336
335
|
// so the first advance of a session has no edge to assert.
|
|
@@ -347,6 +346,16 @@ export class AutoOrchestrator implements AutoOrchestrationModule {
|
|
|
347
346
|
this.runtimeBasePath = context.runtimeBasePath;
|
|
348
347
|
this.s = context.session;
|
|
349
348
|
this.flowId = `auto-orchestrator-${Date.now()}`;
|
|
349
|
+
this.dispatchHistory = createDispatchHistory({
|
|
350
|
+
windowSize: STUCK_WINDOW_SIZE,
|
|
351
|
+
// Same stable scope the auto-loop uses for stuck-state persistence so
|
|
352
|
+
// rehydration reads the rows the dispatch ledger wrote for this project.
|
|
353
|
+
resolveScopeId: () =>
|
|
354
|
+
normalizeRealPath(
|
|
355
|
+
this.s.scope?.workspace.projectRoot ??
|
|
356
|
+
(this.s.originalBasePath || this.s.basePath || this.runtimeBasePath),
|
|
357
|
+
) || null,
|
|
358
|
+
});
|
|
350
359
|
}
|
|
351
360
|
|
|
352
361
|
// ── Live base-path resolution (was the wiring factory's getLiveDispatchBasePath) ──
|
|
@@ -765,7 +774,7 @@ export class AutoOrchestrator implements AutoOrchestrationModule {
|
|
|
765
774
|
* skipped result) instead of stopping.
|
|
766
775
|
*/
|
|
767
776
|
private tryStuckArtifactRecovery(unitType: string, unitId: string): boolean {
|
|
768
|
-
const key =
|
|
777
|
+
const key = buildDispatchKey(unitType, unitId);
|
|
769
778
|
if (this.lastStuckRecoveryKey === key) return false; // already tried this episode
|
|
770
779
|
const basePath = this.getLiveDispatchBasePath();
|
|
771
780
|
if (!verifyExpectedArtifact(unitType, unitId, basePath)) return false;
|
|
@@ -777,7 +786,7 @@ export class AutoOrchestrator implements AutoOrchestrationModule {
|
|
|
777
786
|
if (!refreshed.ok && refreshed.fatal) return false;
|
|
778
787
|
this.lastStuckRecoveryKey = key;
|
|
779
788
|
invalidateAllCaches();
|
|
780
|
-
this.
|
|
789
|
+
this.dispatchHistory.clearOnRecovery();
|
|
781
790
|
this.lastAdvanceKey = null;
|
|
782
791
|
this.lastFinalizedUnitKey = null;
|
|
783
792
|
return true;
|
|
@@ -808,7 +817,12 @@ export class AutoOrchestrator implements AutoOrchestrationModule {
|
|
|
808
817
|
public async start(_sessionContext: AutoSessionContext): Promise<AutoAdvanceResult> {
|
|
809
818
|
this.lastAdvanceKey = null;
|
|
810
819
|
this.lastFinalizedUnitKey = null;
|
|
811
|
-
|
|
820
|
+
// #482: the DB dispatch ledger is the source of truth across sessions.
|
|
821
|
+
// Discard any in-memory window and rebuild it from the ledger so a unit
|
|
822
|
+
// that was re-dispatched in previous sessions is detected as stuck here
|
|
823
|
+
// instead of silently re-dispatching forever.
|
|
824
|
+
this.dispatchHistory.clearOnRecovery();
|
|
825
|
+
this.dispatchHistory.rehydrate();
|
|
812
826
|
this.lastStuckRecoveryKey = null;
|
|
813
827
|
this.lastDerivedPhase = null;
|
|
814
828
|
this.status.phase = "running";
|
|
@@ -913,7 +927,7 @@ export class AutoOrchestrator implements AutoOrchestrationModule {
|
|
|
913
927
|
this.status.phase = "paused";
|
|
914
928
|
this.status.activeUnit = undefined;
|
|
915
929
|
this.lastAdvanceKey = null;
|
|
916
|
-
this.
|
|
930
|
+
this.dispatchHistory.clearOnRecovery();
|
|
917
931
|
this.bumpTransition();
|
|
918
932
|
this.journalTransition({ name: "advance-blocked", reason: settlementBlock.reason });
|
|
919
933
|
this.postAdvanceRecord(settlementBlock);
|
|
@@ -929,7 +943,7 @@ export class AutoOrchestrator implements AutoOrchestrationModule {
|
|
|
929
943
|
this.status.phase = "stopped";
|
|
930
944
|
this.status.activeUnit = undefined;
|
|
931
945
|
this.lastAdvanceKey = null;
|
|
932
|
-
this.
|
|
946
|
+
this.dispatchHistory.clearOnRecovery();
|
|
933
947
|
this.bumpTransition();
|
|
934
948
|
this.journalTransition({ name: "advance-stopped", reason: stopped.reason });
|
|
935
949
|
this.postAdvanceRecord(stopped);
|
|
@@ -979,18 +993,13 @@ export class AutoOrchestrator implements AutoOrchestrationModule {
|
|
|
979
993
|
return blocked;
|
|
980
994
|
}
|
|
981
995
|
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
// Record every dispatch decision in the ring buffer before pre-flight
|
|
996
|
+
// Record every dispatch decision in the history window before pre-flight
|
|
985
997
|
// checks so the stuck-loop detector observes the full decision history
|
|
986
998
|
// (including decisions that idempotency would otherwise short-circuit).
|
|
987
|
-
// The
|
|
988
|
-
this.
|
|
989
|
-
if (this.dispatchKeyWindow.length > STUCK_WINDOW_SIZE) {
|
|
990
|
-
this.dispatchKeyWindow.shift();
|
|
991
|
-
}
|
|
999
|
+
// The window is capped at STUCK_WINDOW_SIZE and evicts oldest-first.
|
|
1000
|
+
const nextKey = this.dispatchHistory.recordDispatch(decision.unitType, decision.unitId);
|
|
992
1001
|
|
|
993
|
-
const matchingCount = this.
|
|
1002
|
+
const matchingCount = this.dispatchHistory.countMatching(nextKey);
|
|
994
1003
|
if (this.lastFinalizedUnitKey === nextKey) {
|
|
995
1004
|
// #442: the unit re-dispatched immediately after finalizing may have
|
|
996
1005
|
// actually completed on disk with a stale DB. Verify + recover before
|
|
@@ -1023,23 +1032,32 @@ export class AutoOrchestrator implements AutoOrchestrationModule {
|
|
|
1023
1032
|
// checks coexist: idempotency for the common immediate-repeat case,
|
|
1024
1033
|
// stuck-loop for the saturated-window case.
|
|
1025
1034
|
if (this.lastAdvanceKey === nextKey && matchingCount < STUCK_WINDOW_SIZE) {
|
|
1035
|
+
// Unit already active — benign no-op. Return skipped so the loop re-polls
|
|
1036
|
+
// without cancelling the in-flight unit (blocked+pause would force-cancel it).
|
|
1026
1037
|
this.clearPendingDispatch();
|
|
1027
|
-
const
|
|
1038
|
+
const skipped: AutoAdvanceResult = { kind: "skipped", reason: "idempotent advance: unit already active" };
|
|
1028
1039
|
this.journalTransition({
|
|
1029
|
-
name: "advance-
|
|
1030
|
-
reason:
|
|
1040
|
+
name: "advance-skipped",
|
|
1041
|
+
reason: skipped.reason,
|
|
1031
1042
|
unitType: decision.unitType,
|
|
1032
1043
|
unitId: decision.unitId,
|
|
1033
1044
|
});
|
|
1034
|
-
this.postAdvanceRecord(
|
|
1035
|
-
return
|
|
1045
|
+
this.postAdvanceRecord(skipped);
|
|
1046
|
+
return skipped;
|
|
1036
1047
|
}
|
|
1037
1048
|
|
|
1038
|
-
// Stuck-loop detection: when the
|
|
1039
|
-
// `nextKey` (count >= STUCK_WINDOW_SIZE), the
|
|
1040
|
-
//
|
|
1041
|
-
//
|
|
1042
|
-
|
|
1049
|
+
// Stuck-loop detection: when the window is saturated with copies of
|
|
1050
|
+
// `nextKey` (count >= STUCK_WINDOW_SIZE), consult the Dispatch History
|
|
1051
|
+
// module's full detect-stuck rule set for the verdict instead of the old
|
|
1052
|
+
// bare saturation count. This keeps the saturation threshold (the window
|
|
1053
|
+
// deliberately records benign idempotent repeats, so earlier-firing
|
|
1054
|
+
// rules would false-positive on pause/resume re-advances) while gaining
|
|
1055
|
+
// retry-budget suppression and diagnosable rule reasons. A saturated
|
|
1056
|
+
// window with no verdict means the dispatch ledger says we are inside
|
|
1057
|
+
// the unit's retry-backoff budget — let the retry proceed.
|
|
1058
|
+
const stuckVerdict =
|
|
1059
|
+
matchingCount >= STUCK_WINDOW_SIZE ? this.dispatchHistory.detectStuck() : null;
|
|
1060
|
+
if (stuckVerdict) {
|
|
1043
1061
|
// #442: before declaring a stuck loop, verify the unit didn't actually
|
|
1044
1062
|
// complete on disk (stale DB) and recover if so — legacy graduated
|
|
1045
1063
|
// stuck-recovery parity. Otherwise hard-stop with a diagnosable reason.
|
|
@@ -1050,7 +1068,7 @@ export class AutoOrchestrator implements AutoOrchestrationModule {
|
|
|
1050
1068
|
this.clearPendingDispatch();
|
|
1051
1069
|
const blocked: AutoAdvanceResult = {
|
|
1052
1070
|
kind: "blocked",
|
|
1053
|
-
reason: `stuck-loop: ${
|
|
1071
|
+
reason: `stuck-loop: ${stuckVerdict.reason}`,
|
|
1054
1072
|
action: "stop",
|
|
1055
1073
|
};
|
|
1056
1074
|
this.journalTransition({
|
|
@@ -1144,7 +1162,7 @@ export class AutoOrchestrator implements AutoOrchestrationModule {
|
|
|
1144
1162
|
if (result.kind === "stopped") {
|
|
1145
1163
|
this.lastAdvanceKey = null;
|
|
1146
1164
|
this.lastFinalizedUnitKey = null;
|
|
1147
|
-
this.
|
|
1165
|
+
this.dispatchHistory.clearOnRecovery();
|
|
1148
1166
|
this.status.activeUnit = undefined;
|
|
1149
1167
|
}
|
|
1150
1168
|
this.bumpTransition();
|
|
@@ -1173,8 +1191,14 @@ export class AutoOrchestrator implements AutoOrchestrationModule {
|
|
|
1173
1191
|
public async resume(): Promise<AutoAdvanceResult> {
|
|
1174
1192
|
this.lastAdvanceKey = null;
|
|
1175
1193
|
this.lastFinalizedUnitKey = null;
|
|
1176
|
-
// Preserve
|
|
1177
|
-
// accumulates across pause/resume cycles rather than
|
|
1194
|
+
// Preserve the dispatch-history window across an in-process resume so
|
|
1195
|
+
// stuck-loop detection accumulates across pause/resume cycles rather than
|
|
1196
|
+
// resetting each time (#572 regression). When the window is empty (fresh
|
|
1197
|
+
// orchestrator resuming a prior session), rehydrate it from the DB
|
|
1198
|
+
// dispatch ledger so cross-session re-dispatch loops are detected (#482).
|
|
1199
|
+
if (this.dispatchHistory.getRecentWindow().length === 0) {
|
|
1200
|
+
this.dispatchHistory.rehydrate();
|
|
1201
|
+
}
|
|
1178
1202
|
this.lastStuckRecoveryKey = null;
|
|
1179
1203
|
// ADR-030: drop the prior "from" — the first advance after resume has no
|
|
1180
1204
|
// edge to assert (avoids a false illegal-edge across the pause boundary).
|
|
@@ -1196,10 +1220,10 @@ export class AutoOrchestrator implements AutoOrchestrationModule {
|
|
|
1196
1220
|
this.lastAdvanceKey = null;
|
|
1197
1221
|
this.lastFinalizedUnitKey = null;
|
|
1198
1222
|
this.lastDerivedPhase = null;
|
|
1199
|
-
// Preserve
|
|
1200
|
-
// across pause/resume cycles. Only clear on a hard stop.
|
|
1223
|
+
// Preserve the dispatch-history window on pause so stuck-loop detection
|
|
1224
|
+
// accumulates across pause/resume cycles. Only clear on a hard stop.
|
|
1201
1225
|
if (reason !== "pause") {
|
|
1202
|
-
this.
|
|
1226
|
+
this.dispatchHistory.clearOnRecovery();
|
|
1203
1227
|
}
|
|
1204
1228
|
this.lastStuckRecoveryKey = null;
|
|
1205
1229
|
this.bumpTransition();
|
|
@@ -1213,9 +1237,9 @@ export class AutoOrchestrator implements AutoOrchestrationModule {
|
|
|
1213
1237
|
}
|
|
1214
1238
|
|
|
1215
1239
|
public async completeActiveUnit(unit: { unitType: string; unitId: string }): Promise<void> {
|
|
1216
|
-
const unitKey =
|
|
1240
|
+
const unitKey = buildDispatchKey(unit.unitType, unit.unitId);
|
|
1217
1241
|
const activeUnitKey = this.status.activeUnit
|
|
1218
|
-
?
|
|
1242
|
+
? buildDispatchKey(this.status.activeUnit.unitType, this.status.activeUnit.unitId)
|
|
1219
1243
|
: null;
|
|
1220
1244
|
if (activeUnitKey !== unitKey) return;
|
|
1221
1245
|
|
|
@@ -1233,9 +1257,9 @@ export class AutoOrchestrator implements AutoOrchestrationModule {
|
|
|
1233
1257
|
}
|
|
1234
1258
|
|
|
1235
1259
|
public async retryActiveUnit(unit: { unitType: string; unitId: string }): Promise<void> {
|
|
1236
|
-
const unitKey =
|
|
1260
|
+
const unitKey = buildDispatchKey(unit.unitType, unit.unitId);
|
|
1237
1261
|
const activeUnitKey = this.status.activeUnit
|
|
1238
|
-
?
|
|
1262
|
+
? buildDispatchKey(this.status.activeUnit.unitType, this.status.activeUnit.unitId)
|
|
1239
1263
|
: null;
|
|
1240
1264
|
if (activeUnitKey !== unitKey && this.lastFinalizedUnitKey !== unitKey) return;
|
|
1241
1265
|
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
type PostUnitContext,
|
|
20
20
|
type PreVerificationOpts,
|
|
21
21
|
} from "../auto-post-unit.js";
|
|
22
|
-
import { lastAssistantText } from "../
|
|
22
|
+
import { lastAssistantText } from "../consent-question.js";
|
|
23
23
|
import { resolveEffectiveUnitIsolationMode } from "../preferences.js";
|
|
24
24
|
import type { Phase } from "../types.js";
|
|
25
25
|
import {
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
type IterationData,
|
|
34
34
|
} from "./types.js";
|
|
35
35
|
import { detectStuck } from "./detect-stuck.js";
|
|
36
|
+
import { STUCK_WINDOW_SIZE } from "./dispatch-history.js";
|
|
36
37
|
import { runUnit } from "./run-unit.js";
|
|
37
38
|
import { debugLog } from "../debug-logger.js";
|
|
38
39
|
import { resolveWorktreeProjectRoot, normalizeWorktreePathForCompare } from "../worktree-root.js";
|
|
@@ -107,7 +108,6 @@ import {
|
|
|
107
108
|
} from "../root-write-leak-guard.js";
|
|
108
109
|
import { classifyError, isTransient } from "../error-classifier.js";
|
|
109
110
|
|
|
110
|
-
export const STUCK_WINDOW_SIZE = 6;
|
|
111
111
|
const STUCK_RECOVERY_ATTEMPTS_KEY = "stuck_recovery_attempts";
|
|
112
112
|
const ZERO_TOOL_PROVIDER_ERROR_PREFIX_RE =
|
|
113
113
|
/^(?:api error(?::|$|\s*\()|provider error(?::|$|\s*\()|request failed\b|(?:http\s*)?(?:429|500|502|503)\b|\b(?:econnreset|etimedout|econnrefused|epipe)\b|socket hang up\b|fetch failed\b|(?:network|connection|server) error(?::|$)|connection (?:reset|refused)(?::|$|\s+by\b)|dns\b.*(?:fail|error|timeout)|unexpected eof\b|stream idle timeout\b|partial response received\b|stream_exhausted\b|terminated(?::|$)|(?:connection|stream|request)\b.{0,40}\bterminated\b|other side closed\b|rate.?limit(?:ed| exceeded| reached| error)|too many requests\b|you(?:'ve| have) (?:hit|reached) your (?:\w+ )?limit\b|.*\b(?:usage|session|weekly|daily|monthly|quota) limit\b|limit\b.{0,40}\bresets?\b|out of extra usage\b|service.?unavailable\b|internal(?: server)? error(?::|$)|internal(?:[_-]server)?[_-]error\b|server[_-]error\b|(?:provider|server|api|model|codex|claude|openai|anthropic|gemini)\b.{0,80}\boverloaded\b|overloaded\b.{0,80}\b(?:provider|server|api|model)\b|context (?:window|length) exceed|context window exceed)/i;
|
|
@@ -10,10 +10,9 @@ import type {
|
|
|
10
10
|
|
|
11
11
|
import { deriveState } from "./state.js";
|
|
12
12
|
import { loadFile } from "./files.js";
|
|
13
|
-
import { isDbAvailable,
|
|
14
|
-
import { parseRoadmap } from "./parsers-legacy.js";
|
|
13
|
+
import { isDbAvailable, getClosedSliceIds } from "./gsd-db.js";
|
|
15
14
|
import {
|
|
16
|
-
|
|
15
|
+
resolveSliceFile, relSliceFile,
|
|
17
16
|
} from "./paths.js";
|
|
18
17
|
import {
|
|
19
18
|
buildResearchSlicePrompt,
|
|
@@ -182,21 +181,9 @@ export async function dispatchDirectPhase(
|
|
|
182
181
|
|
|
183
182
|
case "reassess":
|
|
184
183
|
case "reassess-roadmap": {
|
|
185
|
-
// DB
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
completedSliceIds = getMilestoneSlices(mid).filter(s => s.status === "complete").map(s => s.id);
|
|
189
|
-
}
|
|
190
|
-
if (completedSliceIds.length === 0) {
|
|
191
|
-
// File-based fallback: parse roadmap checkboxes
|
|
192
|
-
const roadmapPath = resolveMilestoneFile(dispatchBase, mid, "ROADMAP");
|
|
193
|
-
if (roadmapPath) {
|
|
194
|
-
const roadmapContent = await loadFile(roadmapPath);
|
|
195
|
-
if (roadmapContent) {
|
|
196
|
-
completedSliceIds = parseRoadmap(roadmapContent).slices.filter(s => s.done).map(s => s.id);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
184
|
+
// DB-authoritative read (ADR-017) — markdown projections are never
|
|
185
|
+
// consulted for dispatch decisions. No DB rows means no completed slices.
|
|
186
|
+
const completedSliceIds = isDbAvailable() ? getClosedSliceIds(mid) : [];
|
|
200
187
|
if (completedSliceIds.length === 0) {
|
|
201
188
|
ctx.ui.notify("Cannot dispatch reassess-roadmap: no completed slices.", "warning");
|
|
202
189
|
return;
|
|
@@ -222,20 +209,9 @@ export async function dispatchDirectPhase(
|
|
|
222
209
|
// incomplete) slice. After slice completion, state.activeSlice advances
|
|
223
210
|
// to the next incomplete slice, so we find the last done slice from the
|
|
224
211
|
// roadmap instead (#1693).
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
229
|
-
if (uatCompletedSliceIds.length === 0) {
|
|
230
|
-
// File-based fallback: parse roadmap checkboxes
|
|
231
|
-
const roadmapPath = resolveMilestoneFile(dispatchBase, mid, "ROADMAP");
|
|
232
|
-
if (roadmapPath) {
|
|
233
|
-
const roadmapContent = await loadFile(roadmapPath);
|
|
234
|
-
if (roadmapContent) {
|
|
235
|
-
uatCompletedSliceIds = parseRoadmap(roadmapContent).slices.filter(s => s.done).map(s => s.id);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
212
|
+
// DB-authoritative read (ADR-017) — no markdown fallback for dispatch
|
|
213
|
+
// decisions.
|
|
214
|
+
const uatCompletedSliceIds = isDbAvailable() ? getClosedSliceIds(mid) : [];
|
|
239
215
|
if (uatCompletedSliceIds.length === 0) {
|
|
240
216
|
ctx.ui.notify("Cannot dispatch run-uat: no completed slices.", "warning");
|
|
241
217
|
return;
|