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
|
@@ -1,108 +1,109 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* survivor-branch-complete.test.ts — #2358
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* The bug: `bootstrapAutoSession` found a survivor milestone branch
|
|
5
|
+
* (previous session's worktree/branch that was never merged) but only
|
|
6
|
+
* triggered recovery when `state.phase === "pre-planning"`. In
|
|
7
|
+
* `phase === "complete"` the milestone artifacts existed but the
|
|
8
|
+
* finalization path (merge + cleanup) never ran, leaving the worktree
|
|
9
|
+
* and branch alive indefinitely.
|
|
9
10
|
*
|
|
10
|
-
* The fix broadens the
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* The fix broadens the detection to include `phase === "complete"` and
|
|
12
|
+
* routes to a finalize-via-mergeAndExit path.
|
|
13
|
+
*
|
|
14
|
+
* The previous version of this file was 4 scenarios that re-implemented
|
|
15
|
+
* the decision logic inline and called `.includes(phase)` on
|
|
16
|
+
* locally-declared arrays — testing the test, not the code. Called out
|
|
17
|
+
* in #4832 and parent #4784 as a pure-tautology case (zero imports
|
|
18
|
+
* from production).
|
|
19
|
+
*
|
|
20
|
+
* This rewrite imports `decideSurvivorAction` from auto-start.ts (a
|
|
21
|
+
* helper extracted in the accompanying refactor) and drives the full
|
|
22
|
+
* decision table through the real function. The helper is wired into
|
|
23
|
+
* `bootstrapAutoSession` at the two call sites that previously used
|
|
24
|
+
* inline conditionals, so the assertions here fail if someone reverts
|
|
25
|
+
* the helper or narrows its branches.
|
|
13
26
|
*/
|
|
14
27
|
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const testPhase = "complete";
|
|
38
|
-
|
|
39
|
-
const detectedBefore = phasesBeforeFix.includes(testPhase);
|
|
40
|
-
assertEq(detectedBefore, false, "before fix: phase=complete should NOT trigger survivor detection");
|
|
41
|
-
|
|
42
|
-
const detectedAfter = phasesAfterFix.includes(testPhase);
|
|
43
|
-
assertEq(detectedAfter, true, "after fix: phase=complete SHOULD trigger survivor detection");
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// ═══ Test: pre-planning survivor detection still works ═══════════════════════
|
|
47
|
-
|
|
48
|
-
{
|
|
49
|
-
console.log("\n=== #2358: pre-planning survivor detection is not broken ===");
|
|
50
|
-
|
|
51
|
-
const phasesAfterFix = ["pre-planning", "complete"];
|
|
52
|
-
const testPhase = "pre-planning";
|
|
53
|
-
|
|
54
|
-
const detected = phasesAfterFix.includes(testPhase);
|
|
55
|
-
assertEq(detected, true, "pre-planning should still trigger survivor detection after fix");
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// ═══ Test: other phases do NOT trigger survivor detection ════════════════════
|
|
59
|
-
|
|
60
|
-
{
|
|
61
|
-
console.log("\n=== #2358: other phases should NOT trigger survivor detection ===");
|
|
62
|
-
|
|
63
|
-
const phasesAfterFix = ["pre-planning", "complete"];
|
|
64
|
-
|
|
65
|
-
for (const phase of ["planning", "executing", "blocked", "needs-discussion"]) {
|
|
66
|
-
const detected = phasesAfterFix.includes(phase);
|
|
67
|
-
assertEq(detected, false, `phase=${phase} should NOT trigger survivor detection`);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// ═══ Test: phase=complete + hasSurvivorBranch should trigger finalization ═════
|
|
72
|
-
|
|
73
|
-
{
|
|
74
|
-
console.log("\n=== #2358: phase=complete + survivor branch triggers finalization path ===");
|
|
75
|
-
|
|
76
|
-
// Simulate the decision logic after the fix:
|
|
77
|
-
// if (hasSurvivorBranch && state.phase === "complete") -> finalize
|
|
78
|
-
// if (hasSurvivorBranch && state.phase === "needs-discussion") -> discuss
|
|
79
|
-
// if (!hasSurvivorBranch && state.phase === "complete") -> showSmartEntry
|
|
80
|
-
|
|
81
|
-
const scenarios = [
|
|
82
|
-
{ hasSurvivorBranch: true, phase: "complete", expected: "finalize" },
|
|
83
|
-
{ hasSurvivorBranch: true, phase: "needs-discussion", expected: "discuss" },
|
|
84
|
-
{ hasSurvivorBranch: true, phase: "pre-planning", expected: "continue" },
|
|
85
|
-
{ hasSurvivorBranch: false, phase: "complete", expected: "showSmartEntry" },
|
|
86
|
-
];
|
|
87
|
-
|
|
88
|
-
for (const { hasSurvivorBranch, phase, expected } of scenarios) {
|
|
89
|
-
let result: string;
|
|
90
|
-
if (hasSurvivorBranch && phase === "complete") {
|
|
91
|
-
result = "finalize";
|
|
92
|
-
} else if (hasSurvivorBranch && phase === "needs-discussion") {
|
|
93
|
-
result = "discuss";
|
|
94
|
-
} else if (!hasSurvivorBranch && (!phase || phase === "complete")) {
|
|
95
|
-
result = "showSmartEntry";
|
|
96
|
-
} else {
|
|
97
|
-
result = "continue";
|
|
28
|
+
import { describe, test } from "node:test";
|
|
29
|
+
import assert from "node:assert/strict";
|
|
30
|
+
|
|
31
|
+
import { decideSurvivorAction } from "../auto-start.ts";
|
|
32
|
+
import type { SurvivorAction } from "../auto-start.ts";
|
|
33
|
+
|
|
34
|
+
describe("decideSurvivorAction (#2358)", () => {
|
|
35
|
+
test("no survivor branch → no action, regardless of phase", () => {
|
|
36
|
+
const phases = [
|
|
37
|
+
"pre-planning",
|
|
38
|
+
"planning",
|
|
39
|
+
"executing",
|
|
40
|
+
"complete",
|
|
41
|
+
"needs-discussion",
|
|
42
|
+
"blocked",
|
|
43
|
+
"",
|
|
44
|
+
null,
|
|
45
|
+
undefined,
|
|
46
|
+
];
|
|
47
|
+
for (const phase of phases) {
|
|
48
|
+
const got: SurvivorAction = decideSurvivorAction(false, phase);
|
|
49
|
+
assert.equal(got, "none", `phase=${phase ?? "(nullish)"} → expected 'none', got '${got}'`);
|
|
98
50
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("survivor + needs-discussion → 'discuss' (#1726)", () => {
|
|
54
|
+
assert.equal(decideSurvivorAction(true, "needs-discussion"), "discuss");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("survivor + complete → 'finalize' (#2358 — the bug this regression guards)", () => {
|
|
58
|
+
// This is THE assertion that fails if someone reverts the fix and
|
|
59
|
+
// narrows the recovery to pre-planning only.
|
|
60
|
+
assert.equal(decideSurvivorAction(true, "complete"), "finalize");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("survivor + other phase → 'none' (caller continues normal flow)", () => {
|
|
64
|
+
// pre-planning, planning, executing, blocked — survivor alone is
|
|
65
|
+
// not sufficient to trigger recovery. Normal auto-mode picks up
|
|
66
|
+
// from state. This protects against regressions that try to run
|
|
67
|
+
// finalize on every survivor regardless of phase.
|
|
68
|
+
const passThroughPhases = ["pre-planning", "planning", "executing", "blocked", ""];
|
|
69
|
+
for (const phase of passThroughPhases) {
|
|
70
|
+
assert.equal(
|
|
71
|
+
decideSurvivorAction(true, phase),
|
|
72
|
+
"none",
|
|
73
|
+
`survivor + phase=${phase} → expected 'none', got ${decideSurvivorAction(true, phase)}`,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("decision table covers the three outcomes the bootstrap code needs", () => {
|
|
79
|
+
// Belt-and-suspenders: enumerate (hasSurvivor, phase) and assert
|
|
80
|
+
// the complete truth table. If someone adds a 4th outcome, this
|
|
81
|
+
// test fails loudly so they must update both the helper and the
|
|
82
|
+
// bootstrap wiring.
|
|
83
|
+
const cases: Array<{ hasSurvivor: boolean; phase: string | null; expected: SurvivorAction }> = [
|
|
84
|
+
{ hasSurvivor: true, phase: "needs-discussion", expected: "discuss" },
|
|
85
|
+
{ hasSurvivor: true, phase: "complete", expected: "finalize" },
|
|
86
|
+
{ hasSurvivor: true, phase: "pre-planning", expected: "none" },
|
|
87
|
+
{ hasSurvivor: true, phase: "planning", expected: "none" },
|
|
88
|
+
{ hasSurvivor: true, phase: null, expected: "none" },
|
|
89
|
+
{ hasSurvivor: false, phase: "complete", expected: "none" },
|
|
90
|
+
{ hasSurvivor: false, phase: "needs-discussion", expected: "none" },
|
|
91
|
+
{ hasSurvivor: false, phase: null, expected: "none" },
|
|
92
|
+
];
|
|
93
|
+
const outcomes = new Set<SurvivorAction>();
|
|
94
|
+
for (const { hasSurvivor, phase, expected } of cases) {
|
|
95
|
+
const got = decideSurvivorAction(hasSurvivor, phase);
|
|
96
|
+
outcomes.add(got);
|
|
97
|
+
assert.equal(
|
|
98
|
+
got,
|
|
99
|
+
expected,
|
|
100
|
+
`(hasSurvivor=${hasSurvivor}, phase=${phase}) → expected '${expected}', got '${got}'`,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
assert.deepEqual(
|
|
104
|
+
[...outcomes].sort(),
|
|
105
|
+
["discuss", "finalize", "none"],
|
|
106
|
+
"decision function should produce exactly three outcomes",
|
|
104
107
|
);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
report();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -120,3 +120,34 @@ test('sync-lock: overrides stale lock file (mtime backdated)', (t) => {
|
|
|
120
120
|
cleanupDir(base);
|
|
121
121
|
}
|
|
122
122
|
});
|
|
123
|
+
|
|
124
|
+
test('sync-lock: EPERM from live owner PID prevents stale lock stealing', () => {
|
|
125
|
+
const base = tempDir();
|
|
126
|
+
fs.mkdirSync(path.join(base, '.gsd'), { recursive: true });
|
|
127
|
+
const lockPath = path.join(base, '.gsd', 'sync.lock');
|
|
128
|
+
const fakePid = process.pid + 100_000;
|
|
129
|
+
const originalKill = process.kill;
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
fs.writeFileSync(lockPath, JSON.stringify({ pid: fakePid, acquired_at: new Date(0).toISOString() }));
|
|
133
|
+
const staleTime = new Date(Date.now() - 120_000);
|
|
134
|
+
fs.utimesSync(lockPath, staleTime, staleTime);
|
|
135
|
+
|
|
136
|
+
process.kill = ((pid: number, signal?: NodeJS.Signals | number) => {
|
|
137
|
+
if (pid === fakePid && signal === 0) {
|
|
138
|
+
const err = new Error('operation not permitted') as NodeJS.ErrnoException;
|
|
139
|
+
err.code = 'EPERM';
|
|
140
|
+
throw err;
|
|
141
|
+
}
|
|
142
|
+
return originalKill(pid, signal);
|
|
143
|
+
}) as typeof process.kill;
|
|
144
|
+
|
|
145
|
+
const result = acquireSyncLock(base, 100);
|
|
146
|
+
assert.strictEqual(result.acquired, false, 'EPERM owner should be treated as live');
|
|
147
|
+
const content = JSON.parse(fs.readFileSync(lockPath, 'utf-8'));
|
|
148
|
+
assert.strictEqual(content.pid, fakePid, 'lock file should not be overwritten');
|
|
149
|
+
} finally {
|
|
150
|
+
process.kill = originalKill;
|
|
151
|
+
cleanupDir(base);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
@@ -13,6 +13,7 @@ import { describe, it } from 'node:test'
|
|
|
13
13
|
import assert from 'node:assert/strict'
|
|
14
14
|
import { readFileSync } from 'node:fs'
|
|
15
15
|
import { resolve } from 'node:path'
|
|
16
|
+
import { extractSourceRegion } from "./test-helpers.ts";
|
|
16
17
|
|
|
17
18
|
const src = readFileSync(
|
|
18
19
|
resolve(process.cwd(), 'src', 'resources', 'extensions', 'gsd', 'auto-worktree.ts'),
|
|
@@ -33,14 +34,14 @@ describe('syncWorktreeStateBack skips current milestone (#3641)', () => {
|
|
|
33
34
|
assert.ok(fnStart !== -1)
|
|
34
35
|
|
|
35
36
|
// Get a reasonable portion of the function
|
|
36
|
-
const fnBlock = src
|
|
37
|
+
const fnBlock = extractSourceRegion(src, 'function syncWorktreeStateBack(', { fromIdx: fnStart })
|
|
37
38
|
|
|
38
39
|
// Find the for loop iterating milestones
|
|
39
40
|
const loopIdx = fnBlock.indexOf('for (const mid of wtMilestones)')
|
|
40
41
|
assert.ok(loopIdx !== -1, 'milestone iteration loop must exist')
|
|
41
42
|
|
|
42
43
|
// After the loop, there should be the skip guard
|
|
43
|
-
const loopBody = fnBlock
|
|
44
|
+
const loopBody = extractSourceRegion(fnBlock, 'for (const mid of wtMilestones)', { fromIdx: loopIdx })
|
|
44
45
|
assert.ok(
|
|
45
46
|
loopBody.includes('mid === milestoneId'),
|
|
46
47
|
'mid === milestoneId skip guard must exist inside the milestone loop',
|
|
@@ -55,7 +56,7 @@ describe('syncWorktreeStateBack skips current milestone (#3641)', () => {
|
|
|
55
56
|
const fnStart = src.indexOf('function syncWorktreeStateBack(')
|
|
56
57
|
assert.ok(fnStart !== -1)
|
|
57
58
|
|
|
58
|
-
const fnBlock = src
|
|
59
|
+
const fnBlock = extractSourceRegion(src, 'function syncWorktreeStateBack(', { fromIdx: fnStart })
|
|
59
60
|
|
|
60
61
|
assert.ok(
|
|
61
62
|
fnBlock.includes('syncMilestoneDir('),
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for test-helpers.ts — the timing helpers (waitForCondition,
|
|
3
|
+
* findLine) used to replace magic-number sleeps and positional line
|
|
4
|
+
* indexing in the test suite.
|
|
5
|
+
*
|
|
6
|
+
* The `extractSourceRegion` helper (introduced in #4773/#4774) is
|
|
7
|
+
* deliberately NOT tested here. It is the source-grep antipattern that
|
|
8
|
+
* #4784 names as the root problem; tests against toy fixtures only
|
|
9
|
+
* legitimize the pattern without validating behaviour. Its test cases
|
|
10
|
+
* were removed as part of #4834 — callers are being migrated to
|
|
11
|
+
* behaviour tests one file at a time, after which the helper is slated
|
|
12
|
+
* for deletion.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import test from "node:test";
|
|
16
|
+
import assert from "node:assert/strict";
|
|
17
|
+
|
|
18
|
+
import { waitForCondition, findLine } from "./test-helpers.ts";
|
|
19
|
+
|
|
20
|
+
// ─── waitForCondition ─────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
test("waitForCondition returns immediately when condition is true", async () => {
|
|
23
|
+
const result = await waitForCondition(() => true);
|
|
24
|
+
assert.equal(result, true);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("waitForCondition waits and returns when condition becomes true", async () => {
|
|
28
|
+
let flipped = false;
|
|
29
|
+
setTimeout(() => { flipped = true; }, 30);
|
|
30
|
+
const result = await waitForCondition(() => flipped, { intervalMs: 5 });
|
|
31
|
+
assert.equal(result, true);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("waitForCondition throws after timeout with description", async () => {
|
|
35
|
+
await assert.rejects(
|
|
36
|
+
waitForCondition(() => false, { timeoutMs: 50, intervalMs: 5, description: "the flag to flip" }),
|
|
37
|
+
/waiting for the flag to flip/i,
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("waitForCondition surfaces last error on timeout", async () => {
|
|
42
|
+
await assert.rejects(
|
|
43
|
+
waitForCondition(
|
|
44
|
+
() => { throw new Error("probe failed"); },
|
|
45
|
+
{ timeoutMs: 30, intervalMs: 5, description: "probe" },
|
|
46
|
+
),
|
|
47
|
+
/probe failed/,
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("waitForCondition returns the truthy value (not just true)", async () => {
|
|
52
|
+
let n = 0;
|
|
53
|
+
const result = await waitForCondition(() => {
|
|
54
|
+
n++;
|
|
55
|
+
return n >= 3 ? { ready: true, iteration: n } : null;
|
|
56
|
+
}, { intervalMs: 5 });
|
|
57
|
+
// The helper only resolves when the condition returns a truthy value, so
|
|
58
|
+
// result cannot be null here. Assert it and narrow for the follow-ups.
|
|
59
|
+
assert.ok(result, "waitForCondition must resolve with a truthy value, not null");
|
|
60
|
+
assert.equal(result.ready, true);
|
|
61
|
+
assert.equal(result.iteration, 3);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// ─── findLine ─────────────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
test("findLine locates a line by regex", () => {
|
|
67
|
+
const output = "header\nstatus: ok\nfooter";
|
|
68
|
+
const match = findLine(output, /^status:/);
|
|
69
|
+
assert.equal(match.index, 1);
|
|
70
|
+
assert.equal(match.text, "status: ok");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("findLine locates a line by predicate", () => {
|
|
74
|
+
const output = "a\nb\nc";
|
|
75
|
+
const match = findLine(output, (l) => l === "b");
|
|
76
|
+
assert.equal(match.index, 1);
|
|
77
|
+
assert.equal(match.text, "b");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("findLine throws with preview when no line matches", () => {
|
|
81
|
+
assert.throws(
|
|
82
|
+
() => findLine("a\nb\nc", /NOTFOUND/),
|
|
83
|
+
/First 10 lines/,
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("findLine resets lastIndex between lines for /g regex patterns", () => {
|
|
88
|
+
// Without the reset, RegExp.test with /g flag stateful-advances lastIndex
|
|
89
|
+
// and can skip matches on subsequent calls. Verify the reset keeps
|
|
90
|
+
// per-line testing deterministic.
|
|
91
|
+
const output = "foo\nfoo\nfoo";
|
|
92
|
+
const globalRe = /foo/g;
|
|
93
|
+
const match = findLine(output, globalRe);
|
|
94
|
+
assert.equal(match.index, 0);
|
|
95
|
+
// Second call on the same pattern must also match — would fail without reset
|
|
96
|
+
const match2 = findLine(output, globalRe);
|
|
97
|
+
assert.equal(match2.index, 0);
|
|
98
|
+
});
|
|
@@ -59,3 +59,156 @@ export function createTestContext() {
|
|
|
59
59
|
|
|
60
60
|
return { assertEq, assertTrue, assertMatch, assertNoMatch, report };
|
|
61
61
|
}
|
|
62
|
+
|
|
63
|
+
// ─── Source-inspection helpers (DEPRECATED — do not add new callers) ──────────
|
|
64
|
+
//
|
|
65
|
+
// `extractSourceRegion` was introduced in #4773 / #4774 as a more-durable
|
|
66
|
+
// replacement for hand-rolled `src.slice(idx, idx + N)` patterns. Issue
|
|
67
|
+
// #4784 surfaced that the helper is the wrong target: its use case —
|
|
68
|
+
// testing that an identifier or phrase exists in production source text
|
|
69
|
+
// — is the source-grep antipattern that causes false coverage in the
|
|
70
|
+
// first place. Tests should exercise behaviour (call the function,
|
|
71
|
+
// assert on its return value or observable effects) rather than check
|
|
72
|
+
// whether specific identifier strings remain in source.
|
|
73
|
+
//
|
|
74
|
+
// CONTRIBUTING.md forbids new callers of this helper. Existing callers
|
|
75
|
+
// are being migrated one file at a time; the helper is slated for
|
|
76
|
+
// removal once the last caller is converted.
|
|
77
|
+
//
|
|
78
|
+
// Do not add new callers. If you are tempted to, file an issue
|
|
79
|
+
// describing the behavioural invariant you want to test and we will
|
|
80
|
+
// find a runtime assertion instead.
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @deprecated Use a real behaviour test against the function under
|
|
84
|
+
* inspection instead. See #4784 and CONTRIBUTING.md > "Test behaviour,
|
|
85
|
+
* not source shape". No new callers.
|
|
86
|
+
*
|
|
87
|
+
* Extract a region of source between a start anchor and either an explicit
|
|
88
|
+
* end anchor or, if none is given, a set of reasonable structural
|
|
89
|
+
* terminators (next `private `/`export `/`function `/`class `/`interface `/
|
|
90
|
+
* `//` section separator). Falls back to end-of-source if none match.
|
|
91
|
+
*
|
|
92
|
+
* @param src The source text.
|
|
93
|
+
* @param startAnchor Literal substring that marks the start of the region.
|
|
94
|
+
* Typically a function name, a section header comment, or
|
|
95
|
+
* a distinctive statement.
|
|
96
|
+
* @param endAnchor Optional. Either a literal substring that marks the end,
|
|
97
|
+
* or `{ fromIdx: number }` to find the *next* occurrence
|
|
98
|
+
* of `startAnchor` at or after `fromIdx` (useful when the
|
|
99
|
+
* anchor appears multiple times and a positional search
|
|
100
|
+
* is required). When omitted, structural terminators are
|
|
101
|
+
* used.
|
|
102
|
+
*
|
|
103
|
+
* Returns the extracted region (including the start anchor), or an empty
|
|
104
|
+
* string if `startAnchor` is not found.
|
|
105
|
+
*/
|
|
106
|
+
export function extractSourceRegion(
|
|
107
|
+
src: string,
|
|
108
|
+
startAnchor: string,
|
|
109
|
+
endAnchor?: string | { fromIdx: number },
|
|
110
|
+
): string {
|
|
111
|
+
const fromIdx = typeof endAnchor === "object" && endAnchor !== null
|
|
112
|
+
? endAnchor.fromIdx
|
|
113
|
+
: 0;
|
|
114
|
+
const endLiteral = typeof endAnchor === "string" ? endAnchor : undefined;
|
|
115
|
+
|
|
116
|
+
const startIdx = src.indexOf(startAnchor, fromIdx);
|
|
117
|
+
if (startIdx < 0) return "";
|
|
118
|
+
|
|
119
|
+
if (endLiteral) {
|
|
120
|
+
const endIdx = src.indexOf(endLiteral, startIdx + startAnchor.length);
|
|
121
|
+
return endIdx > 0 ? src.slice(startIdx, endIdx) : src.slice(startIdx);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Heuristic terminators — the next sibling declaration/section.
|
|
125
|
+
const terminators = [
|
|
126
|
+
"\n private ",
|
|
127
|
+
"\n public ",
|
|
128
|
+
"\n protected ",
|
|
129
|
+
"\n static ",
|
|
130
|
+
"\nexport function ",
|
|
131
|
+
"\nexport async function ",
|
|
132
|
+
"\nfunction ",
|
|
133
|
+
"\nasync function ",
|
|
134
|
+
"\nexport class ",
|
|
135
|
+
"\nclass ",
|
|
136
|
+
"\nexport interface ",
|
|
137
|
+
"\ninterface ",
|
|
138
|
+
"\n// ─", // section separator comments
|
|
139
|
+
"\n/** ", // next docblock
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
let earliestEnd = -1;
|
|
143
|
+
for (const t of terminators) {
|
|
144
|
+
const idx = src.indexOf(t, startIdx + startAnchor.length);
|
|
145
|
+
if (idx > 0 && (earliestEnd < 0 || idx < earliestEnd)) earliestEnd = idx;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return earliestEnd > 0 ? src.slice(startIdx, earliestEnd) : src.slice(startIdx);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Poll `condition()` until it returns truthy or `timeoutMs` elapses.
|
|
153
|
+
* Returns the truthy value from `condition()`, or throws on timeout.
|
|
154
|
+
*
|
|
155
|
+
* Use this instead of `await new Promise(r => setTimeout(r, <magic-ms>))`
|
|
156
|
+
* when waiting for a state change that tests produce. The fixed-sleep
|
|
157
|
+
* pattern is flaky: too short → race; too long → slow tests.
|
|
158
|
+
*
|
|
159
|
+
* @param condition Predicate. Returns truthy when done. May be async.
|
|
160
|
+
* @param opts.timeoutMs Max total wait. Defaults to 2000ms.
|
|
161
|
+
* @param opts.intervalMs Poll interval. Defaults to 10ms.
|
|
162
|
+
* @param opts.description Included in the timeout error for debuggability.
|
|
163
|
+
*/
|
|
164
|
+
export async function waitForCondition<T>(
|
|
165
|
+
condition: () => T | Promise<T>,
|
|
166
|
+
opts: { timeoutMs?: number; intervalMs?: number; description?: string } = {},
|
|
167
|
+
): Promise<T> {
|
|
168
|
+
const timeoutMs = opts.timeoutMs ?? 2000;
|
|
169
|
+
const intervalMs = opts.intervalMs ?? 10;
|
|
170
|
+
const deadline = Date.now() + timeoutMs;
|
|
171
|
+
let lastErr: unknown;
|
|
172
|
+
while (Date.now() < deadline) {
|
|
173
|
+
try {
|
|
174
|
+
const result = await condition();
|
|
175
|
+
if (result) return result;
|
|
176
|
+
} catch (e) {
|
|
177
|
+
lastErr = e;
|
|
178
|
+
}
|
|
179
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
180
|
+
}
|
|
181
|
+
const desc = opts.description ?? "condition";
|
|
182
|
+
const errSuffix = lastErr instanceof Error ? ` (last error: ${lastErr.message})` : "";
|
|
183
|
+
throw new Error(`waitForCondition timed out after ${timeoutMs}ms waiting for ${desc}${errSuffix}`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Find the first line in rendered output that matches a predicate. Returns
|
|
188
|
+
* the line's index and text, or throws if no line matches.
|
|
189
|
+
*
|
|
190
|
+
* Use this instead of `lines[N]` indexing when the N is positional and
|
|
191
|
+
* could shift under formatting changes.
|
|
192
|
+
*/
|
|
193
|
+
export function findLine(
|
|
194
|
+
output: string,
|
|
195
|
+
predicate: RegExp | ((line: string) => boolean),
|
|
196
|
+
): { index: number; text: string } {
|
|
197
|
+
const lines = output.split("\n");
|
|
198
|
+
const fn = predicate instanceof RegExp
|
|
199
|
+
? (l: string) => {
|
|
200
|
+
// RegExp.test is stateful when the pattern has /g or /y flags
|
|
201
|
+
// (maintains lastIndex across calls). Reset before each test so
|
|
202
|
+
// matches on different lines don't silently skip.
|
|
203
|
+
if (predicate.global || predicate.sticky) predicate.lastIndex = 0;
|
|
204
|
+
return predicate.test(l);
|
|
205
|
+
}
|
|
206
|
+
: predicate;
|
|
207
|
+
for (let i = 0; i < lines.length; i++) {
|
|
208
|
+
if (fn(lines[i]!)) return { index: i, text: lines[i]! };
|
|
209
|
+
}
|
|
210
|
+
const preview = lines.slice(0, 10).join("\n");
|
|
211
|
+
throw new Error(
|
|
212
|
+
`findLine: no line matched. First 10 lines were:\n${preview}`,
|
|
213
|
+
);
|
|
214
|
+
}
|
|
@@ -14,6 +14,7 @@ import assert from "node:assert/strict";
|
|
|
14
14
|
import { readFileSync } from "node:fs";
|
|
15
15
|
import { join, dirname } from "node:path";
|
|
16
16
|
import { fileURLToPath } from "node:url";
|
|
17
|
+
import { extractSourceRegion } from "./test-helpers.ts";
|
|
17
18
|
|
|
18
19
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
19
20
|
|
|
@@ -127,7 +128,13 @@ test("profile: balanced profile skips research, reassess, and slice research (AD
|
|
|
127
128
|
|
|
128
129
|
test("profile: quality profile skips research, slice research, and reassess (ADR-003)", () => {
|
|
129
130
|
const qualityIdx = preferencesSrc.indexOf('case "quality":');
|
|
130
|
-
|
|
131
|
+
// preferencesSrc is concatenated from multiple modules — bound the region
|
|
132
|
+
// with the next case marker so the assertion stays tightly scoped.
|
|
133
|
+
const qualityBlock = extractSourceRegion(
|
|
134
|
+
preferencesSrc,
|
|
135
|
+
'case "quality":',
|
|
136
|
+
'case "burn-max":',
|
|
137
|
+
);
|
|
131
138
|
assert.ok(qualityBlock.includes("skip_research: true"), "quality should skip research");
|
|
132
139
|
assert.ok(qualityBlock.includes("skip_slice_research: true"), "quality should skip slice research");
|
|
133
140
|
assert.ok(qualityBlock.includes("skip_reassess: true"), "quality should skip reassess");
|
|
@@ -44,7 +44,19 @@ describe("#2883: tool invocation error tracking on AutoSession", () => {
|
|
|
44
44
|
|
|
45
45
|
// ─── isToolInvocationError classifier ────────────────────────────────────
|
|
46
46
|
|
|
47
|
-
import {
|
|
47
|
+
import {
|
|
48
|
+
isDeterministicPolicyError,
|
|
49
|
+
isToolInvocationError,
|
|
50
|
+
isQueuedUserMessageSkip,
|
|
51
|
+
} from "../auto-tool-tracking.ts";
|
|
52
|
+
import {
|
|
53
|
+
shouldBlockContextWrite,
|
|
54
|
+
shouldBlockPendingGateInSnapshot,
|
|
55
|
+
shouldBlockQueueExecutionInSnapshot,
|
|
56
|
+
resetWriteGateState,
|
|
57
|
+
type WriteGateSnapshot,
|
|
58
|
+
} from "../bootstrap/write-gate.ts";
|
|
59
|
+
import { BLOCKED_WRITE_ERROR } from "../write-intercept.ts";
|
|
48
60
|
|
|
49
61
|
describe("#2883: isToolInvocationError classification", () => {
|
|
50
62
|
test("detects JSON validation failure pattern", () => {
|
|
@@ -89,11 +101,59 @@ describe("#2883: isToolInvocationError classification", () => {
|
|
|
89
101
|
);
|
|
90
102
|
});
|
|
91
103
|
|
|
104
|
+
test("detects raw write-gate CONTEXT failures for non-GSD write tools", () => {
|
|
105
|
+
resetWriteGateState();
|
|
106
|
+
const result = shouldBlockContextWrite(
|
|
107
|
+
"write",
|
|
108
|
+
"/tmp/project/.gsd/milestones/M001/M001-CONTEXT.md",
|
|
109
|
+
"M001",
|
|
110
|
+
false,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
assert.equal(result.block, true);
|
|
114
|
+
assert.equal(isDeterministicPolicyError(result.reason ?? ""), true);
|
|
115
|
+
assert.equal(isToolInvocationError(result.reason ?? ""), true);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("detects raw pending-gate failures for non-GSD write tools", () => {
|
|
119
|
+
const snapshot: WriteGateSnapshot = {
|
|
120
|
+
verifiedDepthMilestones: [],
|
|
121
|
+
activeQueuePhase: false,
|
|
122
|
+
pendingGateId: "depth_verification_M001",
|
|
123
|
+
};
|
|
124
|
+
const result = shouldBlockPendingGateInSnapshot(snapshot, "write", "M001", false);
|
|
125
|
+
|
|
126
|
+
assert.equal(result.block, true);
|
|
127
|
+
assert.equal(isDeterministicPolicyError(result.reason ?? ""), true);
|
|
128
|
+
assert.equal(isToolInvocationError(result.reason ?? ""), true);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("detects queue-mode policy blocks for raw write/edit/bash tools", () => {
|
|
132
|
+
const snapshot: WriteGateSnapshot = {
|
|
133
|
+
verifiedDepthMilestones: [],
|
|
134
|
+
activeQueuePhase: true,
|
|
135
|
+
pendingGateId: null,
|
|
136
|
+
};
|
|
137
|
+
const writeResult = shouldBlockQueueExecutionInSnapshot(snapshot, "write", "src/app.ts", true);
|
|
138
|
+
const bashResult = shouldBlockQueueExecutionInSnapshot(snapshot, "bash", "npm run build", true);
|
|
139
|
+
|
|
140
|
+
assert.equal(writeResult.block, true);
|
|
141
|
+
assert.equal(bashResult.block, true);
|
|
142
|
+
assert.equal(isDeterministicPolicyError(writeResult.reason ?? ""), true);
|
|
143
|
+
assert.equal(isToolInvocationError(bashResult.reason ?? ""), true);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("detects direct STATE.md/gsd.db write-intercept policy blocks", () => {
|
|
147
|
+
assert.equal(isDeterministicPolicyError(BLOCKED_WRITE_ERROR), true);
|
|
148
|
+
assert.equal(isToolInvocationError(BLOCKED_WRITE_ERROR), true);
|
|
149
|
+
});
|
|
150
|
+
|
|
92
151
|
test("returns false for normal tool errors (business logic)", () => {
|
|
93
152
|
assert.equal(
|
|
94
153
|
isToolInvocationError("Slice S01 is already complete"),
|
|
95
154
|
false,
|
|
96
155
|
);
|
|
156
|
+
assert.equal(isDeterministicPolicyError("Slice S01 is already complete"), false);
|
|
97
157
|
});
|
|
98
158
|
|
|
99
159
|
test("returns false for empty string", () => {
|
|
@@ -36,6 +36,9 @@ const RENAME_MAP: Array<{ canonical: string; alias: string }> = [
|
|
|
36
36
|
{ canonical: "gsd_reassess_roadmap", alias: "gsd_roadmap_reassess" },
|
|
37
37
|
{ canonical: "gsd_complete_milestone", alias: "gsd_milestone_complete" },
|
|
38
38
|
{ canonical: "gsd_validate_milestone", alias: "gsd_milestone_validate" },
|
|
39
|
+
{ canonical: "gsd_task_reopen", alias: "gsd_reopen_task" },
|
|
40
|
+
{ canonical: "gsd_slice_reopen", alias: "gsd_reopen_slice" },
|
|
41
|
+
{ canonical: "gsd_milestone_reopen", alias: "gsd_reopen_milestone" },
|
|
39
42
|
];
|
|
40
43
|
|
|
41
44
|
// ─── Registration count ──────────────────────────────────────────────────────
|
|
@@ -45,7 +48,11 @@ console.log('\n── Tool naming: registration count ──');
|
|
|
45
48
|
const pi = makeMockPi();
|
|
46
49
|
registerDbTools(pi);
|
|
47
50
|
|
|
48
|
-
assert.deepStrictEqual(
|
|
51
|
+
assert.deepStrictEqual(
|
|
52
|
+
pi.tools.length,
|
|
53
|
+
RENAME_MAP.length * 2 + 2,
|
|
54
|
+
'Should register canonical/alias tool pairs plus 1 gate tool and 1 gsd_skip_slice',
|
|
55
|
+
);
|
|
49
56
|
|
|
50
57
|
// ─── Both names exist for each pair ──────────────────────────────────────────
|
|
51
58
|
|