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
package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
// GSD-2 + Regression tests for deterministic policy error classification (#4973)
|
|
2
|
+
//
|
|
3
|
+
// When gsd_summary_save returns context_write_blocked (a deterministic write-gate
|
|
4
|
+
// rejection), the retry controller must NOT re-dispatch with escalating model tiers.
|
|
5
|
+
// Instead it must write a blocker placeholder and advance the pipeline immediately.
|
|
6
|
+
//
|
|
7
|
+
// Test 5 — deterministic error short-circuits retry:
|
|
8
|
+
// - isDeterministicPolicyError correctly classifies context_write_blocked errors
|
|
9
|
+
// - recordToolInvocationError captures deterministic errors in lastToolInvocationError
|
|
10
|
+
// - postUnitPreVerification returns "continue" (not "retry"), writes placeholder,
|
|
11
|
+
// leaves pendingVerificationRetry null — zero additional model calls dispatched
|
|
12
|
+
//
|
|
13
|
+
// Test 6 — model-quality failures still use standard retry path:
|
|
14
|
+
// - non-deterministic failures set pendingVerificationRetry and return "retry"
|
|
15
|
+
// - tier escalates on retry 1 (previousTier "standard" → "heavy")
|
|
16
|
+
// - tier is RETAINED at "heavy" on subsequent retries (no downgrade back to fresh
|
|
17
|
+
// classification when already at max tier) — "escalate once" semantics
|
|
18
|
+
|
|
19
|
+
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
20
|
+
import assert from "node:assert/strict";
|
|
21
|
+
import { mkdtempSync, mkdirSync, existsSync, rmSync } from "node:fs";
|
|
22
|
+
import { join } from "node:path";
|
|
23
|
+
import { tmpdir } from "node:os";
|
|
24
|
+
import { randomUUID } from "node:crypto";
|
|
25
|
+
|
|
26
|
+
import {
|
|
27
|
+
isDeterministicPolicyError,
|
|
28
|
+
DETERMINISTIC_POLICY_ERROR_STRINGS,
|
|
29
|
+
} from "../auto-tool-tracking.ts";
|
|
30
|
+
import { AutoSession } from "../auto/session.ts";
|
|
31
|
+
import { _setAutoActiveForTest } from "../auto.ts";
|
|
32
|
+
import { escalateTier } from "../model-router.ts";
|
|
33
|
+
|
|
34
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
const tmpDirs: string[] = [];
|
|
37
|
+
|
|
38
|
+
function makeTmpBase(): string {
|
|
39
|
+
const base = mkdtempSync(join(tmpdir(), `gsd-test-4973-${randomUUID().slice(0, 8)}-`));
|
|
40
|
+
tmpDirs.push(base);
|
|
41
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
|
|
42
|
+
return base;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function resetAutoState(): void {
|
|
46
|
+
_setAutoActiveForTest(false);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ─── Test 5: Deterministic error short-circuits retry ─────────────────────
|
|
50
|
+
|
|
51
|
+
describe("Test 5 — isDeterministicPolicyError classifier (#4973)", () => {
|
|
52
|
+
// ── Classifier unit tests ──────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
test("classifies context_write_blocked fallback text as deterministic", () => {
|
|
55
|
+
// This is the text emitted by workflow-tool-executors.ts when contextGuard.reason
|
|
56
|
+
// is undefined: `Error saving artifact: ${contextGuard.reason ?? "context write blocked"}`
|
|
57
|
+
const errorText = "gsd_summary_save: Error saving artifact: context write blocked";
|
|
58
|
+
assert.strictEqual(
|
|
59
|
+
isDeterministicPolicyError(errorText),
|
|
60
|
+
true,
|
|
61
|
+
"fallback context_write_blocked text must be classified as deterministic",
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("classifies write-gate verbose reason as deterministic", () => {
|
|
66
|
+
// This is the text when shouldBlockContextArtifactSaveInSnapshot returns its reason:
|
|
67
|
+
// "HARD BLOCK: Cannot save milestone CONTEXT without depth verification for M001. ..."
|
|
68
|
+
const verboseError = [
|
|
69
|
+
"gsd_summary_save: Error saving artifact:",
|
|
70
|
+
"HARD BLOCK: Cannot save milestone CONTEXT without depth verification for M001.",
|
|
71
|
+
"This is a mechanical gate — you MUST NOT proceed, retry, or rationalize past this block.",
|
|
72
|
+
].join(" ");
|
|
73
|
+
assert.strictEqual(
|
|
74
|
+
isDeterministicPolicyError(verboseError),
|
|
75
|
+
true,
|
|
76
|
+
"verbose write-gate reason containing 'CONTEXT without depth verification' must be classified as deterministic",
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("returns false for malformed-JSON errors (separate classification path)", () => {
|
|
81
|
+
assert.strictEqual(
|
|
82
|
+
isDeterministicPolicyError("Unexpected end of JSON input"),
|
|
83
|
+
false,
|
|
84
|
+
"malformed-JSON errors are not deterministic policy errors",
|
|
85
|
+
);
|
|
86
|
+
assert.strictEqual(
|
|
87
|
+
isDeterministicPolicyError("Validation failed for tool gsd_complete_slice"),
|
|
88
|
+
false,
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("returns false for normal business-logic tool errors", () => {
|
|
93
|
+
assert.strictEqual(
|
|
94
|
+
isDeterministicPolicyError("Slice S01 is already complete"),
|
|
95
|
+
false,
|
|
96
|
+
);
|
|
97
|
+
assert.strictEqual(
|
|
98
|
+
isDeterministicPolicyError("Error saving artifact: db_unavailable"),
|
|
99
|
+
false,
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("returns false for empty string", () => {
|
|
104
|
+
assert.strictEqual(isDeterministicPolicyError(""), false);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("DETERMINISTIC_POLICY_ERROR_STRINGS list is non-empty and contains context_write_blocked entry", () => {
|
|
108
|
+
assert.ok(
|
|
109
|
+
DETERMINISTIC_POLICY_ERROR_STRINGS.length > 0,
|
|
110
|
+
"must have at least one known deterministic error string",
|
|
111
|
+
);
|
|
112
|
+
const hasContextWriteBlocked = DETERMINISTIC_POLICY_ERROR_STRINGS.some(
|
|
113
|
+
(s) => s.includes("context write blocked") || s.includes("CONTEXT without depth verification"),
|
|
114
|
+
);
|
|
115
|
+
assert.ok(hasContextWriteBlocked, "must include context_write_blocked family entries");
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe("Test 5 — recordToolInvocationError captures deterministic errors (#4973)", () => {
|
|
120
|
+
beforeEach(resetAutoState);
|
|
121
|
+
afterEach(resetAutoState);
|
|
122
|
+
|
|
123
|
+
test("lastToolInvocationError is NOT set for deterministic errors on current main (pre-fix baseline)", () => {
|
|
124
|
+
// This test documents the FIXED behavior: deterministic errors ARE captured.
|
|
125
|
+
// On current main (before this fix), recordToolInvocationError would NOT store
|
|
126
|
+
// context_write_blocked because it only checked isToolInvocationError and
|
|
127
|
+
// isQueuedUserMessageSkip. After the fix, it also checks isDeterministicPolicyError.
|
|
128
|
+
//
|
|
129
|
+
// We test the fixed behavior here: the error IS captured.
|
|
130
|
+
_setAutoActiveForTest(true);
|
|
131
|
+
|
|
132
|
+
// Import recordToolInvocationError from auto.ts (it delegates to auto-tool-tracking.ts)
|
|
133
|
+
// We test indirectly via the session state: after calling recordToolInvocationError,
|
|
134
|
+
// lastToolInvocationError should be set for deterministic errors.
|
|
135
|
+
//
|
|
136
|
+
// Since recordToolInvocationError is not exported directly, we verify the fix
|
|
137
|
+
// through the AutoSession field behavior documented in the classifier tests above.
|
|
138
|
+
// The recordToolInvocationError integration is exercised in the postUnitPreVerification
|
|
139
|
+
// integration test below.
|
|
140
|
+
const s = new AutoSession();
|
|
141
|
+
assert.strictEqual(s.lastToolInvocationError, null, "starts null");
|
|
142
|
+
|
|
143
|
+
// Simulate what postUnitPreVerification checks: if isDeterministicPolicyError
|
|
144
|
+
// matches on lastToolInvocationError, the short-circuit fires.
|
|
145
|
+
// The value is set by recordToolInvocationError (tested via auto.ts integration).
|
|
146
|
+
s.lastToolInvocationError = "gsd_summary_save: Error saving artifact: context write blocked";
|
|
147
|
+
assert.ok(
|
|
148
|
+
isDeterministicPolicyError(s.lastToolInvocationError),
|
|
149
|
+
"classifier recognises the stored error — short-circuit will fire",
|
|
150
|
+
);
|
|
151
|
+
assert.strictEqual(s.pendingVerificationRetry, null, "pendingVerificationRetry starts null");
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("AutoSession.lastToolInvocationError can hold a deterministic policy error string", () => {
|
|
155
|
+
const s = new AutoSession();
|
|
156
|
+
s.lastToolInvocationError = "gsd_summary_save: Error saving artifact: context write blocked";
|
|
157
|
+
assert.ok(s.lastToolInvocationError);
|
|
158
|
+
assert.ok(isDeterministicPolicyError(s.lastToolInvocationError));
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("AutoSession.lastToolInvocationError is cleared on reset()", () => {
|
|
162
|
+
const s = new AutoSession();
|
|
163
|
+
s.lastToolInvocationError = "gsd_summary_save: Error saving artifact: context write blocked";
|
|
164
|
+
s.reset();
|
|
165
|
+
assert.strictEqual(s.lastToolInvocationError, null);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe("Test 5 — postUnitPreVerification short-circuits on deterministic error (#4973)", () => {
|
|
170
|
+
// This integration test calls postUnitPreVerification with a deterministic error
|
|
171
|
+
// in lastToolInvocationError and asserts that:
|
|
172
|
+
// 1. pendingVerificationRetry is NOT set (no retry dispatched)
|
|
173
|
+
// 2. the blocker placeholder is written to disk
|
|
174
|
+
// 3. the function returns "continue" (not "retry" or "dispatched")
|
|
175
|
+
|
|
176
|
+
let base = "";
|
|
177
|
+
beforeEach(() => {
|
|
178
|
+
base = makeTmpBase();
|
|
179
|
+
_setAutoActiveForTest(true);
|
|
180
|
+
});
|
|
181
|
+
afterEach(() => {
|
|
182
|
+
_setAutoActiveForTest(false);
|
|
183
|
+
// Cleanup is handled by tmpDirs at process exit; individual cleanup here
|
|
184
|
+
// is best-effort only so as not to mask assertion failures.
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("returns 'continue' and writes placeholder for context_write_blocked — no pendingVerificationRetry set", async () => {
|
|
188
|
+
const { postUnitPreVerification } = await import("../auto-post-unit.ts");
|
|
189
|
+
|
|
190
|
+
const s = new AutoSession();
|
|
191
|
+
s.active = true;
|
|
192
|
+
s.basePath = base;
|
|
193
|
+
s.currentUnit = { type: "discuss-milestone", id: "M001", startedAt: Date.now() };
|
|
194
|
+
// Set the deterministic error that would be recorded by recordToolInvocationError
|
|
195
|
+
s.lastToolInvocationError = "gsd_summary_save: Error saving artifact: context write blocked";
|
|
196
|
+
s.verificationRetryCount.set("discuss-milestone:M001", 2);
|
|
197
|
+
|
|
198
|
+
let pauseCalled = false;
|
|
199
|
+
const ctx = {
|
|
200
|
+
ui: { notify: () => {} },
|
|
201
|
+
} as any;
|
|
202
|
+
const pi = {} as any;
|
|
203
|
+
|
|
204
|
+
const pctx = {
|
|
205
|
+
s,
|
|
206
|
+
ctx,
|
|
207
|
+
pi,
|
|
208
|
+
buildSnapshotOpts: () => ({}) as any,
|
|
209
|
+
lockBase: () => base,
|
|
210
|
+
stopAuto: async () => {},
|
|
211
|
+
pauseAuto: async () => { pauseCalled = true; },
|
|
212
|
+
updateProgressWidget: () => {},
|
|
213
|
+
} as any;
|
|
214
|
+
|
|
215
|
+
const result = await postUnitPreVerification(pctx, { skipSettleDelay: true });
|
|
216
|
+
|
|
217
|
+
// Core assertion: deterministic error short-circuits — returns "continue",
|
|
218
|
+
// no retry, and the placeholder is written so the pipeline can advance.
|
|
219
|
+
assert.strictEqual(result, "continue", "must return 'continue', not 'retry' or 'dispatched'");
|
|
220
|
+
assert.strictEqual(s.pendingVerificationRetry, null, "pendingVerificationRetry must NOT be set");
|
|
221
|
+
assert.strictEqual(s.verificationRetryCount.has("discuss-milestone:M001"), false, "deterministic short-circuit clears stale retry count");
|
|
222
|
+
assert.strictEqual(s.lastToolInvocationError, null, "lastToolInvocationError cleared after handling");
|
|
223
|
+
assert.strictEqual(pauseCalled, false, "pauseAuto must NOT be called for deterministic errors");
|
|
224
|
+
|
|
225
|
+
// The blocker placeholder must exist on disk so the pipeline can advance.
|
|
226
|
+
const placeholderPath = join(base, ".gsd", "milestones", "M001", "M001-CONTEXT.md");
|
|
227
|
+
assert.ok(
|
|
228
|
+
existsSync(placeholderPath),
|
|
229
|
+
`blocker placeholder must be written at ${placeholderPath}`,
|
|
230
|
+
);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// ─── Test 6: Model-quality failures use standard retry path ──────────────────
|
|
235
|
+
|
|
236
|
+
describe("Test 6 — non-deterministic failures use standard retry; tier escalates once (#4973)", () => {
|
|
237
|
+
// ── escalateTier behavior (existing, unchanged) ───────────────────────────
|
|
238
|
+
|
|
239
|
+
test("escalateTier: light → standard → heavy → null (max)", () => {
|
|
240
|
+
assert.strictEqual(escalateTier("light"), "standard");
|
|
241
|
+
assert.strictEqual(escalateTier("standard"), "heavy");
|
|
242
|
+
assert.strictEqual(escalateTier("heavy"), null, "heavy is the max tier — no further escalation");
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test("standard-start retry: escalates to heavy on retry 1, stays at heavy on retry 2 (escalateTier returns null)", () => {
|
|
246
|
+
// Simulate what selectAndApplyModel does across two retries for a standard-start unit.
|
|
247
|
+
// Retry 1: previousTier = "standard", escalateTier → "heavy". Applied tier = "heavy".
|
|
248
|
+
const tier1 = escalateTier("standard");
|
|
249
|
+
assert.strictEqual(tier1, "heavy", "retry 1: standard escalates to heavy");
|
|
250
|
+
|
|
251
|
+
// Retry 2: previousTier = "heavy" (from retry 1 result), escalateTier → null.
|
|
252
|
+
// The "retain escalated tier" fix kicks in: prevOrder(heavy=2) > freshOrder(standard=1),
|
|
253
|
+
// so the tier stays at "heavy" rather than reverting to fresh classification.
|
|
254
|
+
const tier2 = escalateTier("heavy");
|
|
255
|
+
assert.strictEqual(tier2, null, "retry 2: heavy cannot escalate further");
|
|
256
|
+
|
|
257
|
+
// Verify the tier-order comparison used in selectAndApplyModel (#4973 fix):
|
|
258
|
+
const tierOrder: Record<string, number> = { light: 0, standard: 1, heavy: 2 };
|
|
259
|
+
const prevOrder = tierOrder["heavy"] ?? 0; // 2 (from retry 1 result)
|
|
260
|
+
const freshOrder = tierOrder["standard"] ?? 0; // 1 (fresh classifyUnitComplexity for a standard unit)
|
|
261
|
+
assert.ok(
|
|
262
|
+
prevOrder > freshOrder,
|
|
263
|
+
"prevOrder(heavy=2) > freshOrder(standard=1) — the fix retains 'heavy' and prevents revert",
|
|
264
|
+
);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test("light-start retry 3: escalated tier is retained, not reverted to 'light'", () => {
|
|
268
|
+
// Without the fix: retry 3 would see previousTier="heavy" (from retry 2),
|
|
269
|
+
// escalateTier returns null, and fresh classification is "light" — the model
|
|
270
|
+
// reverts to a cheap light-tier model. With the fix, we retain "heavy".
|
|
271
|
+
|
|
272
|
+
// Retry 1: light → standard
|
|
273
|
+
assert.strictEqual(escalateTier("light"), "standard");
|
|
274
|
+
// Retry 2: standard → heavy
|
|
275
|
+
assert.strictEqual(escalateTier("standard"), "heavy");
|
|
276
|
+
// Retry 3: heavy → null (can't escalate), fix retains "heavy" instead of reverting to "light"
|
|
277
|
+
assert.strictEqual(escalateTier("heavy"), null);
|
|
278
|
+
|
|
279
|
+
// The fix logic: when escalateTier returns null, compare prevOrder vs freshOrder.
|
|
280
|
+
const tierOrder: Record<string, number> = { light: 0, standard: 1, heavy: 2 };
|
|
281
|
+
const prevOrderRetry3 = tierOrder["heavy"] ?? 0; // 2
|
|
282
|
+
const freshOrderLight = tierOrder["light"] ?? 0; // 0
|
|
283
|
+
assert.ok(
|
|
284
|
+
prevOrderRetry3 > freshOrderLight,
|
|
285
|
+
"on retry 3, prevOrder(heavy=2) > freshOrder(light=0) — 'heavy' must be retained, not reverted",
|
|
286
|
+
);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
test("non-deterministic error: session sets pendingVerificationRetry (standard retry path)", () => {
|
|
290
|
+
// Simulate what postUnitPreVerification does for a non-deterministic failure:
|
|
291
|
+
// no lastToolInvocationError → falls into the standard retry path → sets pendingVerificationRetry.
|
|
292
|
+
const s = new AutoSession();
|
|
293
|
+
s.currentUnit = { type: "plan-slice", id: "M001:S01", startedAt: Date.now() };
|
|
294
|
+
|
|
295
|
+
// Simulate the retry count increment (as postUnitPreVerification does internally)
|
|
296
|
+
const retryKey = `${s.currentUnit.type}:${s.currentUnit.id}`;
|
|
297
|
+
const attempt = (s.verificationRetryCount.get(retryKey) ?? 0) + 1;
|
|
298
|
+
s.verificationRetryCount.set(retryKey, attempt);
|
|
299
|
+
|
|
300
|
+
// Simulate setting pendingVerificationRetry (what the "else" branch does)
|
|
301
|
+
s.pendingVerificationRetry = {
|
|
302
|
+
unitId: s.currentUnit.id,
|
|
303
|
+
failureContext: `Artifact verification failed: expected artifact for ${s.currentUnit.type} "${s.currentUnit.id}" was not found on disk after unit execution (attempt ${attempt}).`,
|
|
304
|
+
attempt,
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
assert.ok(s.pendingVerificationRetry !== null, "standard retry path sets pendingVerificationRetry");
|
|
308
|
+
assert.strictEqual(s.pendingVerificationRetry.attempt, 1, "attempt is 1");
|
|
309
|
+
assert.ok(
|
|
310
|
+
s.pendingVerificationRetry.failureContext.includes("plan-slice"),
|
|
311
|
+
"failureContext references the unit type",
|
|
312
|
+
);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
test("isDeterministicPolicyError returns false for non-deterministic verification failure", () => {
|
|
316
|
+
// A plain 'artifact not found' is NOT a deterministic policy error.
|
|
317
|
+
// The standard retry path must still fire for these.
|
|
318
|
+
assert.strictEqual(
|
|
319
|
+
isDeterministicPolicyError(""),
|
|
320
|
+
false,
|
|
321
|
+
"empty error (no tool error) is not deterministic",
|
|
322
|
+
);
|
|
323
|
+
assert.strictEqual(
|
|
324
|
+
isDeterministicPolicyError("Artifact not found on disk"),
|
|
325
|
+
false,
|
|
326
|
+
"plain artifact-missing message is not a deterministic policy error",
|
|
327
|
+
);
|
|
328
|
+
assert.strictEqual(
|
|
329
|
+
isDeterministicPolicyError("existsSync returned false"),
|
|
330
|
+
false,
|
|
331
|
+
);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
// Cleanup all temp dirs after the test suite completes
|
|
337
|
+
process.on("exit", () => {
|
|
338
|
+
for (const dir of tmpDirs) {
|
|
339
|
+
try { rmSync(dir, { recursive: true, force: true }); } catch { /* ignore */ }
|
|
340
|
+
}
|
|
341
|
+
});
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
// GSD-2 + Regression tests for auto-mode discuss-milestone write-gate deadlock (#4973)
|
|
2
|
+
//
|
|
3
|
+
// The depth-verification write-gate in write-gate.ts:415-443 blocks
|
|
4
|
+
// gsd_summary_save({artifact_type:"CONTEXT"}) until markDepthVerified() is
|
|
5
|
+
// called. In interactive mode this happens when the user picks the confirmation
|
|
6
|
+
// option in ask_user_questions. In auto-mode there is no human — the gate
|
|
7
|
+
// deadlocked every discuss-milestone unit, wasting 200K-360K tokens per run.
|
|
8
|
+
//
|
|
9
|
+
// Fix: each dispatch rule that fires a discuss-milestone unit now calls
|
|
10
|
+
// markDepthVerified(mid) when isAutoActive() is true, before returning the
|
|
11
|
+
// dispatch action. These tests verify:
|
|
12
|
+
// Test 1 — CONTEXT artifact save unblocks after markDepthVerified
|
|
13
|
+
// Test 2 — raw write to *-CONTEXT.md unblocks after markDepthVerified
|
|
14
|
+
// Test 3 — session_switch ordering: clearDiscussionFlowState clears the mark
|
|
15
|
+
// Test 4 — interactive sessions (isAutoActive===false) are unaffected
|
|
16
|
+
|
|
17
|
+
import { describe, test, afterEach, beforeEach } from 'node:test';
|
|
18
|
+
import assert from 'node:assert/strict';
|
|
19
|
+
import { mkdtempSync, rmSync, existsSync, unlinkSync } from 'node:fs';
|
|
20
|
+
import { tmpdir } from 'node:os';
|
|
21
|
+
import { join } from 'node:path';
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
markDepthVerified,
|
|
25
|
+
clearDiscussionFlowState,
|
|
26
|
+
shouldBlockContextArtifactSaveInSnapshot,
|
|
27
|
+
shouldBlockContextWrite,
|
|
28
|
+
loadWriteGateSnapshot,
|
|
29
|
+
isMilestoneDepthVerifiedInSnapshot,
|
|
30
|
+
} from '../bootstrap/write-gate.ts';
|
|
31
|
+
|
|
32
|
+
import { DISPATCH_RULES, type DispatchContext } from '../auto-dispatch.ts';
|
|
33
|
+
import { _setAutoActiveForTest } from '../auto.ts';
|
|
34
|
+
|
|
35
|
+
// Reset all relevant state before and after each test.
|
|
36
|
+
function resetState(): void {
|
|
37
|
+
_setAutoActiveForTest(false);
|
|
38
|
+
clearDiscussionFlowState();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
describe('auto-discuss-milestone-deadlock-4973', () => {
|
|
42
|
+
beforeEach(resetState);
|
|
43
|
+
afterEach(resetState);
|
|
44
|
+
|
|
45
|
+
// ── Test 1 ──────────────────────────────────────────────────────────────
|
|
46
|
+
// CONTEXT artifact save via gsd_summary_save is blocked before the mark
|
|
47
|
+
// and unblocked after it. This is the exact path that deadlocked in #4973:
|
|
48
|
+
// workflow-tool-executors.ts calls shouldBlockContextArtifactSaveInSnapshot
|
|
49
|
+
// against a snapshot that had no verified milestones.
|
|
50
|
+
test('Test 1: CONTEXT artifact save unblocks after markDepthVerified (auto-mode)', () => {
|
|
51
|
+
_setAutoActiveForTest(true);
|
|
52
|
+
|
|
53
|
+
// Before mark: blocked
|
|
54
|
+
const snapshotBefore = loadWriteGateSnapshot();
|
|
55
|
+
const beforeResult = shouldBlockContextArtifactSaveInSnapshot(
|
|
56
|
+
snapshotBefore,
|
|
57
|
+
'CONTEXT',
|
|
58
|
+
'M001',
|
|
59
|
+
null,
|
|
60
|
+
);
|
|
61
|
+
assert.strictEqual(beforeResult.block, true, 'should block before markDepthVerified');
|
|
62
|
+
|
|
63
|
+
// Simulate what the dispatch rule now does in auto-mode
|
|
64
|
+
markDepthVerified('M001');
|
|
65
|
+
|
|
66
|
+
// After mark: unblocked
|
|
67
|
+
const snapshotAfter = loadWriteGateSnapshot();
|
|
68
|
+
const afterResult = shouldBlockContextArtifactSaveInSnapshot(
|
|
69
|
+
snapshotAfter,
|
|
70
|
+
'CONTEXT',
|
|
71
|
+
'M001',
|
|
72
|
+
null,
|
|
73
|
+
);
|
|
74
|
+
assert.strictEqual(afterResult.block, false, 'should not block after markDepthVerified');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// ── Test 2 ──────────────────────────────────────────────────────────────
|
|
78
|
+
// Raw write tool to a *-CONTEXT.md path is also gated. The register-hooks
|
|
79
|
+
// tool_call handler calls shouldBlockContextWrite for write events.
|
|
80
|
+
test('Test 2: raw write to M001-CONTEXT.md unblocks after markDepthVerified (auto-mode)', () => {
|
|
81
|
+
_setAutoActiveForTest(true);
|
|
82
|
+
|
|
83
|
+
const contextPath = '.gsd/milestones/M001/M001-CONTEXT.md';
|
|
84
|
+
|
|
85
|
+
// Before mark: blocked
|
|
86
|
+
const beforeResult = shouldBlockContextWrite('write', contextPath, 'M001');
|
|
87
|
+
assert.strictEqual(beforeResult.block, true, 'write should be blocked before markDepthVerified');
|
|
88
|
+
|
|
89
|
+
// Simulate dispatch rule auto-mark
|
|
90
|
+
markDepthVerified('M001');
|
|
91
|
+
|
|
92
|
+
// After mark: unblocked
|
|
93
|
+
const afterResult = shouldBlockContextWrite('write', contextPath, 'M001');
|
|
94
|
+
assert.strictEqual(afterResult.block, false, 'write should not be blocked after markDepthVerified');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// ── Test 3 ──────────────────────────────────────────────────────────────
|
|
98
|
+
// Documents the session_switch ordering contract.
|
|
99
|
+
//
|
|
100
|
+
// When auto-mode dispatches a new session, the event sequence is:
|
|
101
|
+
// session_switch → clearDiscussionFlowState() (register-hooks.ts:106)
|
|
102
|
+
// before_agent_start fires
|
|
103
|
+
// resolveDispatch is called → discuss-milestone rule match fn runs
|
|
104
|
+
// markDepthVerified(mid) is called HERE (after the clear)
|
|
105
|
+
//
|
|
106
|
+
// This test demonstrates that clearDiscussionFlowState() (the session_switch
|
|
107
|
+
// side effect) clears any previously set mark, and that calling
|
|
108
|
+
// markDepthVerified after the clear correctly re-establishes it — proving
|
|
109
|
+
// the dispatch-site call site is safe regardless of prior session state.
|
|
110
|
+
test('Test 3: session_switch ordering — clearDiscussionFlowState clears mark; dispatch-site call re-establishes it', () => {
|
|
111
|
+
// Simulate a mark from a prior session
|
|
112
|
+
markDepthVerified('M001');
|
|
113
|
+
let snapshot = loadWriteGateSnapshot();
|
|
114
|
+
assert.strictEqual(
|
|
115
|
+
isMilestoneDepthVerifiedInSnapshot(snapshot, 'M001'),
|
|
116
|
+
true,
|
|
117
|
+
'precondition: mark set from prior session',
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// session_switch fires clearDiscussionFlowState() — this is exactly what
|
|
121
|
+
// register-hooks.ts:106 does
|
|
122
|
+
clearDiscussionFlowState();
|
|
123
|
+
snapshot = loadWriteGateSnapshot();
|
|
124
|
+
assert.strictEqual(
|
|
125
|
+
isMilestoneDepthVerifiedInSnapshot(snapshot, 'M001'),
|
|
126
|
+
false,
|
|
127
|
+
'session_switch (clearDiscussionFlowState) must clear the mark',
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
// Now the dispatch rule fires (after session_switch cleared state)
|
|
131
|
+
// and re-establishes the mark for the new session
|
|
132
|
+
_setAutoActiveForTest(true);
|
|
133
|
+
markDepthVerified('M001'); // this is what the dispatch rule does
|
|
134
|
+
|
|
135
|
+
snapshot = loadWriteGateSnapshot();
|
|
136
|
+
assert.strictEqual(
|
|
137
|
+
isMilestoneDepthVerifiedInSnapshot(snapshot, 'M001'),
|
|
138
|
+
true,
|
|
139
|
+
'dispatch-site markDepthVerified re-establishes the mark after session_switch cleared it',
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
// And the artifact save is now unblocked for this session
|
|
143
|
+
const result = shouldBlockContextArtifactSaveInSnapshot(snapshot, 'CONTEXT', 'M001', null);
|
|
144
|
+
assert.strictEqual(result.block, false, 'CONTEXT save unblocked in the new session');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// ── Test 4 ──────────────────────────────────────────────────────────────
|
|
148
|
+
// Interactive sessions (isAutoActive()===false) must NOT be auto-marked.
|
|
149
|
+
// The dispatch rules guard the markDepthVerified call with isAutoActive(),
|
|
150
|
+
// so in a non-auto session the gate still requires the human to confirm.
|
|
151
|
+
// This test passes on both current main AND with the fix applied.
|
|
152
|
+
test('Test 4: interactive sessions unaffected — gate still blocks unverified milestones when auto is off', () => {
|
|
153
|
+
_setAutoActiveForTest(false);
|
|
154
|
+
|
|
155
|
+
// Do NOT call markDepthVerified — simulating that dispatch rule's
|
|
156
|
+
// isAutoActive() guard prevented the auto-mark (as it should for
|
|
157
|
+
// interactive sessions)
|
|
158
|
+
|
|
159
|
+
// CONTEXT artifact save is still blocked
|
|
160
|
+
const snapshotResult = shouldBlockContextArtifactSaveInSnapshot(
|
|
161
|
+
loadWriteGateSnapshot(),
|
|
162
|
+
'CONTEXT',
|
|
163
|
+
'M002',
|
|
164
|
+
null,
|
|
165
|
+
);
|
|
166
|
+
assert.strictEqual(
|
|
167
|
+
snapshotResult.block,
|
|
168
|
+
true,
|
|
169
|
+
'CONTEXT save must still be blocked in interactive mode without depth verification',
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
// Raw write to CONTEXT.md is still blocked
|
|
173
|
+
const writeResult = shouldBlockContextWrite(
|
|
174
|
+
'write',
|
|
175
|
+
'.gsd/milestones/M002/M002-CONTEXT.md',
|
|
176
|
+
'M002',
|
|
177
|
+
);
|
|
178
|
+
assert.strictEqual(
|
|
179
|
+
writeResult.block,
|
|
180
|
+
true,
|
|
181
|
+
'write to CONTEXT.md must still be blocked in interactive mode',
|
|
182
|
+
);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// ── Test 5 ──────────────────────────────────────────────────────────────
|
|
186
|
+
// The actual fix lives inside the discuss-milestone dispatch rules at
|
|
187
|
+
// auto-dispatch.ts:280-291, :423-432, :449-458. This test invokes the
|
|
188
|
+
// "needs-discussion → discuss-milestone" rule directly and asserts that
|
|
189
|
+
// (a) the rule auto-marks depth-verified when isAutoActive() is true, and
|
|
190
|
+
// (b) it does NOT mark when isAutoActive() is false.
|
|
191
|
+
//
|
|
192
|
+
// This is the test codex flagged as missing: Tests 1-4 above only exercise
|
|
193
|
+
// the markDepthVerified primitive — they pass on origin/main. This Test 5
|
|
194
|
+
// FAILS on origin/main (the rule does nothing for the gate) and PASSES with
|
|
195
|
+
// the fix (the rule calls markDepthVerified inside isAutoActive()).
|
|
196
|
+
test('Test 5: needs-discussion dispatch rule auto-marks depth-verified in auto-mode', async () => {
|
|
197
|
+
const rule = DISPATCH_RULES.find(r => r.name === 'needs-discussion → discuss-milestone');
|
|
198
|
+
assert.ok(rule, 'dispatch rule must exist');
|
|
199
|
+
|
|
200
|
+
// Use a real temp directory so the snapshot file the rule writes is
|
|
201
|
+
// readable by the same loadWriteGateSnapshot(basePath) the test reads
|
|
202
|
+
// from. The rule passes basePath through to markDepthVerified (since
|
|
203
|
+
// commit 73bb7e085) — without this, the rule writes the snapshot under
|
|
204
|
+
// basePath but the test would read process.cwd() and never see it.
|
|
205
|
+
const tempBase = mkdtempSync(join(tmpdir(), '4973-rule-test-'));
|
|
206
|
+
const snapshotFile = join(tempBase, '.gsd', 'runtime', 'write-gate-state.json');
|
|
207
|
+
try {
|
|
208
|
+
const baseCtx = {
|
|
209
|
+
basePath: tempBase,
|
|
210
|
+
mid: 'M005',
|
|
211
|
+
midTitle: 'Test Milestone',
|
|
212
|
+
state: { phase: 'needs-discussion' },
|
|
213
|
+
prefs: undefined,
|
|
214
|
+
structuredQuestionsAvailable: 'false',
|
|
215
|
+
} as unknown as DispatchContext;
|
|
216
|
+
|
|
217
|
+
// ── Auto-mode case: the rule must call markDepthVerified ──
|
|
218
|
+
_setAutoActiveForTest(true);
|
|
219
|
+
let snap = loadWriteGateSnapshot(tempBase);
|
|
220
|
+
assert.strictEqual(
|
|
221
|
+
isMilestoneDepthVerifiedInSnapshot(snap, 'M005'),
|
|
222
|
+
false,
|
|
223
|
+
'precondition: M005 not yet marked',
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
// The rule's match fn calls markDepthVerified(mid, basePath) BEFORE
|
|
227
|
+
// awaiting buildDiscussMilestonePrompt — so even if the prompt build
|
|
228
|
+
// fails (e.g. because basePath does not contain a valid milestone),
|
|
229
|
+
// the side effect (snapshot write) has already happened.
|
|
230
|
+
try { await rule!.match(baseCtx); } catch { /* prompt build may fail; we only care about the mark */ }
|
|
231
|
+
|
|
232
|
+
snap = loadWriteGateSnapshot(tempBase);
|
|
233
|
+
assert.strictEqual(
|
|
234
|
+
isMilestoneDepthVerifiedInSnapshot(snap, 'M005'),
|
|
235
|
+
true,
|
|
236
|
+
'auto-mode: dispatch rule must call markDepthVerified(mid) — this fails on origin/main without the H6 fix',
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
// ── Interactive case: the rule must NOT call markDepthVerified ──
|
|
240
|
+
// clearDiscussionFlowState() only deletes the snapshot at process.cwd(),
|
|
241
|
+
// so we must explicitly remove the snapshot under our tempBase too.
|
|
242
|
+
clearDiscussionFlowState();
|
|
243
|
+
if (existsSync(snapshotFile)) unlinkSync(snapshotFile);
|
|
244
|
+
_setAutoActiveForTest(false);
|
|
245
|
+
snap = loadWriteGateSnapshot(tempBase);
|
|
246
|
+
assert.strictEqual(
|
|
247
|
+
isMilestoneDepthVerifiedInSnapshot(snap, 'M005'),
|
|
248
|
+
false,
|
|
249
|
+
'precondition: state cleared',
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
try { await rule!.match(baseCtx); } catch { /* prompt build may fail */ }
|
|
253
|
+
|
|
254
|
+
snap = loadWriteGateSnapshot(tempBase);
|
|
255
|
+
assert.strictEqual(
|
|
256
|
+
isMilestoneDepthVerifiedInSnapshot(snap, 'M005'),
|
|
257
|
+
false,
|
|
258
|
+
'interactive mode: dispatch rule must NOT call markDepthVerified — humans still confirm',
|
|
259
|
+
);
|
|
260
|
+
} finally {
|
|
261
|
+
rmSync(tempBase, { recursive: true, force: true });
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
});
|