gsd-pi 2.77.0-dev.eaa4973bc → 2.78.0-dev.aeeb2ca00
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -17
- package/dist/claude-cli-check.js +46 -10
- package/dist/headless.js +49 -4
- package/dist/resource-loader.d.ts +40 -0
- package/dist/resource-loader.js +32 -13
- package/dist/resources/extensions/browser-tools/capture.js +9 -0
- package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
- package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
- package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
- package/dist/resources/extensions/browser-tools/tools/forms.js +5 -1
- package/dist/resources/extensions/browser-tools/tools/intent.js +5 -1
- package/dist/resources/extensions/claude-code-cli/readiness.js +72 -16
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +481 -17
- package/dist/resources/extensions/github-sync/templates.js +103 -0
- package/dist/resources/extensions/google-search/index.js +3 -2
- package/dist/resources/extensions/gsd/auto/loop.js +124 -2
- package/dist/resources/extensions/gsd/auto/phases.js +57 -39
- package/dist/resources/extensions/gsd/auto/session.js +6 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +142 -29
- package/dist/resources/extensions/gsd/auto-model-selection.js +124 -4
- package/dist/resources/extensions/gsd/auto-post-unit.js +150 -64
- package/dist/resources/extensions/gsd/auto-prompts.js +372 -104
- package/dist/resources/extensions/gsd/auto-recovery.js +197 -48
- package/dist/resources/extensions/gsd/auto-start.js +107 -29
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +47 -7
- package/dist/resources/extensions/gsd/auto-worktree.js +122 -26
- package/dist/resources/extensions/gsd/auto.js +76 -21
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +19 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +209 -0
- package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +3 -6
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -3
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +127 -9
- package/dist/resources/extensions/gsd/component-loader.js +447 -0
- package/dist/resources/extensions/gsd/component-types.js +69 -0
- package/dist/resources/extensions/gsd/context-store.js +23 -7
- package/dist/resources/extensions/gsd/detection.js +49 -1
- package/dist/resources/extensions/gsd/dispatch-guard.js +2 -17
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
- package/dist/resources/extensions/gsd/forensics.js +106 -0
- package/dist/resources/extensions/gsd/gate-registry.js +2 -2
- package/dist/resources/extensions/gsd/git-constants.js +28 -1
- package/dist/resources/extensions/gsd/git-self-heal.js +27 -0
- package/dist/resources/extensions/gsd/git-service.js +126 -2
- package/dist/resources/extensions/gsd/gsd-db.js +6 -3
- package/dist/resources/extensions/gsd/guided-flow.js +39 -13
- package/dist/resources/extensions/gsd/memory-extractor.js +7 -1
- package/dist/resources/extensions/gsd/milestone-scope-classifier.js +299 -0
- package/dist/resources/extensions/gsd/milestone-summary-classifier.js +37 -0
- package/dist/resources/extensions/gsd/model-cost-table.js +3 -0
- package/dist/resources/extensions/gsd/model-router.js +6 -0
- package/dist/resources/extensions/gsd/native-git-bridge.js +34 -4
- package/dist/resources/extensions/gsd/preferences-validation.js +23 -0
- package/dist/resources/extensions/gsd/prompt-cache-optimizer.js +4 -0
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +23 -4
- package/dist/resources/extensions/gsd/prompts/doctor-heal.md +5 -4
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -2
- package/dist/resources/extensions/gsd/safety/git-checkpoint.js +11 -0
- package/dist/resources/extensions/gsd/service-tier.js +5 -2
- package/dist/resources/extensions/gsd/session-lock.js +19 -10
- package/dist/resources/extensions/gsd/skill-manifest.js +168 -0
- package/dist/resources/extensions/gsd/slice-cadence.js +238 -0
- package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +278 -8
- package/dist/resources/extensions/gsd/state-transition-matrix.js +118 -0
- package/dist/resources/extensions/gsd/state.js +69 -58
- package/dist/resources/extensions/gsd/sync-lock.js +98 -42
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +7 -2
- package/dist/resources/extensions/gsd/unit-context-composer.js +147 -0
- package/dist/resources/extensions/gsd/unit-context-manifest.js +370 -0
- package/dist/resources/extensions/gsd/uok/dispatch-envelope.js +33 -0
- package/dist/resources/extensions/gsd/uok/execution-graph.js +10 -0
- package/dist/resources/extensions/gsd/uok/gate-runner.js +53 -5
- package/dist/resources/extensions/gsd/uok/gitops.js +2 -1
- package/dist/resources/extensions/gsd/uok/loop-adapter.js +37 -10
- package/dist/resources/extensions/gsd/uok/parity-report.js +58 -0
- package/dist/resources/extensions/gsd/uok/plan-v2.js +10 -4
- package/dist/resources/extensions/gsd/uok/writer.js +82 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +6 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +85 -8
- package/dist/resources/extensions/gsd/worktree-resolver.js +86 -7
- package/dist/resources/extensions/gsd/worktree-telemetry.js +198 -0
- package/dist/resources/extensions/mcp-client/index.js +3 -1
- package/dist/resources/extensions/ollama/index.js +5 -1
- package/dist/resources/extensions/remote-questions/manager.js +11 -5
- 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 +11 -11
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- 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/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +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 +11 -11
- package/dist/web/standalone/.next/server/chunks/1926.js +1 -1
- package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
- 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/package.json +2 -3
- package/packages/daemon/package.json +2 -2
- package/packages/daemon/src/logger.ts +4 -3
- package/packages/mcp-server/dist/server.d.ts +24 -0
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +88 -87
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +15 -6
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +2 -2
- package/packages/mcp-server/src/mcp-server.test.ts +25 -3
- package/packages/mcp-server/src/readers/graph.test.ts +87 -15
- package/packages/mcp-server/src/secure-env-collect.test.ts +232 -237
- package/packages/mcp-server/src/server.ts +131 -105
- package/packages/mcp-server/src/workflow-tools.test.ts +85 -0
- package/packages/mcp-server/src/workflow-tools.ts +19 -6
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/native/package.json +2 -2
- package/packages/native/src/__tests__/_test-coverage-guard.test.mjs +98 -0
- package/packages/native/src/__tests__/module-compat.test.mjs +59 -27
- package/packages/native/src/__tests__/ps.test.mjs +14 -8
- package/packages/native/src/__tests__/stream-process.test.mjs +23 -2
- package/packages/native/src/__tests__/truncate.test.mjs +17 -2
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-agent-core/src/agent-loop.test.ts +5 -15
- package/packages/pi-agent-core/src/agent.test.ts +96 -102
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-ai/dist/models/capability-patches.d.ts.map +1 -1
- package/packages/pi-ai/dist/models/capability-patches.js +9 -2
- package/packages/pi-ai/dist/models/capability-patches.js.map +1 -1
- package/packages/pi-ai/dist/models/generated/index.d.ts +34 -0
- package/packages/pi-ai/dist/models/generated/index.d.ts.map +1 -1
- package/packages/pi-ai/dist/models/generated/openai-codex.d.ts +17 -0
- package/packages/pi-ai/dist/models/generated/openai-codex.d.ts.map +1 -1
- package/packages/pi-ai/dist/models/generated/openai-codex.js +17 -0
- package/packages/pi-ai/dist/models/generated/openai-codex.js.map +1 -1
- package/packages/pi-ai/dist/models/generated/openai.d.ts +17 -0
- package/packages/pi-ai/dist/models/generated/openai.d.ts.map +1 -1
- package/packages/pi-ai/dist/models/generated/openai.js +17 -0
- package/packages/pi-ai/dist/models/generated/openai.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.test.js +43 -70
- package/packages/pi-ai/dist/models.generated.test.js.map +1 -1
- package/packages/pi-ai/dist/models.test.js +36 -11
- package/packages/pi-ai/dist/models.test.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-ai/scripts/generate-models.ts +44 -0
- package/packages/pi-ai/src/models/capability-patches.ts +10 -2
- package/packages/pi-ai/src/models/generated/openai-codex.ts +17 -0
- package/packages/pi-ai/src/models/generated/openai.ts +17 -0
- package/packages/pi-ai/src/models.generated.test.ts +46 -73
- package/packages/pi-ai/src/models.test.ts +48 -11
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +96 -32
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js +75 -12
- package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +99 -31
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +61 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +30 -4
- package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +17 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +76 -18
- package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js +2 -6
- package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js +5 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts +18 -0
- package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/retryable-error-regex.js +18 -0
- package/packages/pi-coding-agent/dist/core/retryable-error-regex.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +20 -0
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +16 -2
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -0
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -0
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +36 -5
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +20 -13
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +30 -12
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +18 -3
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +125 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +105 -13
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js +130 -0
- package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js.map +1 -0
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +113 -37
- package/packages/pi-coding-agent/src/core/agent-session-model-switch.test.ts +89 -17
- package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +112 -43
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +58 -0
- package/packages/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +35 -4
- package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +20 -0
- package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +93 -28
- package/packages/pi-coding-agent/src/core/retry-handler.test.ts +5 -1
- package/packages/pi-coding-agent/src/core/retry-handler.ts +2 -8
- package/packages/pi-coding-agent/src/core/retryable-error-regex.ts +18 -0
- package/packages/pi-coding-agent/src/core/system-prompt.ts +35 -1
- package/packages/pi-coding-agent/src/index.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +49 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +26 -20
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +48 -9
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +146 -1
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +20 -3
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +2 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +119 -13
- package/packages/pi-coding-agent/src/tests/system-prompt-skill-filter.test.ts +157 -0
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js +18 -8
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +128 -17
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
- package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +37 -11
- package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -1
- package/packages/pi-tui/dist/__tests__/tui.test.js +18 -30
- package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/input.test.js +10 -3
- package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/loader.test.js +53 -9
- package/packages/pi-tui/dist/components/__tests__/loader.test.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +6 -2
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -1
- package/packages/pi-tui/dist/components/editor.d.ts +14 -0
- package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/editor.js +19 -0
- package/packages/pi-tui/dist/components/editor.js.map +1 -1
- package/packages/pi-tui/dist/components/image.test.js +6 -5
- package/packages/pi-tui/dist/components/image.test.js.map +1 -1
- package/packages/pi-tui/dist/editor-component.d.ts +2 -0
- package/packages/pi-tui/dist/editor-component.d.ts.map +1 -1
- package/packages/pi-tui/dist/editor-component.js.map +1 -1
- package/packages/pi-tui/package.json +1 -1
- package/packages/pi-tui/src/__tests__/autocomplete.test.ts +24 -8
- package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +140 -17
- package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +42 -11
- package/packages/pi-tui/src/__tests__/tui.test.ts +18 -37
- package/packages/pi-tui/src/components/__tests__/input.test.ts +19 -3
- package/packages/pi-tui/src/components/__tests__/loader.test.ts +112 -35
- package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +9 -2
- package/packages/pi-tui/src/components/editor.ts +22 -0
- package/packages/pi-tui/src/components/image.test.ts +10 -5
- package/packages/pi-tui/src/editor-component.ts +3 -0
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/packages/rpc-client/dist/rpc-client.test.js +101 -51
- package/packages/rpc-client/dist/rpc-client.test.js.map +1 -1
- package/packages/rpc-client/package.json +1 -1
- package/packages/rpc-client/src/rpc-client.test.ts +109 -52
- package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
- package/pkg/package.json +1 -1
- package/scripts/install.js +15 -1
- package/src/resources/extensions/browser-tools/capture.ts +12 -0
- package/src/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
- package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
- package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
- package/src/resources/extensions/browser-tools/tools/forms.ts +5 -1
- package/src/resources/extensions/browser-tools/tools/intent.ts +5 -1
- package/src/resources/extensions/claude-code-cli/readiness.ts +75 -16
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +518 -19
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +919 -75
- package/src/resources/extensions/github-sync/templates.ts +151 -0
- package/src/resources/extensions/github-sync/tests/cli.test.ts +76 -7
- package/src/resources/extensions/github-sync/tests/templates.test.ts +92 -1
- package/src/resources/extensions/google-search/index.ts +3 -2
- package/src/resources/extensions/gsd/auto/loop.ts +142 -2
- package/src/resources/extensions/gsd/auto/phases.ts +62 -38
- package/src/resources/extensions/gsd/auto/session.ts +7 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +156 -29
- package/src/resources/extensions/gsd/auto-model-selection.ts +131 -4
- package/src/resources/extensions/gsd/auto-post-unit.ts +163 -73
- package/src/resources/extensions/gsd/auto-prompts.ts +385 -93
- package/src/resources/extensions/gsd/auto-recovery.ts +230 -51
- package/src/resources/extensions/gsd/auto-start.ts +127 -9
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +51 -7
- package/src/resources/extensions/gsd/auto-worktree.ts +130 -26
- package/src/resources/extensions/gsd/auto.ts +90 -23
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +20 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +221 -0
- package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +3 -7
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +7 -3
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +158 -9
- package/src/resources/extensions/gsd/component-loader.ts +598 -0
- package/src/resources/extensions/gsd/component-types.ts +362 -0
- package/src/resources/extensions/gsd/context-store.ts +25 -8
- package/src/resources/extensions/gsd/detection.ts +58 -1
- package/src/resources/extensions/gsd/dispatch-guard.ts +2 -20
- package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
- package/src/resources/extensions/gsd/forensics.ts +118 -1
- package/src/resources/extensions/gsd/gate-registry.ts +2 -2
- package/src/resources/extensions/gsd/git-constants.ts +30 -1
- package/src/resources/extensions/gsd/git-self-heal.ts +31 -0
- package/src/resources/extensions/gsd/git-service.ts +149 -2
- package/src/resources/extensions/gsd/gsd-db.ts +6 -3
- package/src/resources/extensions/gsd/guided-flow.ts +57 -14
- package/src/resources/extensions/gsd/journal.ts +11 -1
- package/src/resources/extensions/gsd/memory-extractor.ts +11 -3
- package/src/resources/extensions/gsd/milestone-scope-classifier.ts +366 -0
- package/src/resources/extensions/gsd/milestone-summary-classifier.ts +42 -0
- package/src/resources/extensions/gsd/model-cost-table.ts +3 -0
- package/src/resources/extensions/gsd/model-router.ts +6 -0
- package/src/resources/extensions/gsd/native-git-bridge.ts +34 -4
- package/src/resources/extensions/gsd/preferences-validation.ts +21 -0
- package/src/resources/extensions/gsd/prompt-cache-optimizer.ts +4 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +23 -4
- package/src/resources/extensions/gsd/prompts/doctor-heal.md +5 -4
- package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -2
- package/src/resources/extensions/gsd/safety/git-checkpoint.ts +15 -0
- package/src/resources/extensions/gsd/service-tier.ts +5 -2
- package/src/resources/extensions/gsd/session-lock.ts +20 -10
- package/src/resources/extensions/gsd/skill-manifest.ts +175 -0
- package/src/resources/extensions/gsd/slice-cadence.ts +299 -0
- package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +309 -8
- package/src/resources/extensions/gsd/state-transition-matrix.ts +152 -0
- package/src/resources/extensions/gsd/state.ts +76 -66
- package/src/resources/extensions/gsd/sync-lock.ts +97 -39
- package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +270 -0
- package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +341 -0
- package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +264 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +133 -292
- package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +742 -0
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +78 -0
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +61 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +4 -1
- package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +8 -194
- package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/auto-start-cold-db-bootstrap.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +15 -58
- package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +17 -21
- package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +108 -0
- package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +263 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +192 -0
- package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +16 -8
- package/src/resources/extensions/gsd/tests/component-loader.test.ts +589 -0
- package/src/resources/extensions/gsd/tests/component-types.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/context-store.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +50 -1
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +159 -0
- package/src/resources/extensions/gsd/tests/db-access-guardrails.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +91 -3
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +5 -0
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/execution-entry-missing-context-4671.test.ts +173 -0
- package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +139 -129
- package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +8 -104
- package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +102 -0
- package/src/resources/extensions/gsd/tests/gate-storage.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/google-search-stub.test.ts +14 -4
- package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +117 -0
- package/src/resources/extensions/gsd/tests/hook-key-parsing.test.ts +4 -55
- package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +7 -56
- package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +18 -2
- package/src/resources/extensions/gsd/tests/integration/queue-completed-milestone-perf.test.ts +10 -4
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +144 -7
- package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +2 -16
- package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +9 -3
- package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +6 -9
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +64 -0
- package/src/resources/extensions/gsd/tests/knowledge.test.ts +93 -1
- package/src/resources/extensions/gsd/tests/mcp-client-security.test.ts +8 -37
- package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +5 -15
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +227 -55
- package/src/resources/extensions/gsd/tests/milestone-scope-classifier.test.ts +187 -0
- package/src/resources/extensions/gsd/tests/milestone-summary-classifier.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +9 -1
- package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +6 -48
- package/src/resources/extensions/gsd/tests/notification-widget.test.ts +6 -3
- package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +59 -2
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +273 -130
- package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +301 -0
- package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +32 -1
- package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/prompt-cache-optimizer.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +15 -4
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +23 -24
- package/src/resources/extensions/gsd/tests/queue-auto-guard.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +4 -5
- package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +75 -2
- package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +132 -0
- package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +8 -40
- package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +136 -256
- package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +114 -0
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +6 -3
- package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +148 -0
- package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/sidecar-queue.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +55 -95
- package/src/resources/extensions/gsd/tests/single-writer-v3-tool-surface.test.ts +158 -0
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +120 -1
- package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +112 -0
- package/src/resources/extensions/gsd/tests/slice-cadence.test.ts +242 -0
- package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +164 -1
- package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/stale-dirlistcache-4648.test.ts +112 -0
- package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +29 -5
- package/src/resources/extensions/gsd/tests/state-transition-matrix.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +11 -92
- package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +7 -6
- package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +102 -101
- package/src/resources/extensions/gsd/tests/sync-lock.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/test-helpers.test.ts +98 -0
- package/src/resources/extensions/gsd/tests/test-helpers.ts +153 -0
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +8 -1
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +61 -1
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +8 -1
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +355 -0
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +258 -0
- package/src/resources/extensions/gsd/tests/uok-contracts.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/uok-execution-graph.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +75 -0
- package/src/resources/extensions/gsd/tests/uok-gitops-wiring.test.ts +49 -26
- package/src/resources/extensions/gsd/tests/uok-loop-adapter-writer.test.ts +65 -0
- package/src/resources/extensions/gsd/tests/uok-parity-report.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +19 -2
- package/src/resources/extensions/gsd/tests/uok-writer.test.ts +75 -0
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +144 -80
- package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -54
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +342 -277
- package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +37 -29
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +226 -266
- package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +103 -67
- package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +92 -90
- package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +238 -59
- package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +113 -161
- package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +210 -0
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +262 -0
- package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +7 -5
- package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +80 -96
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +8 -2
- package/src/resources/extensions/gsd/types.ts +3 -3
- package/src/resources/extensions/gsd/unit-context-composer.ts +218 -0
- package/src/resources/extensions/gsd/unit-context-manifest.ts +574 -0
- package/src/resources/extensions/gsd/uok/contracts.ts +65 -0
- package/src/resources/extensions/gsd/uok/dispatch-envelope.ts +56 -0
- package/src/resources/extensions/gsd/uok/execution-graph.ts +22 -0
- package/src/resources/extensions/gsd/uok/gate-runner.ts +65 -5
- package/src/resources/extensions/gsd/uok/gitops.ts +6 -1
- package/src/resources/extensions/gsd/uok/loop-adapter.ts +45 -10
- package/src/resources/extensions/gsd/uok/parity-report.ts +84 -0
- package/src/resources/extensions/gsd/uok/plan-v2.ts +13 -5
- package/src/resources/extensions/gsd/uok/writer.ts +113 -0
- package/src/resources/extensions/gsd/workflow-mcp.ts +6 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +108 -7
- package/src/resources/extensions/gsd/worktree-resolver.ts +96 -9
- package/src/resources/extensions/gsd/worktree-telemetry.ts +322 -0
- package/src/resources/extensions/mcp-client/index.ts +3 -1
- package/src/resources/extensions/mcp-client/tests/server-name-spaces.test.ts +70 -36
- package/src/resources/extensions/ollama/index.ts +5 -1
- package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +123 -15
- package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +206 -19
- package/src/resources/extensions/remote-questions/manager.ts +36 -4
- package/src/resources/extensions/remote-questions/tests/command-polling.test.ts +200 -190
- package/src/resources/extensions/shared/tests/interview-preview.test.ts +11 -3
- package/src/resources/extensions/voice/tests/linux-ready.test.ts +129 -113
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +0 -2
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +0 -1
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +0 -289
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +0 -1
- package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +0 -363
- package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +0 -143
- package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +0 -157
- package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +0 -107
- package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +0 -48
- package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +0 -159
- package/src/resources/extensions/gsd/tests/forensics-db-completion.test.ts +0 -96
- package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +0 -79
- package/src/resources/extensions/gsd/tests/forensics-hook-key-parse.test.ts +0 -74
- package/src/resources/extensions/gsd/tests/forensics-journal.test.ts +0 -162
- package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +0 -38
- package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +0 -73
- package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +0 -125
- package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +0 -42
- /package/dist/web/standalone/.next/static/{5wbu35_C2_MQ3Jj1lEVDx → cAJH99yNS1UPbeSEiNRrV}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{5wbu35_C2_MQ3Jj1lEVDx → cAJH99yNS1UPbeSEiNRrV}/_ssgManifest.js +0 -0
|
@@ -15,6 +15,7 @@ import { logWarning, logError } from './workflow-logger.js';
|
|
|
15
15
|
import { extractVerdict } from './verdict-parser.js';
|
|
16
16
|
import { loadEffectiveGSDPreferences } from './preferences.js';
|
|
17
17
|
import { detectPendingEscalation } from './escalation.js';
|
|
18
|
+
import { isTerminalMilestoneSummaryContent } from './milestone-summary-classifier.js';
|
|
18
19
|
import { isDbAvailable, wasDbOpenAttempted, getAllMilestones, getMilestone, getMilestoneSlices, getSliceTasks, getReplanHistory, getSlice, insertMilestone, insertSlice, insertTask, updateSliceStatus, updateTaskStatus, getPendingGateCountForTurn, autoHealSketchFlags, } from './gsd-db.js';
|
|
19
20
|
/**
|
|
20
21
|
* A "ghost" milestone directory contains only META.json (and no substantive
|
|
@@ -81,6 +82,10 @@ export function isMilestoneComplete(roadmap) {
|
|
|
81
82
|
export function isValidationTerminal(validationContent) {
|
|
82
83
|
return extractVerdict(validationContent) != null;
|
|
83
84
|
}
|
|
85
|
+
async function isTerminalMilestoneSummaryFile(path, loader) {
|
|
86
|
+
const content = await loader(path);
|
|
87
|
+
return content != null && isTerminalMilestoneSummaryContent(content);
|
|
88
|
+
}
|
|
84
89
|
const CACHE_TTL_MS = 100;
|
|
85
90
|
let _stateCache = null;
|
|
86
91
|
// ── Telemetry counters for derive-path observability ────────────────────────
|
|
@@ -138,18 +143,19 @@ export async function getActiveMilestoneId(basePath) {
|
|
|
138
143
|
const content = roadmapFile ? await loadFile(roadmapFile) : null;
|
|
139
144
|
if (!content) {
|
|
140
145
|
const summaryFile = resolveMilestoneFile(basePath, mid, "SUMMARY");
|
|
141
|
-
if (summaryFile)
|
|
146
|
+
if (summaryFile && await isTerminalMilestoneSummaryFile(summaryFile, loadFile))
|
|
142
147
|
continue;
|
|
143
148
|
if (isGhostMilestone(basePath, mid))
|
|
144
149
|
continue;
|
|
145
150
|
return mid;
|
|
146
151
|
}
|
|
147
152
|
const roadmap = parseRoadmap(content);
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
+
const summaryFile = resolveMilestoneFile(basePath, mid, "SUMMARY");
|
|
154
|
+
if (summaryFile && await isTerminalMilestoneSummaryFile(summaryFile, loadFile))
|
|
155
|
+
continue;
|
|
156
|
+
if (!isMilestoneComplete(roadmap))
|
|
157
|
+
return mid;
|
|
158
|
+
return mid;
|
|
153
159
|
}
|
|
154
160
|
return null;
|
|
155
161
|
}
|
|
@@ -182,7 +188,7 @@ export async function deriveState(basePath) {
|
|
|
182
188
|
let synced = false;
|
|
183
189
|
for (const diskId of diskIds) {
|
|
184
190
|
if (!isGhostMilestone(basePath, diskId)) {
|
|
185
|
-
insertMilestone(
|
|
191
|
+
insertMilestone(diskMilestoneInsert(basePath, diskId));
|
|
186
192
|
synced = true;
|
|
187
193
|
}
|
|
188
194
|
}
|
|
@@ -240,6 +246,36 @@ function extractContextTitle(content, fallback) {
|
|
|
240
246
|
// isStatusDone replaced by isClosedStatus from status-guards.ts (single source of truth).
|
|
241
247
|
// Alias kept for backward compatibility within this file.
|
|
242
248
|
const isStatusDone = isClosedStatus;
|
|
249
|
+
function loadSync(path) {
|
|
250
|
+
if (!path)
|
|
251
|
+
return null;
|
|
252
|
+
try {
|
|
253
|
+
return readFileSync(path, "utf-8");
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
function diskMilestoneInsert(basePath, mid) {
|
|
260
|
+
const contextContent = loadSync(resolveMilestoneFile(basePath, mid, "CONTEXT"));
|
|
261
|
+
const draftContent = !contextContent ? loadSync(resolveMilestoneFile(basePath, mid, "CONTEXT-DRAFT")) : null;
|
|
262
|
+
const roadmapContent = loadSync(resolveMilestoneFile(basePath, mid, "ROADMAP"));
|
|
263
|
+
const summaryContent = loadSync(resolveMilestoneFile(basePath, mid, "SUMMARY"));
|
|
264
|
+
const roadmap = roadmapContent ? parseRoadmap(roadmapContent) : null;
|
|
265
|
+
const summary = summaryContent ? parseSummary(summaryContent) : null;
|
|
266
|
+
const summaryTerminal = summaryContent != null && isTerminalMilestoneSummaryContent(summaryContent);
|
|
267
|
+
const parked = resolveMilestoneFile(basePath, mid, "PARKED") !== null;
|
|
268
|
+
return {
|
|
269
|
+
id: mid,
|
|
270
|
+
title: roadmap
|
|
271
|
+
? stripMilestonePrefix(roadmap.title)
|
|
272
|
+
: (contextContent || draftContent)
|
|
273
|
+
? extractContextTitle(contextContent || draftContent, mid)
|
|
274
|
+
: (summary?.title || mid),
|
|
275
|
+
status: parked ? "parked" : summaryTerminal ? "complete" : "active",
|
|
276
|
+
depends_on: parseContextDependsOn(contextContent ?? draftContent),
|
|
277
|
+
};
|
|
278
|
+
}
|
|
243
279
|
/**
|
|
244
280
|
* Derive GSD state from the milestones/slices/tasks DB tables.
|
|
245
281
|
* Flag files (PARKED, VALIDATION, CONTINUE, REPLAN, REPLAN-TRIGGER, CONTEXT-DRAFT)
|
|
@@ -255,7 +291,7 @@ function reconcileDiskToDb(basePath) {
|
|
|
255
291
|
let synced = false;
|
|
256
292
|
for (const diskId of diskIds) {
|
|
257
293
|
if (!dbIdSet.has(diskId) && !isGhostMilestone(basePath, diskId)) {
|
|
258
|
-
insertMilestone(
|
|
294
|
+
insertMilestone(diskMilestoneInsert(basePath, diskId));
|
|
259
295
|
synced = true;
|
|
260
296
|
}
|
|
261
297
|
}
|
|
@@ -550,9 +586,6 @@ function resolveSliceDependencies(activeMilestoneSlices) {
|
|
|
550
586
|
return { activeSlice: null, activeSliceRow: null };
|
|
551
587
|
}
|
|
552
588
|
}
|
|
553
|
-
// First pass: find a slice with ALL dependencies satisfied (strict)
|
|
554
|
-
let bestFallback = null;
|
|
555
|
-
let bestFallbackSatisfied = -1;
|
|
556
589
|
for (const s of activeMilestoneSlices) {
|
|
557
590
|
if (isStatusDone(s.status))
|
|
558
591
|
continue;
|
|
@@ -561,36 +594,25 @@ function resolveSliceDependencies(activeMilestoneSlices) {
|
|
|
561
594
|
if (s.depends.every(dep => doneSliceIds.has(dep))) {
|
|
562
595
|
return { activeSlice: { id: s.id, title: s.title }, activeSliceRow: s };
|
|
563
596
|
}
|
|
564
|
-
// Track the slice with the most satisfied dependencies as fallback
|
|
565
|
-
const satisfied = s.depends.filter(dep => doneSliceIds.has(dep)).length;
|
|
566
|
-
if (satisfied > bestFallbackSatisfied || (satisfied === bestFallbackSatisfied && !bestFallback)) {
|
|
567
|
-
bestFallback = s;
|
|
568
|
-
bestFallbackSatisfied = satisfied;
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
// Fallback: if no slice has all deps met but there ARE incomplete non-deferred
|
|
572
|
-
// slices, pick the one with the most deps satisfied. This prevents hard-blocking
|
|
573
|
-
// when dependency metadata is stale (e.g. after reassessment added/removed slices)
|
|
574
|
-
// or when deps reference slices from previous milestones.
|
|
575
|
-
if (bestFallback) {
|
|
576
|
-
const unmet = bestFallback.depends.filter(dep => !doneSliceIds.has(dep));
|
|
577
|
-
logWarning("state", `No slice has all deps satisfied — falling back to ${bestFallback.id} ` +
|
|
578
|
-
`(${bestFallbackSatisfied}/${bestFallback.depends.length} deps met, ` +
|
|
579
|
-
`unmet: ${unmet.join(", ")})`, { mid: activeMilestoneSlices[0]?.milestone_id, sid: bestFallback.id });
|
|
580
|
-
return { activeSlice: { id: bestFallback.id, title: bestFallback.title }, activeSliceRow: bestFallback };
|
|
581
597
|
}
|
|
582
598
|
return { activeSlice: null, activeSliceRow: null };
|
|
583
599
|
}
|
|
584
600
|
async function reconcileSliceTasks(basePath, milestoneId, sliceId, planFile) {
|
|
585
601
|
let tasks = getSliceTasks(milestoneId, sliceId);
|
|
586
|
-
|
|
602
|
+
// #3600/#4974: import missing plan-file tasks even when the DB already has
|
|
603
|
+
// a partial task set. Existing DB task statuses stay authoritative.
|
|
604
|
+
if (planFile) {
|
|
587
605
|
try {
|
|
588
606
|
const planContent = await loadFile(planFile);
|
|
589
607
|
if (planContent) {
|
|
590
608
|
const diskPlan = parsePlan(planContent);
|
|
591
609
|
if (diskPlan.tasks.length > 0) {
|
|
610
|
+
const dbTaskIds = new Set(tasks.map(t => t.id));
|
|
611
|
+
let inserted = 0;
|
|
592
612
|
for (let i = 0; i < diskPlan.tasks.length; i++) {
|
|
593
613
|
const t = diskPlan.tasks[i];
|
|
614
|
+
if (dbTaskIds.has(t.id))
|
|
615
|
+
continue;
|
|
594
616
|
try {
|
|
595
617
|
insertTask({
|
|
596
618
|
id: t.id,
|
|
@@ -600,13 +622,16 @@ async function reconcileSliceTasks(basePath, milestoneId, sliceId, planFile) {
|
|
|
600
622
|
status: t.done ? 'complete' : 'pending',
|
|
601
623
|
sequence: i + 1,
|
|
602
624
|
});
|
|
625
|
+
inserted++;
|
|
603
626
|
}
|
|
604
627
|
catch (insertErr) {
|
|
605
628
|
logWarning("reconcile", `failed to insert task ${t.id} from plan file: ${insertErr instanceof Error ? insertErr.message : String(insertErr)}`);
|
|
606
629
|
}
|
|
607
630
|
}
|
|
608
|
-
|
|
609
|
-
|
|
631
|
+
if (inserted > 0) {
|
|
632
|
+
tasks = getSliceTasks(milestoneId, sliceId);
|
|
633
|
+
logWarning("reconcile", `imported ${inserted} missing task(s) from plan file for ${milestoneId}/${sliceId}`, { mid: milestoneId, sid: sliceId });
|
|
634
|
+
}
|
|
610
635
|
}
|
|
611
636
|
}
|
|
612
637
|
}
|
|
@@ -988,7 +1013,7 @@ export async function _deriveStateImpl(basePath) {
|
|
|
988
1013
|
const rc = rf ? await cachedLoadFile(rf) : null;
|
|
989
1014
|
if (!rc) {
|
|
990
1015
|
const sf = resolveMilestoneFile(basePath, mid, "SUMMARY");
|
|
991
|
-
if (sf)
|
|
1016
|
+
if (sf && await isTerminalMilestoneSummaryFile(sf, cachedLoadFile))
|
|
992
1017
|
completeMilestoneIds.add(mid);
|
|
993
1018
|
continue;
|
|
994
1019
|
}
|
|
@@ -998,12 +1023,12 @@ export async function _deriveStateImpl(basePath) {
|
|
|
998
1023
|
// Summary is the terminal artifact — if it exists, the milestone is
|
|
999
1024
|
// complete even when roadmap checkboxes weren't ticked (#864).
|
|
1000
1025
|
const sf = resolveMilestoneFile(basePath, mid, "SUMMARY");
|
|
1001
|
-
if (sf)
|
|
1026
|
+
if (sf && await isTerminalMilestoneSummaryFile(sf, cachedLoadFile))
|
|
1002
1027
|
completeMilestoneIds.add(mid);
|
|
1003
1028
|
continue;
|
|
1004
1029
|
}
|
|
1005
1030
|
const sf = resolveMilestoneFile(basePath, mid, "SUMMARY");
|
|
1006
|
-
if (sf)
|
|
1031
|
+
if (sf && await isTerminalMilestoneSummaryFile(sf, cachedLoadFile))
|
|
1007
1032
|
completeMilestoneIds.add(mid);
|
|
1008
1033
|
}
|
|
1009
1034
|
// Phase 2: Build registry using cached roadmaps (no re-parsing or re-reading)
|
|
@@ -1028,12 +1053,14 @@ export async function _deriveStateImpl(basePath) {
|
|
|
1028
1053
|
const summaryFile = resolveMilestoneFile(basePath, mid, "SUMMARY");
|
|
1029
1054
|
if (summaryFile) {
|
|
1030
1055
|
const summaryContent = await cachedLoadFile(summaryFile);
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1056
|
+
if (summaryContent != null && isTerminalMilestoneSummaryContent(summaryContent)) {
|
|
1057
|
+
const summaryTitle = summaryContent
|
|
1058
|
+
? (parseSummary(summaryContent).title || mid)
|
|
1059
|
+
: mid;
|
|
1060
|
+
registry.push({ id: mid, title: summaryTitle, status: 'complete' });
|
|
1061
|
+
completeMilestoneIds.add(mid);
|
|
1062
|
+
continue;
|
|
1063
|
+
}
|
|
1037
1064
|
}
|
|
1038
1065
|
// Ghost milestone (only META.json, no CONTEXT/ROADMAP/SUMMARY) — skip entirely
|
|
1039
1066
|
if (isGhostMilestone(basePath, mid))
|
|
@@ -1087,7 +1114,7 @@ export async function _deriveStateImpl(basePath) {
|
|
|
1087
1114
|
const verdict = validationContent ? extractVerdict(validationContent) : undefined;
|
|
1088
1115
|
// needs-remediation is terminal but requires re-validation (#3596)
|
|
1089
1116
|
const needsRevalidation = !validationTerminal || verdict === 'needs-remediation';
|
|
1090
|
-
if (summaryFile) {
|
|
1117
|
+
if (summaryFile && await isTerminalMilestoneSummaryFile(summaryFile, cachedLoadFile)) {
|
|
1091
1118
|
// Summary exists → milestone is complete regardless of validation state.
|
|
1092
1119
|
// The summary is the terminal artifact (#864).
|
|
1093
1120
|
registry.push({ id: mid, title, status: 'complete' });
|
|
@@ -1118,7 +1145,7 @@ export async function _deriveStateImpl(basePath) {
|
|
|
1118
1145
|
// Roadmap slices not all checked — but if a summary exists, the milestone
|
|
1119
1146
|
// is still complete. The summary is the terminal artifact (#864).
|
|
1120
1147
|
const summaryFile = resolveMilestoneFile(basePath, mid, "SUMMARY");
|
|
1121
|
-
if (summaryFile) {
|
|
1148
|
+
if (summaryFile && await isTerminalMilestoneSummaryFile(summaryFile, cachedLoadFile)) {
|
|
1122
1149
|
registry.push({ id: mid, title, status: 'complete' });
|
|
1123
1150
|
}
|
|
1124
1151
|
else if (!activeMilestoneFound) {
|
|
@@ -1387,8 +1414,6 @@ export async function _deriveStateImpl(basePath) {
|
|
|
1387
1414
|
}
|
|
1388
1415
|
}
|
|
1389
1416
|
else {
|
|
1390
|
-
let bestFallbackLegacy = null;
|
|
1391
|
-
let bestFallbackLegacySatisfied = -1;
|
|
1392
1417
|
for (const s of activeRoadmap.slices) {
|
|
1393
1418
|
if (s.done)
|
|
1394
1419
|
continue;
|
|
@@ -1396,20 +1421,6 @@ export async function _deriveStateImpl(basePath) {
|
|
|
1396
1421
|
activeSlice = { id: s.id, title: s.title };
|
|
1397
1422
|
break;
|
|
1398
1423
|
}
|
|
1399
|
-
// Track best fallback
|
|
1400
|
-
const satisfied = s.depends.filter(dep => doneSliceIds.has(dep)).length;
|
|
1401
|
-
if (satisfied > bestFallbackLegacySatisfied) {
|
|
1402
|
-
bestFallbackLegacy = s;
|
|
1403
|
-
bestFallbackLegacySatisfied = satisfied;
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
// Fallback: if no slice has all deps met, pick the one with the most deps satisfied
|
|
1407
|
-
if (!activeSlice && bestFallbackLegacy) {
|
|
1408
|
-
const unmet = bestFallbackLegacy.depends.filter(dep => !doneSliceIds.has(dep));
|
|
1409
|
-
logWarning("state", `No slice has all deps satisfied — falling back to ${bestFallbackLegacy.id} ` +
|
|
1410
|
-
`(${bestFallbackLegacySatisfied}/${bestFallbackLegacy.depends.length} deps met, ` +
|
|
1411
|
-
`unmet: ${unmet.join(", ")})`);
|
|
1412
|
-
activeSlice = { id: bestFallbackLegacy.id, title: bestFallbackLegacy.title };
|
|
1413
1424
|
}
|
|
1414
1425
|
}
|
|
1415
1426
|
if (!activeSlice) {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
// GSD Extension — Advisory Sync Lock
|
|
2
2
|
// Prevents concurrent worktree syncs from colliding via a simple file lock.
|
|
3
|
-
// Stale locks (mtime > 60s) are
|
|
4
|
-
// to 5 seconds then skips non-fatally.
|
|
5
|
-
import { existsSync, statSync, unlinkSync } from "node:fs";
|
|
6
|
-
import { join } from "node:path";
|
|
7
|
-
import { atomicWriteSync } from "./atomic-write.js";
|
|
3
|
+
// Stale locks (mtime > 60s, owner PID confirmed dead) are overridden. Lock
|
|
4
|
+
// acquisition waits up to 5 seconds then skips non-fatally.
|
|
5
|
+
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, statSync, unlinkSync, writeSync } from "node:fs";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
8
7
|
const STALE_THRESHOLD_MS = 60_000; // 60 seconds
|
|
9
8
|
const DEFAULT_TIMEOUT_MS = 5_000; // 5 seconds
|
|
10
9
|
const SPIN_INTERVAL_MS = 100; // 100ms polling interval
|
|
@@ -17,60 +16,117 @@ function lockFilePath(basePath) {
|
|
|
17
16
|
function sleepSync(ms) {
|
|
18
17
|
Atomics.wait(SLEEP_VIEW, 0, 0, ms);
|
|
19
18
|
}
|
|
19
|
+
/** True if the given PID is alive in the current process namespace. */
|
|
20
|
+
function isPidAlive(pid) {
|
|
21
|
+
try {
|
|
22
|
+
process.kill(pid, 0);
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
if (err.code === "EPERM")
|
|
27
|
+
return true;
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Atomically create the lock file with `O_EXCL` semantics. Returns true on
|
|
33
|
+
* exclusive create, false if the file already existed. Any other error
|
|
34
|
+
* propagates.
|
|
35
|
+
*/
|
|
36
|
+
function tryCreateLockFile(lp, payload) {
|
|
37
|
+
// Ensure parent dir exists (`atomicWriteSync` previously did this implicitly).
|
|
38
|
+
try {
|
|
39
|
+
mkdirSync(dirname(lp), { recursive: true });
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
/* best-effort */
|
|
43
|
+
}
|
|
44
|
+
let fd;
|
|
45
|
+
try {
|
|
46
|
+
// "wx" → O_WRONLY | O_CREAT | O_EXCL — atomic create-if-not-exists on POSIX.
|
|
47
|
+
fd = openSync(lp, "wx");
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
const code = err.code;
|
|
51
|
+
if (code === "EEXIST")
|
|
52
|
+
return false;
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
writeSync(fd, payload);
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
closeSync(fd);
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
20
63
|
/**
|
|
21
64
|
* Acquire an advisory sync lock for the given basePath.
|
|
22
65
|
* Returns { acquired: true } on success, { acquired: false } after timeout.
|
|
23
66
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
67
|
+
* Replaces a non-atomic `existsSync` + `atomicWriteSync` (write-temp+rename,
|
|
68
|
+
* which is not exclusive-create) sequence that allowed two callers to both
|
|
69
|
+
* believe they had acquired the lock, corrupting the event log.
|
|
70
|
+
* (Issue #4980 CRIT-4)
|
|
71
|
+
*
|
|
72
|
+
* Stale-lock override now also verifies the recorded owner PID is dead
|
|
73
|
+
* before stealing — prevents a slow event-loop pause (>60s under heavy I/O)
|
|
74
|
+
* from making a legitimately-held lock appear stale and get stolen.
|
|
75
|
+
* (Issue #4980 M-concurrency-3)
|
|
27
76
|
*/
|
|
28
77
|
export function acquireSyncLock(basePath, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
29
78
|
const lp = lockFilePath(basePath);
|
|
30
79
|
const deadline = Date.now() + timeoutMs;
|
|
80
|
+
const lockData = JSON.stringify({ pid: process.pid, acquired_at: new Date().toISOString() }, null, 2);
|
|
31
81
|
while (true) {
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
82
|
+
// First try the fast path: atomic create. No check-then-write race.
|
|
83
|
+
try {
|
|
84
|
+
if (tryCreateLockFile(lp, lockData)) {
|
|
85
|
+
return { acquired: true };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
/* unexpected — fall through to retry */
|
|
90
|
+
}
|
|
91
|
+
// File exists. Decide whether to steal (stale + owner dead) or wait.
|
|
92
|
+
let canSteal = false;
|
|
93
|
+
try {
|
|
94
|
+
const stat = statSync(lp);
|
|
95
|
+
const age = Date.now() - stat.mtimeMs;
|
|
96
|
+
if (age > STALE_THRESHOLD_MS) {
|
|
97
|
+
// Verify the recorded owner PID is dead before stealing.
|
|
98
|
+
let ownerAlive = false;
|
|
99
|
+
try {
|
|
100
|
+
const data = JSON.parse(readFileSync(lp, "utf-8"));
|
|
101
|
+
if (typeof data.pid === "number" && data.pid !== process.pid) {
|
|
102
|
+
ownerAlive = isPidAlive(data.pid);
|
|
42
103
|
}
|
|
43
|
-
catch { /* race: already removed */ }
|
|
44
104
|
}
|
|
45
|
-
|
|
46
|
-
// Lock
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
sleepSync(SPIN_INTERVAL_MS);
|
|
51
|
-
continue;
|
|
105
|
+
catch {
|
|
106
|
+
// Lock contents unreadable — be conservative and steal only on age.
|
|
107
|
+
// A garbage lock file that we cannot parse is safer to remove than
|
|
108
|
+
// to leave wedging the lock indefinitely.
|
|
52
109
|
}
|
|
110
|
+
canSteal = !ownerAlive;
|
|
53
111
|
}
|
|
54
|
-
catch {
|
|
55
|
-
// stat failed (file removed between exists check and stat) — try to acquire
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
// Lock file does not exist (or was just removed) — try to write it
|
|
59
|
-
try {
|
|
60
|
-
const lockData = {
|
|
61
|
-
pid: process.pid,
|
|
62
|
-
acquired_at: new Date().toISOString(),
|
|
63
|
-
};
|
|
64
|
-
atomicWriteSync(lp, JSON.stringify(lockData, null, 2));
|
|
65
|
-
return { acquired: true };
|
|
66
112
|
}
|
|
67
113
|
catch {
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
|
|
114
|
+
// stat failed (file removed between exists and stat) — retry create.
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (canSteal) {
|
|
118
|
+
try {
|
|
119
|
+
unlinkSync(lp);
|
|
71
120
|
}
|
|
72
|
-
|
|
121
|
+
catch { /* race: already removed */ }
|
|
122
|
+
// Loop back to retry create.
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
// Lock is held and not stale (or owner is alive) — wait or give up.
|
|
126
|
+
if (Date.now() >= deadline) {
|
|
127
|
+
return { acquired: false };
|
|
73
128
|
}
|
|
129
|
+
sleepSync(SPIN_INTERVAL_MS);
|
|
74
130
|
}
|
|
75
131
|
}
|
|
76
132
|
/**
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
import { transaction, insertAssessment, deleteAssessmentByScope, getMilestoneSlices, } from "../gsd-db.js";
|
|
13
13
|
import { resolveMilestonePath, clearPathCache } from "../paths.js";
|
|
14
|
+
import { resolveCanonicalMilestoneRoot } from "../worktree-manager.js";
|
|
14
15
|
import { saveFile, clearParseCache } from "../files.js";
|
|
15
16
|
import { invalidateStateCache } from "../state.js";
|
|
16
17
|
import { VALIDATION_VERDICTS, isValidMilestoneVerdict } from "../verdict-parser.js";
|
|
@@ -59,14 +60,18 @@ export async function handleValidateMilestone(params, basePath, opts) {
|
|
|
59
60
|
return { error: `verdict must be one of: ${VALIDATION_VERDICTS.join(", ")}` };
|
|
60
61
|
}
|
|
61
62
|
// ── Resolve paths and render markdown ────────────────────────────────
|
|
63
|
+
// #4761: route through the canonical-root resolver so that when a live
|
|
64
|
+
// worktree exists for this milestone, validation reads/writes the
|
|
65
|
+
// worktree's artifacts instead of stale project-root state.
|
|
62
66
|
const validationMd = renderValidationMarkdown(params);
|
|
67
|
+
const canonicalBase = resolveCanonicalMilestoneRoot(basePath, params.milestoneId);
|
|
63
68
|
let validationPath;
|
|
64
|
-
const milestoneDir = resolveMilestonePath(
|
|
69
|
+
const milestoneDir = resolveMilestonePath(canonicalBase, params.milestoneId);
|
|
65
70
|
if (milestoneDir) {
|
|
66
71
|
validationPath = join(milestoneDir, `${params.milestoneId}-VALIDATION.md`);
|
|
67
72
|
}
|
|
68
73
|
else {
|
|
69
|
-
const gsdDir = join(
|
|
74
|
+
const gsdDir = join(canonicalBase, ".gsd");
|
|
70
75
|
const manualDir = join(gsdDir, "milestones", params.milestoneId);
|
|
71
76
|
validationPath = join(manualDir, `${params.milestoneId}-VALIDATION.md`);
|
|
72
77
|
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// GSD-2 — UnitContextComposer (#4782 phase 2).
|
|
2
|
+
//
|
|
3
|
+
// Reads a unit type's manifest and orchestrates artifact inlining through
|
|
4
|
+
// a caller-provided resolver. Returns a joined context block suitable for
|
|
5
|
+
// substitution into the unit's prompt template.
|
|
6
|
+
//
|
|
7
|
+
// Design rationale:
|
|
8
|
+
// - Pure dependency on the manifest module — no circular import with
|
|
9
|
+
// `auto-prompts.ts` where the per-artifact-key resolver lives.
|
|
10
|
+
// - Caller-supplied resolver means the composer can be unit-tested with
|
|
11
|
+
// trivial mocks; production wiring in `auto-prompts.ts` dispatches to
|
|
12
|
+
// the existing `inlineFile` / `inline*FromDb` helpers.
|
|
13
|
+
// - Null-returning resolvers are skipped silently: they model the
|
|
14
|
+
// "artifact is optional / missing / not applicable to this milestone"
|
|
15
|
+
// case. The composer never errors on a missing artifact.
|
|
16
|
+
//
|
|
17
|
+
// Scope: phase 2 pilot shipped `composeInlinedContext` for static-key
|
|
18
|
+
// inlining. Phase 3.5 (#4924) adds the v2 surface — `composeUnitContext`
|
|
19
|
+
// — which also handles excerpts, computed artifacts, and prepended blocks.
|
|
20
|
+
// `composeInlinedContext` stays for backward compatibility with the
|
|
21
|
+
// already-migrated simple builders.
|
|
22
|
+
//
|
|
23
|
+
// ─── Composer boundary invariant (#4924) ─────────────────────────────────
|
|
24
|
+
//
|
|
25
|
+
// The composer is allowed to:
|
|
26
|
+
// - order named sections per the manifest's declared sequence
|
|
27
|
+
// - resolve registered artifacts (static / computed / excerpt / on-demand)
|
|
28
|
+
// - apply typed policies (knowledge / memory / codebase-map / preferences)
|
|
29
|
+
//
|
|
30
|
+
// The composer must NOT grow:
|
|
31
|
+
// - arbitrary conditionals on unit state
|
|
32
|
+
// - loops over caller-supplied data
|
|
33
|
+
// - string templating beyond section composition (join + separator)
|
|
34
|
+
//
|
|
35
|
+
// Logic that needs those belongs in a typed computed-artifact builder
|
|
36
|
+
// owned by the unit, not in the composer. Reviews must enforce this — it
|
|
37
|
+
// is the difference between an orchestrator and a runaway DSL.
|
|
38
|
+
import { resolveManifest, } from "./unit-context-manifest.js";
|
|
39
|
+
/**
|
|
40
|
+
* Produce the inlined-context portion of a unit's system prompt by
|
|
41
|
+
* walking the manifest's `artifacts.inline` list in order and calling
|
|
42
|
+
* the provided resolver for each key.
|
|
43
|
+
*
|
|
44
|
+
* Returns an empty string when the unit type has no manifest registered,
|
|
45
|
+
* so callers can guard their wiring with a simple truthy check. Unknown
|
|
46
|
+
* unit types do not error — this mirrors `resolveManifest`'s contract.
|
|
47
|
+
*
|
|
48
|
+
* The separator between inlined blocks matches the in-tree convention
|
|
49
|
+
* (`\n\n---\n\n`) so composer output slots into existing prompt templates
|
|
50
|
+
* without visible diff.
|
|
51
|
+
*/
|
|
52
|
+
export async function composeInlinedContext(unitType, resolveArtifact) {
|
|
53
|
+
const manifest = resolveManifest(unitType);
|
|
54
|
+
if (!manifest)
|
|
55
|
+
return "";
|
|
56
|
+
const blocks = [];
|
|
57
|
+
for (const key of manifest.artifacts.inline) {
|
|
58
|
+
const body = await resolveArtifact(key);
|
|
59
|
+
if (body !== null && body.length > 0) {
|
|
60
|
+
blocks.push(body);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return blocks.join("\n\n---\n\n");
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Convenience helper returning the manifest's declared budget so callers
|
|
67
|
+
* can telemetry a mismatch between actual prompt size and declared budget.
|
|
68
|
+
* Returns null for unknown unit types.
|
|
69
|
+
*/
|
|
70
|
+
export function manifestBudgetChars(unitType) {
|
|
71
|
+
const manifest = resolveManifest(unitType);
|
|
72
|
+
return manifest ? manifest.maxSystemPromptChars : null;
|
|
73
|
+
}
|
|
74
|
+
const SECTION_SEPARATOR = "\n\n---\n\n";
|
|
75
|
+
/**
|
|
76
|
+
* Compose all manifest-declared context for a unit type using the v2
|
|
77
|
+
* surface. Walks `prepend` first (computed-only), then the `inline` list
|
|
78
|
+
* (static keys via `resolveArtifact`), then `excerpt` (via `resolveExcerpt`),
|
|
79
|
+
* then `artifacts.computed` (via the typed registry). Order within each
|
|
80
|
+
* section follows the manifest's declared sequence.
|
|
81
|
+
*
|
|
82
|
+
* Unknown unit types return empty strings for both sections — callers can
|
|
83
|
+
* fall back to existing imperative wiring without a special case.
|
|
84
|
+
*
|
|
85
|
+
* Resolver / registry omissions: if the manifest declares an entry but no
|
|
86
|
+
* resolver / registry entry is provided, the composer skips it silently.
|
|
87
|
+
* This matches the v1 contract where a null body is a no-op, and lets
|
|
88
|
+
* partial migrations land without forcing every consumer to register
|
|
89
|
+
* every artifact class up-front.
|
|
90
|
+
*/
|
|
91
|
+
export async function composeUnitContext(unitType, opts) {
|
|
92
|
+
const manifest = resolveManifest(unitType);
|
|
93
|
+
if (!manifest)
|
|
94
|
+
return { prepend: "", inline: "" };
|
|
95
|
+
// Single-source `unitType`: the manifest is resolved against the
|
|
96
|
+
// function arg, but computed builders read it from `base.unitType`.
|
|
97
|
+
// If those ever diverge (caller passes one type to composeUnitContext
|
|
98
|
+
// but a different one in opts.base), the composer would silently
|
|
99
|
+
// mix one unit's manifest with another unit's computed context.
|
|
100
|
+
// Normalize here so the composer dispatches a consistent identity
|
|
101
|
+
// through to every builder.
|
|
102
|
+
const normalizedOpts = {
|
|
103
|
+
...opts,
|
|
104
|
+
base: { ...opts.base, unitType },
|
|
105
|
+
};
|
|
106
|
+
const prependBlocks = await runComputed(manifest.prepend ?? [], normalizedOpts);
|
|
107
|
+
const inlineBlocks = [];
|
|
108
|
+
for (const key of manifest.artifacts.inline) {
|
|
109
|
+
if (!normalizedOpts.resolveArtifact)
|
|
110
|
+
break;
|
|
111
|
+
const body = await normalizedOpts.resolveArtifact(key);
|
|
112
|
+
if (body && body.length > 0)
|
|
113
|
+
inlineBlocks.push(body);
|
|
114
|
+
}
|
|
115
|
+
for (const key of manifest.artifacts.excerpt) {
|
|
116
|
+
if (!normalizedOpts.resolveExcerpt)
|
|
117
|
+
break;
|
|
118
|
+
const body = await normalizedOpts.resolveExcerpt(key);
|
|
119
|
+
if (body && body.length > 0)
|
|
120
|
+
inlineBlocks.push(body);
|
|
121
|
+
}
|
|
122
|
+
inlineBlocks.push(...await runComputed(manifest.artifacts.computed ?? [], normalizedOpts));
|
|
123
|
+
return {
|
|
124
|
+
prepend: prependBlocks.join(SECTION_SEPARATOR),
|
|
125
|
+
inline: inlineBlocks.join(SECTION_SEPARATOR),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Invoke the registered builder for each declared computed id, in order.
|
|
130
|
+
* Missing registry entries (manifest declares the id but caller didn't
|
|
131
|
+
* register it) are skipped silently — see composeUnitContext rationale.
|
|
132
|
+
*/
|
|
133
|
+
async function runComputed(ids, opts) {
|
|
134
|
+
if (ids.length === 0 || !opts.computed)
|
|
135
|
+
return [];
|
|
136
|
+
const registry = opts.computed;
|
|
137
|
+
const out = [];
|
|
138
|
+
for (const id of ids) {
|
|
139
|
+
const entry = registry[id];
|
|
140
|
+
if (!entry)
|
|
141
|
+
continue;
|
|
142
|
+
const body = await entry.build(entry.inputs, opts.base);
|
|
143
|
+
if (body && body.length > 0)
|
|
144
|
+
out.push(body);
|
|
145
|
+
}
|
|
146
|
+
return out;
|
|
147
|
+
}
|