gsd-pi 2.77.0-dev.58d3d4d6c → 2.77.0-dev.cfd69e714
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 +1 -1
- package/dist/claude-cli-check.js +5 -1
- 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 +5 -1
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +481 -17
- package/dist/resources/extensions/gsd/auto/loop.js +43 -0
- package/dist/resources/extensions/gsd/auto/phases.js +15 -21
- package/dist/resources/extensions/gsd/auto/session.js +0 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +102 -24
- package/dist/resources/extensions/gsd/auto-model-selection.js +124 -4
- package/dist/resources/extensions/gsd/auto-post-unit.js +71 -64
- package/dist/resources/extensions/gsd/auto-prompts.js +329 -102
- package/dist/resources/extensions/gsd/auto-recovery.js +195 -23
- package/dist/resources/extensions/gsd/auto-start.js +34 -24
- 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 +31 -20
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +9 -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/detection.js +49 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
- 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 +17 -5
- 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/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/prompts/complete-milestone.md +6 -2
- 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-parallel-orchestrator.js +278 -8
- package/dist/resources/extensions/gsd/state.js +44 -33
- package/dist/resources/extensions/gsd/sync-lock.js +98 -42
- 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/gate-runner.js +53 -5
- package/dist/resources/extensions/gsd/workflow-mcp.js +6 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +34 -8
- 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 +5 -5
- 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 +5 -5
- 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/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/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 +80 -39
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/native/package.json +1 -1
- 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/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/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/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/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/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/dynamic-border.test.ts +26 -20
- 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 +36 -12
- 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/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 +41 -12
- 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/src/rpc-client.test.ts +109 -52
- package/packages/rpc-client/tsconfig.tsbuildinfo +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 +5 -1
- 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/tests/cli.test.ts +76 -7
- package/src/resources/extensions/github-sync/tests/templates.test.ts +33 -1
- package/src/resources/extensions/gsd/auto/loop.ts +47 -0
- package/src/resources/extensions/gsd/auto/phases.ts +16 -20
- package/src/resources/extensions/gsd/auto/session.ts +0 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +113 -24
- package/src/resources/extensions/gsd/auto-model-selection.ts +131 -4
- package/src/resources/extensions/gsd/auto-post-unit.ts +82 -73
- package/src/resources/extensions/gsd/auto-prompts.ts +330 -90
- package/src/resources/extensions/gsd/auto-recovery.ts +225 -24
- package/src/resources/extensions/gsd/auto-start.ts +54 -6
- 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 +43 -22
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +9 -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/detection.ts +58 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -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 +133 -2
- package/src/resources/extensions/gsd/gsd-db.ts +6 -3
- package/src/resources/extensions/gsd/guided-flow.ts +20 -5
- 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/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/prompts/complete-milestone.md +6 -2
- 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-parallel-orchestrator.ts +309 -8
- package/src/resources/extensions/gsd/state.ts +49 -44
- 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/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 +94 -289
- 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-retry-mcp-churn-fixes.test.ts +8 -197
- package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +15 -58
- package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +17 -21
- 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-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/crash-recovery.test.ts +50 -1
- 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 -3
- package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +139 -129
- package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +9 -105
- 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/hook-key-parsing.test.ts +4 -55
- package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +7 -57
- 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/interrupted-session-ui.test.ts +6 -9
- 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 -62
- package/src/resources/extensions/gsd/tests/milestone-scope-classifier.test.ts +187 -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 -49
- package/src/resources/extensions/gsd/tests/notification-widget.test.ts +6 -3
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +273 -133
- 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/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/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/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/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-parallel-orchestrator.test.ts +164 -1
- package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +5 -5
- package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +11 -92
- 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/test-helpers.test.ts +12 -61
- package/src/resources/extensions/gsd/tests/test-helpers.ts +21 -8
- 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-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/validate-milestone.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +144 -81
- 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/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/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/gate-runner.ts +65 -5
- package/src/resources/extensions/gsd/workflow-mcp.ts +6 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +55 -7
- 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 -144
- 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 -75
- package/src/resources/extensions/gsd/tests/forensics-journal.test.ts +0 -162
- package/src/resources/extensions/gsd/tests/forensics-worktree-telemetry.test.ts +0 -145
- 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 -130
- package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +0 -43
- /package/dist/web/standalone/.next/static/{Cev5xrAYA3ZGTRLyjR2fX → SvCJDZPQW104bR1KnBQg1}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{Cev5xrAYA3ZGTRLyjR2fX → SvCJDZPQW104bR1KnBQg1}/_ssgManifest.js +0 -0
|
@@ -4,7 +4,13 @@ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import { loadSkills } from "@gsd/pi-coding-agent";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
buildPlanMilestonePrompt,
|
|
9
|
+
buildResearchMilestonePrompt,
|
|
10
|
+
buildSkillActivationBlock,
|
|
11
|
+
} from "../auto-prompts.js";
|
|
12
|
+
import { warnIfManifestHasMissingSkills } from "../skill-manifest.js";
|
|
13
|
+
import { _resetLogs, drainLogs, setStderrLoggingEnabled } from "../workflow-logger.js";
|
|
8
14
|
import type { GSDPreferences } from "../preferences.js";
|
|
9
15
|
|
|
10
16
|
function makeTempBase(): string {
|
|
@@ -25,6 +31,11 @@ function loadOnlyTestSkills(base: string): void {
|
|
|
25
31
|
loadSkills({ cwd: base, includeDefaults: false, skillPaths: [join(base, "skills")] });
|
|
26
32
|
}
|
|
27
33
|
|
|
34
|
+
function writeProjectPreferences(base: string, preferences: string): void {
|
|
35
|
+
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
36
|
+
writeFileSync(join(base, ".gsd", "PREFERENCES.md"), `---\n${preferences}---\n`);
|
|
37
|
+
}
|
|
38
|
+
|
|
28
39
|
function buildBlock(
|
|
29
40
|
base: string,
|
|
30
41
|
params: Partial<Parameters<typeof buildSkillActivationBlock>[0]> = {},
|
|
@@ -231,3 +242,111 @@ test("buildSkillActivationBlock allows valid skill names and rejects invalid one
|
|
|
231
242
|
cleanup(base);
|
|
232
243
|
}
|
|
233
244
|
});
|
|
245
|
+
|
|
246
|
+
// ─── Per-unit-type skill manifest (RFC #4779) ─────────────────────────────────
|
|
247
|
+
|
|
248
|
+
test("buildSkillActivationBlock: explicit always_use_skills bypass the unit-type manifest", () => {
|
|
249
|
+
const base = makeTempBase();
|
|
250
|
+
try {
|
|
251
|
+
// write-docs is in the research-milestone manifest; swiftui is not.
|
|
252
|
+
// Both are in always_use_skills — a user-explicit source — so BOTH
|
|
253
|
+
// should activate regardless of the manifest. User intent wins over
|
|
254
|
+
// unit-type defaults. See RFC #4779 and skill-manifest.ts rationale.
|
|
255
|
+
writeSkill(base, "write-docs", "Use when writing docs or RFCs.");
|
|
256
|
+
writeSkill(base, "swiftui", "Use for SwiftUI views.");
|
|
257
|
+
loadOnlyTestSkills(base);
|
|
258
|
+
|
|
259
|
+
const result = buildBlock(base, { unitType: "research-milestone" }, {
|
|
260
|
+
always_use_skills: ["write-docs", "swiftui"],
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
assert.match(result, /Call Skill\(\{ skill: 'write-docs' \}\)/);
|
|
264
|
+
assert.match(result, /Call Skill\(\{ skill: 'swiftui' \}\)/);
|
|
265
|
+
} finally {
|
|
266
|
+
cleanup(base);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test("buildSkillActivationBlock falls through to all skills for unknown unit type", () => {
|
|
271
|
+
const base = makeTempBase();
|
|
272
|
+
try {
|
|
273
|
+
writeSkill(base, "swiftui", "Use for SwiftUI views.");
|
|
274
|
+
loadOnlyTestSkills(base);
|
|
275
|
+
|
|
276
|
+
const result = buildBlock(base, { unitType: "unknown-unit-type" }, {
|
|
277
|
+
always_use_skills: ["swiftui"],
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// Unknown unit type = wildcard fallback (pre-manifest behavior).
|
|
281
|
+
assert.match(result, /Call Skill\(\{ skill: 'swiftui' \}\)/);
|
|
282
|
+
} finally {
|
|
283
|
+
cleanup(base);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test("buildSkillActivationBlock without unitType preserves pre-manifest behavior", () => {
|
|
288
|
+
const base = makeTempBase();
|
|
289
|
+
try {
|
|
290
|
+
writeSkill(base, "swiftui", "Use for SwiftUI views.");
|
|
291
|
+
loadOnlyTestSkills(base);
|
|
292
|
+
|
|
293
|
+
// No unitType param — filter should no-op.
|
|
294
|
+
const result = buildBlock(base, {}, {
|
|
295
|
+
always_use_skills: ["swiftui"],
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
assert.match(result, /Call Skill\(\{ skill: 'swiftui' \}\)/);
|
|
299
|
+
} finally {
|
|
300
|
+
cleanup(base);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
test("milestone prompt builders propagate always_use_skills through buildSkillActivationBlock", async () => {
|
|
305
|
+
const base = makeTempBase();
|
|
306
|
+
try {
|
|
307
|
+
// Both skills are in always_use_skills — explicit user intent bypasses
|
|
308
|
+
// the unit-type manifest, so both activate in both milestone flows.
|
|
309
|
+
writeSkill(base, "write-docs", "Use when writing docs or RFCs.");
|
|
310
|
+
writeSkill(base, "swiftui", "Use for SwiftUI views.");
|
|
311
|
+
writeProjectPreferences(base, "always_use_skills:\n - write-docs\n - swiftui\n");
|
|
312
|
+
loadOnlyTestSkills(base);
|
|
313
|
+
|
|
314
|
+
const researchPrompt = await buildResearchMilestonePrompt("M001", "Test", base);
|
|
315
|
+
assert.match(researchPrompt, /Call Skill\(\{ skill: 'write-docs' \}\)/);
|
|
316
|
+
assert.match(researchPrompt, /Call Skill\(\{ skill: 'swiftui' \}\)/);
|
|
317
|
+
|
|
318
|
+
const planPrompt = await buildPlanMilestonePrompt("M001", "Test", base);
|
|
319
|
+
assert.match(planPrompt, /Call Skill\(\{ skill: 'write-docs' \}\)/);
|
|
320
|
+
assert.match(planPrompt, /Call Skill\(\{ skill: 'swiftui' \}\)/);
|
|
321
|
+
} finally {
|
|
322
|
+
cleanup(base);
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
test("skill manifest strict warnings require GSD_SKILL_MANIFEST_STRICT=1", (t) => {
|
|
327
|
+
const previousStrict = process.env.GSD_SKILL_MANIFEST_STRICT;
|
|
328
|
+
const previousStderr = setStderrLoggingEnabled(false);
|
|
329
|
+
t.after(() => {
|
|
330
|
+
if (previousStrict === undefined) {
|
|
331
|
+
delete process.env.GSD_SKILL_MANIFEST_STRICT;
|
|
332
|
+
} else {
|
|
333
|
+
process.env.GSD_SKILL_MANIFEST_STRICT = previousStrict;
|
|
334
|
+
}
|
|
335
|
+
setStderrLoggingEnabled(previousStderr);
|
|
336
|
+
_resetLogs();
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
process.env.GSD_SKILL_MANIFEST_STRICT = "0";
|
|
340
|
+
_resetLogs();
|
|
341
|
+
warnIfManifestHasMissingSkills("research-milestone", new Set());
|
|
342
|
+
assert.equal(drainLogs().length, 0, "strict=0 must preserve silent behavior");
|
|
343
|
+
|
|
344
|
+
process.env.GSD_SKILL_MANIFEST_STRICT = "1";
|
|
345
|
+
_resetLogs();
|
|
346
|
+
warnIfManifestHasMissingSkills("research-milestone", new Set());
|
|
347
|
+
const logs = drainLogs();
|
|
348
|
+
assert.ok(
|
|
349
|
+
logs.some(log => log.message.includes("skill-manifest: references uninstalled skill")),
|
|
350
|
+
"strict=1 should warn about missing manifest entries",
|
|
351
|
+
);
|
|
352
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// GSD2 + skill-manifest.test — unit coverage for the skill manifest resolver
|
|
2
|
+
//
|
|
3
|
+
// Focused tests for `resolveSkillManifest` and `filterSkillsByManifest`.
|
|
4
|
+
// Covers the wildcard semantics, the newly seeded unit-type entries
|
|
5
|
+
// (complete-milestone, validate-milestone, reassess-roadmap, research-slice,
|
|
6
|
+
// plan-slice, refine-slice, replan-slice, run-uat), and the deliberate
|
|
7
|
+
// wildcard fallback for the execute-task hot path (RFC #4779).
|
|
8
|
+
|
|
9
|
+
import test from "node:test";
|
|
10
|
+
import assert from "node:assert/strict";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
resolveSkillManifest,
|
|
14
|
+
filterSkillsByManifest,
|
|
15
|
+
} from "../skill-manifest.js";
|
|
16
|
+
|
|
17
|
+
const NEWLY_WIRED_UNIT_TYPES = [
|
|
18
|
+
"complete-milestone",
|
|
19
|
+
"validate-milestone",
|
|
20
|
+
"reassess-roadmap",
|
|
21
|
+
"research-slice",
|
|
22
|
+
"plan-slice",
|
|
23
|
+
"refine-slice",
|
|
24
|
+
"replan-slice",
|
|
25
|
+
"run-uat",
|
|
26
|
+
] as const;
|
|
27
|
+
|
|
28
|
+
test("resolveSkillManifest returns null for undefined unit type (wildcard)", () => {
|
|
29
|
+
assert.equal(resolveSkillManifest(undefined), null);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("resolveSkillManifest returns null for unknown unit types (wildcard fallback)", () => {
|
|
33
|
+
assert.equal(resolveSkillManifest("nonexistent-unit-type"), null);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("resolveSkillManifest returns null for execute-task (intentional wildcard)", () => {
|
|
37
|
+
// execute-task is the implementation hot path; allowlisting it requires
|
|
38
|
+
// per-task skill hints from task-plan frontmatter. Documented in
|
|
39
|
+
// skill-manifest.ts — regression guard.
|
|
40
|
+
assert.equal(resolveSkillManifest("execute-task"), null);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
for (const unitType of NEWLY_WIRED_UNIT_TYPES) {
|
|
44
|
+
test(`resolveSkillManifest returns a non-empty allowlist for '${unitType}'`, () => {
|
|
45
|
+
const allowlist = resolveSkillManifest(unitType);
|
|
46
|
+
assert.ok(allowlist !== null, `${unitType} should resolve to an allowlist, not wildcard`);
|
|
47
|
+
assert.ok(allowlist.length > 0, `${unitType} allowlist should not be empty`);
|
|
48
|
+
// Every entry must be lowercase (normalized).
|
|
49
|
+
for (const name of allowlist) {
|
|
50
|
+
assert.equal(name, name.toLowerCase(), `${unitType} entry '${name}' should be lowercase`);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
test("resolveSkillManifest: slice-level manifests include decompose-into-slices", () => {
|
|
56
|
+
// Planning-shaped slice flows all benefit from the decomposition skill.
|
|
57
|
+
// Sanity-check a representative entry from each.
|
|
58
|
+
for (const unitType of ["research-slice", "plan-slice", "refine-slice", "replan-slice"] as const) {
|
|
59
|
+
const allowlist = resolveSkillManifest(unitType);
|
|
60
|
+
assert.ok(
|
|
61
|
+
allowlist?.includes("decompose-into-slices"),
|
|
62
|
+
`${unitType} should list decompose-into-slices`,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("resolveSkillManifest: validation / completion flows include verify-before-complete", () => {
|
|
68
|
+
for (const unitType of ["complete-milestone", "validate-milestone", "run-uat"] as const) {
|
|
69
|
+
const allowlist = resolveSkillManifest(unitType);
|
|
70
|
+
assert.ok(
|
|
71
|
+
allowlist?.includes("verify-before-complete"),
|
|
72
|
+
`${unitType} should list verify-before-complete`,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("filterSkillsByManifest: pass-through when unit type is unknown", () => {
|
|
78
|
+
const skills = [{ name: "swiftui" }, { name: "solidity-security" }];
|
|
79
|
+
const result = filterSkillsByManifest(skills, "nonexistent-unit-type");
|
|
80
|
+
assert.deepEqual(result, skills);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("filterSkillsByManifest: pass-through when unitType is undefined", () => {
|
|
84
|
+
const skills = [{ name: "swiftui" }];
|
|
85
|
+
const result = filterSkillsByManifest(skills, undefined);
|
|
86
|
+
assert.deepEqual(result, skills);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("filterSkillsByManifest: restricts to allowlisted names for known unit type", () => {
|
|
90
|
+
// research-slice allowlists include decompose-into-slices but not swiftui.
|
|
91
|
+
const skills = [
|
|
92
|
+
{ name: "decompose-into-slices" },
|
|
93
|
+
{ name: "swiftui" },
|
|
94
|
+
{ name: "write-docs" },
|
|
95
|
+
];
|
|
96
|
+
const result = filterSkillsByManifest(skills, "research-slice");
|
|
97
|
+
const names = result.map(s => s.name);
|
|
98
|
+
assert.ok(names.includes("decompose-into-slices"));
|
|
99
|
+
assert.ok(names.includes("write-docs"));
|
|
100
|
+
assert.ok(!names.includes("swiftui"));
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("filterSkillsByManifest: matching is case-insensitive via normalize", () => {
|
|
104
|
+
const skills = [
|
|
105
|
+
{ name: "Write-Docs" }, // different case from manifest entry
|
|
106
|
+
{ name: "SWIFTUI" },
|
|
107
|
+
];
|
|
108
|
+
const result = filterSkillsByManifest(skills, "research-milestone");
|
|
109
|
+
const names = result.map(s => s.name);
|
|
110
|
+
assert.ok(names.includes("Write-Docs"));
|
|
111
|
+
assert.ok(!names.includes("SWIFTUI"));
|
|
112
|
+
});
|
|
@@ -6,13 +6,83 @@
|
|
|
6
6
|
|
|
7
7
|
import { describe, it } from "node:test";
|
|
8
8
|
import assert from "node:assert/strict";
|
|
9
|
-
import {
|
|
9
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
10
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
10
11
|
import { join, dirname } from "node:path";
|
|
12
|
+
import { tmpdir } from "node:os";
|
|
11
13
|
import { fileURLToPath } from "node:url";
|
|
14
|
+
import { restoreSliceState } from "../slice-parallel-orchestrator.ts";
|
|
12
15
|
|
|
13
16
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
17
|
const gsdDir = join(__dirname, "..");
|
|
15
18
|
|
|
19
|
+
function readLinuxProcessStartFingerprint(pid: number): string | null {
|
|
20
|
+
try {
|
|
21
|
+
const stat = readFileSync(`/proc/${pid}/stat`, "utf-8");
|
|
22
|
+
const afterCommand = stat.slice(stat.lastIndexOf(")") + 2).trim();
|
|
23
|
+
const fields = afterCommand.split(/\s+/);
|
|
24
|
+
const startTimeTicks = fields[19];
|
|
25
|
+
return startTimeTicks ? `linux-stat:${startTimeTicks}` : null;
|
|
26
|
+
} catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function readPsProcessStartFingerprint(pid: number): string | null {
|
|
32
|
+
try {
|
|
33
|
+
const raw = execFileSync("ps", ["-p", String(pid), "-o", "lstart="], {
|
|
34
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
35
|
+
encoding: "utf-8",
|
|
36
|
+
}).trim().replace(/\s+/g, " ");
|
|
37
|
+
return raw ? `ps-lstart:${raw}` : null;
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function readProcessStartFingerprint(pid: number): string | null {
|
|
44
|
+
return readLinuxProcessStartFingerprint(pid) ?? readPsProcessStartFingerprint(pid);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function makeTempProject(): string {
|
|
48
|
+
const basePath = mkdtempSync(join(tmpdir(), "gsd-slice-parallel-"));
|
|
49
|
+
mkdirSync(join(basePath, ".gsd"), { recursive: true });
|
|
50
|
+
return basePath;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function writeSliceOrchestratorState(
|
|
54
|
+
basePath: string,
|
|
55
|
+
worker: {
|
|
56
|
+
pid: number;
|
|
57
|
+
workerToken?: string;
|
|
58
|
+
processStartFingerprint?: string | null;
|
|
59
|
+
},
|
|
60
|
+
): void {
|
|
61
|
+
writeFileSync(
|
|
62
|
+
join(basePath, ".gsd", "slice-orchestrator.json"),
|
|
63
|
+
JSON.stringify({
|
|
64
|
+
active: true,
|
|
65
|
+
workers: [{
|
|
66
|
+
milestoneId: "M900",
|
|
67
|
+
sliceId: "S01",
|
|
68
|
+
pid: worker.pid,
|
|
69
|
+
workerToken: worker.workerToken,
|
|
70
|
+
processStartFingerprint: worker.processStartFingerprint,
|
|
71
|
+
worktreePath: join(basePath, ".gsd", "worktrees", "M900-S01"),
|
|
72
|
+
startedAt: Date.now(),
|
|
73
|
+
state: "running",
|
|
74
|
+
completedUnits: 0,
|
|
75
|
+
cost: 0,
|
|
76
|
+
}],
|
|
77
|
+
totalCost: 0,
|
|
78
|
+
maxWorkers: 1,
|
|
79
|
+
startedAt: Date.now(),
|
|
80
|
+
basePath,
|
|
81
|
+
}),
|
|
82
|
+
"utf-8",
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
16
86
|
describe("slice-parallel-orchestrator structural tests", () => {
|
|
17
87
|
it("orchestrator uses GSD_SLICE_LOCK env var", () => {
|
|
18
88
|
const source = readFileSync(join(gsdDir, "slice-parallel-orchestrator.ts"), "utf-8");
|
|
@@ -46,6 +116,99 @@ describe("slice-parallel-orchestrator structural tests", () => {
|
|
|
46
116
|
"Orchestrator must also pass GSD_MILESTONE_LOCK for milestone context",
|
|
47
117
|
);
|
|
48
118
|
});
|
|
119
|
+
|
|
120
|
+
it("recovery preserves terminal workers for coordinator-side collection", () => {
|
|
121
|
+
const source = readFileSync(join(gsdDir, "slice-parallel-orchestrator.ts"), "utf-8");
|
|
122
|
+
assert.ok(
|
|
123
|
+
source.includes('} else if (w.state === "running")') &&
|
|
124
|
+
source.includes("survivors.push(w);"),
|
|
125
|
+
"Recovery must only prune dead running workers, not stopped/error workers",
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("recovered PID-only workers are validated before signaling", () => {
|
|
130
|
+
const source = readFileSync(join(gsdDir, "slice-parallel-orchestrator.ts"), "utf-8");
|
|
131
|
+
assert.ok(
|
|
132
|
+
source.includes("isRecoveredSliceWorkerAlive(worker)") &&
|
|
133
|
+
source.includes('process.kill(worker.pid, "SIGTERM")'),
|
|
134
|
+
"stopSliceParallel must validate recovered worker identity before signaling a PID",
|
|
135
|
+
);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("persists worker identity for crash recovery", () => {
|
|
139
|
+
const source = readFileSync(join(gsdDir, "slice-parallel-orchestrator.ts"), "utf-8");
|
|
140
|
+
assert.ok(
|
|
141
|
+
source.includes("workerToken") &&
|
|
142
|
+
source.includes("processStartFingerprint") &&
|
|
143
|
+
source.includes("GSD_SLICE_WORKER_TOKEN"),
|
|
144
|
+
"Orchestrator must persist stable worker identity metadata for recovered workers",
|
|
145
|
+
);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("spawn failures remove stale worker state and worktree", () => {
|
|
149
|
+
const source = readFileSync(join(gsdDir, "slice-parallel-orchestrator.ts"), "utf-8");
|
|
150
|
+
assert.ok(
|
|
151
|
+
source.includes("sliceState.workers.delete(slice.id)") &&
|
|
152
|
+
source.includes("removeWorktree(basePath, wtName, { deleteBranch: true, force: true })"),
|
|
153
|
+
"Failed slice worker spawns must remove stale worker state and clean up the worktree",
|
|
154
|
+
);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe("slice-parallel-orchestrator recovery identity", () => {
|
|
159
|
+
it("rejects a live PID when the process start fingerprint does not match", () => {
|
|
160
|
+
const basePath = makeTempProject();
|
|
161
|
+
try {
|
|
162
|
+
writeSliceOrchestratorState(basePath, {
|
|
163
|
+
pid: process.pid,
|
|
164
|
+
processStartFingerprint: "mismatched-fingerprint",
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
const restored = restoreSliceState(basePath);
|
|
168
|
+
assert.equal(restored, null, "mismatched fingerprint is treated as a dead worker");
|
|
169
|
+
assert.equal(
|
|
170
|
+
existsSync(join(basePath, ".gsd", "slice-orchestrator.json")),
|
|
171
|
+
false,
|
|
172
|
+
"state file is removed when no recovered worker identity validates",
|
|
173
|
+
);
|
|
174
|
+
} finally {
|
|
175
|
+
rmSync(basePath, { recursive: true, force: true });
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("keeps a recovered worker when PID, token, and process start fingerprint match", async () => {
|
|
180
|
+
const basePath = makeTempProject();
|
|
181
|
+
const token = `test-token-${Date.now()}`;
|
|
182
|
+
const child = spawn(
|
|
183
|
+
process.execPath,
|
|
184
|
+
["-e", "setTimeout(() => {}, 30000)"],
|
|
185
|
+
{
|
|
186
|
+
env: { ...process.env, GSD_SLICE_WORKER_TOKEN: token },
|
|
187
|
+
stdio: "ignore",
|
|
188
|
+
},
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
assert.ok(child.pid, "child process has a pid");
|
|
193
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
194
|
+
const fingerprint = readProcessStartFingerprint(child.pid!);
|
|
195
|
+
if (!fingerprint) return;
|
|
196
|
+
|
|
197
|
+
writeSliceOrchestratorState(basePath, {
|
|
198
|
+
pid: child.pid!,
|
|
199
|
+
workerToken: token,
|
|
200
|
+
processStartFingerprint: fingerprint,
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const restored = restoreSliceState(basePath);
|
|
204
|
+
assert.ok(restored, "matching worker identity is restored");
|
|
205
|
+
assert.equal(restored.workers.length, 1);
|
|
206
|
+
assert.equal(restored.workers[0].pid, child.pid);
|
|
207
|
+
} finally {
|
|
208
|
+
child.kill("SIGTERM");
|
|
209
|
+
rmSync(basePath, { recursive: true, force: true });
|
|
210
|
+
}
|
|
211
|
+
});
|
|
49
212
|
});
|
|
50
213
|
|
|
51
214
|
describe("slice_parallel preference gating", () => {
|
|
@@ -835,9 +835,9 @@ describe("state-machine-full-walkthrough", () => {
|
|
|
835
835
|
assert.ok(state.blockers.length > 0, "should have blockers");
|
|
836
836
|
});
|
|
837
837
|
|
|
838
|
-
test("no eligible slice (all deps unmet) →
|
|
838
|
+
test("no eligible slice (all deps unmet) → blocked", async () => {
|
|
839
839
|
const base = createFixtureBase();
|
|
840
|
-
// S01 depends on S00 which doesn't exist
|
|
840
|
+
// S01 depends on S00 which doesn't exist.
|
|
841
841
|
writeRoadmap(base, "M001", [
|
|
842
842
|
"# M001: Test Milestone",
|
|
843
843
|
"",
|
|
@@ -851,9 +851,9 @@ describe("state-machine-full-walkthrough", () => {
|
|
|
851
851
|
invalidateStateCache();
|
|
852
852
|
const state = await deriveState(base);
|
|
853
853
|
|
|
854
|
-
|
|
855
|
-
assert.equal(state.
|
|
856
|
-
assert.
|
|
854
|
+
assert.equal(state.phase, "blocked");
|
|
855
|
+
assert.equal(state.activeSlice, null);
|
|
856
|
+
assert.ok(state.blockers.some(b => b.includes("No slice eligible")));
|
|
857
857
|
});
|
|
858
858
|
});
|
|
859
859
|
|
|
@@ -271,96 +271,15 @@ describe("structured-data-formatter: measureSavings", () => {
|
|
|
271
271
|
});
|
|
272
272
|
|
|
273
273
|
// ---------------------------------------------------------------------------
|
|
274
|
-
//
|
|
274
|
+
// Dropped (#4836): the previous "realistic savings" suite asserted that the
|
|
275
|
+
// compact formatter beat hand-authored "typical markdown" baselines by 30%+.
|
|
276
|
+
// Those baselines were written by the test author to make the assertion pass
|
|
277
|
+
// — they are not the format GSD actually emits anywhere else, so shifting
|
|
278
|
+
// the compact output by any amount could be absorbed by padding the baseline.
|
|
279
|
+
// There was no regression signal.
|
|
280
|
+
//
|
|
281
|
+
// The unit tests above already pin the compact format's structure byte for
|
|
282
|
+
// byte; a genuine regression changes one of those exact-string assertions.
|
|
283
|
+
// If a size-budget guarantee is needed later, capture a real production
|
|
284
|
+
// baseline into a fixture file and assert against a checked-in byte count.
|
|
275
285
|
// ---------------------------------------------------------------------------
|
|
276
|
-
|
|
277
|
-
describe("structured-data-formatter: realistic savings", () => {
|
|
278
|
-
it("decisions compact format saves 30%+ vs markdown table", () => {
|
|
279
|
-
const decisions = [sampleDecision, sampleDecision2];
|
|
280
|
-
|
|
281
|
-
// Simulate a typical markdown table
|
|
282
|
-
const markdownTable = [
|
|
283
|
-
"| ID | When | Scope | Decision | Choice | Rationale | Revisable |",
|
|
284
|
-
"|------|------------|--------------|-------------------------|------------------------|--------------------------|-----------|",
|
|
285
|
-
"| D001 | M001/S01 | architecture | Use SQLite for storage | WAL mode, single-writer | Built-in, no external deps | yes |",
|
|
286
|
-
"| D002 | M001/S02 | testing | Unit test all parsers | node:test framework | Fast, zero-dependency | no |",
|
|
287
|
-
].join("\n");
|
|
288
|
-
|
|
289
|
-
const compactOutput = formatDecisionsCompact(decisions);
|
|
290
|
-
const savings = measureSavings(compactOutput, markdownTable);
|
|
291
|
-
assert.ok(
|
|
292
|
-
savings >= 30,
|
|
293
|
-
`expected >=30% savings, got ${savings.toFixed(1)}%`,
|
|
294
|
-
);
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
it("requirements compact format saves 30%+ vs markdown sections", () => {
|
|
298
|
-
const requirements = [sampleRequirement, sampleRequirement2];
|
|
299
|
-
|
|
300
|
-
// Simulate verbose markdown format with all fields
|
|
301
|
-
const markdownSections = [
|
|
302
|
-
"## R001",
|
|
303
|
-
"",
|
|
304
|
-
"- **Class:** functional",
|
|
305
|
-
"- **Status:** active",
|
|
306
|
-
"- **Description:** Response latency < 200ms for API endpoints",
|
|
307
|
-
"- **Why:** Critical for user experience",
|
|
308
|
-
"- **Source:** architecture review",
|
|
309
|
-
"- **Primary Owner:** S01",
|
|
310
|
-
"- **Supporting Slices:** S02, S03",
|
|
311
|
-
"- **Validation:** Load test confirms P99 < 200ms",
|
|
312
|
-
"- **Notes:** Monitor in production",
|
|
313
|
-
"",
|
|
314
|
-
"## R002",
|
|
315
|
-
"",
|
|
316
|
-
"- **Class:** non-functional",
|
|
317
|
-
"- **Status:** active",
|
|
318
|
-
"- **Description:** Data consistency across writes",
|
|
319
|
-
"- **Why:** Prevents data loss",
|
|
320
|
-
"- **Source:** data team review",
|
|
321
|
-
"- **Primary Owner:** S02",
|
|
322
|
-
"- **Supporting Slices:** S01",
|
|
323
|
-
"- **Validation:** Integration test suite",
|
|
324
|
-
"- **Notes:** Requires WAL mode",
|
|
325
|
-
].join("\n");
|
|
326
|
-
|
|
327
|
-
const compactOutput = formatRequirementsCompact(requirements);
|
|
328
|
-
const savings = measureSavings(compactOutput, markdownSections);
|
|
329
|
-
assert.ok(
|
|
330
|
-
savings >= 30,
|
|
331
|
-
`expected >=30% savings, got ${savings.toFixed(1)}%`,
|
|
332
|
-
);
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
it("task plan compact format saves 30%+ vs markdown sections", () => {
|
|
336
|
-
const tasks = [sampleTaskDone, sampleTaskPending];
|
|
337
|
-
|
|
338
|
-
// Simulate verbose markdown task format
|
|
339
|
-
const markdownTasks = [
|
|
340
|
-
"## T01 - Database schema",
|
|
341
|
-
"",
|
|
342
|
-
"- **Status:** Done",
|
|
343
|
-
"- **Estimate:** 30m",
|
|
344
|
-
"- **Description:** Create tables for decisions and requirements",
|
|
345
|
-
"- **Files:**",
|
|
346
|
-
" - src/db.ts",
|
|
347
|
-
" - src/schema.ts",
|
|
348
|
-
"",
|
|
349
|
-
"## T02 - API endpoints",
|
|
350
|
-
"",
|
|
351
|
-
"- **Status:** Pending",
|
|
352
|
-
"- **Estimate:** 1h",
|
|
353
|
-
"- **Description:** REST endpoints for CRUD operations",
|
|
354
|
-
"- **Files:**",
|
|
355
|
-
" - src/api.ts",
|
|
356
|
-
"- **Verify:** npm test",
|
|
357
|
-
].join("\n");
|
|
358
|
-
|
|
359
|
-
const compactOutput = formatTaskPlanCompact(tasks);
|
|
360
|
-
const savings = measureSavings(compactOutput, markdownTasks);
|
|
361
|
-
assert.ok(
|
|
362
|
-
savings >= 30,
|
|
363
|
-
`expected >=30% savings, got ${savings.toFixed(1)}%`,
|
|
364
|
-
);
|
|
365
|
-
});
|
|
366
|
-
});
|