gsd-pi 2.77.0-dev.1d17f366c → 2.77.0-dev.2daa994b6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/headless.js +25 -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/gsd/auto/phases.js +5 -18
- package/dist/resources/extensions/gsd/auto/session.js +6 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +37 -8
- package/dist/resources/extensions/gsd/auto-post-unit.js +79 -0
- package/dist/resources/extensions/gsd/auto-prompts.js +372 -104
- package/dist/resources/extensions/gsd/auto-start.js +75 -24
- package/dist/resources/extensions/gsd/auto.js +34 -0
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +9 -1
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +7 -1
- 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/docs/preferences-reference.md +1 -1
- package/dist/resources/extensions/gsd/forensics.js +106 -0
- package/dist/resources/extensions/gsd/gsd-db.js +1 -1
- package/dist/resources/extensions/gsd/guided-flow.js +2 -4
- 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/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 +5 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -2
- package/dist/resources/extensions/gsd/service-tier.js +5 -2
- 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/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 +334 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +51 -0
- 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 +17 -17
- 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/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 +17 -17
- 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 +1 -3
- 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/workflow-tools.test.ts +80 -39
- 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/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 +29 -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/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 +39 -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/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/__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/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/image.test.js +6 -5
- package/packages/pi-tui/dist/components/image.test.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/image.test.ts +10 -5
- 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/tests/stream-adapter.test.ts +80 -72
- 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/phases.ts +6 -17
- package/src/resources/extensions/gsd/auto/session.ts +7 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +40 -8
- package/src/resources/extensions/gsd/auto-post-unit.ts +81 -0
- package/src/resources/extensions/gsd/auto-prompts.ts +385 -93
- package/src/resources/extensions/gsd/auto-start.ts +97 -4
- package/src/resources/extensions/gsd/auto.ts +37 -0
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +9 -1
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +7 -1
- 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/docs/preferences-reference.md +1 -1
- package/src/resources/extensions/gsd/forensics.ts +118 -1
- package/src/resources/extensions/gsd/git-service.ts +16 -0
- package/src/resources/extensions/gsd/gsd-db.ts +1 -1
- package/src/resources/extensions/gsd/guided-flow.ts +2 -4
- 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/model-cost-table.ts +3 -0
- package/src/resources/extensions/gsd/model-router.ts +6 -0
- 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 +5 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -2
- package/src/resources/extensions/gsd/service-tier.ts +5 -2
- 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/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +25 -292
- 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-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 +8 -4
- 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/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/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/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/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/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 +1 -1
- 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/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/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/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 +22 -16
- 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/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/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/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/smart-entry-draft.test.ts +2 -1
- 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-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/unit-context-composer.test.ts +355 -0
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +203 -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 -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/zombie-gsd-state.test.ts +80 -96
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +8 -2
- package/src/resources/extensions/gsd/unit-context-composer.ts +218 -0
- package/src/resources/extensions/gsd/unit-context-manifest.ts +492 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +53 -0
- 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/{vidAVJkURvTJ0_V2-64ro → gYYky7yfxW8txb9vU2TrJ}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{vidAVJkURvTJ0_V2-64ro → gYYky7yfxW8txb9vU2TrJ}/_ssgManifest.js +0 -0
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import test, { mock } from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
-
import { readFileSync } from "node:fs";
|
|
4
|
-
import { resolve } from "node:path";
|
|
5
3
|
|
|
6
4
|
import {
|
|
7
5
|
resolveAgentEnd,
|
|
@@ -556,65 +554,15 @@ test("auto-loop.ts exports autoLoop, runUnit, resolveAgentEnd", async () => {
|
|
|
556
554
|
);
|
|
557
555
|
});
|
|
558
556
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
});
|
|
569
|
-
|
|
570
|
-
test("auto/resolve.ts one-shot pattern: _currentResolve is nulled before calling resolver", () => {
|
|
571
|
-
const src = readFileSync(
|
|
572
|
-
resolve(import.meta.dirname, "..", "auto", "resolve.ts"),
|
|
573
|
-
"utf-8",
|
|
574
|
-
);
|
|
575
|
-
// The one-shot pattern requires: save ref, null the variable, then call
|
|
576
|
-
const resolveBlock = src.slice(
|
|
577
|
-
src.indexOf("export function resolveAgentEnd"),
|
|
578
|
-
src.indexOf("export function resolveAgentEnd") + 600,
|
|
579
|
-
);
|
|
580
|
-
const nullIdx = resolveBlock.indexOf("_currentResolve = null");
|
|
581
|
-
const callIdx = resolveBlock.indexOf("r({");
|
|
582
|
-
assert.ok(nullIdx > 0, "should null _currentResolve in resolveAgentEnd");
|
|
583
|
-
assert.ok(callIdx > 0, "should call resolver in resolveAgentEnd");
|
|
584
|
-
assert.ok(
|
|
585
|
-
nullIdx < callIdx,
|
|
586
|
-
"_currentResolve should be nulled before calling the resolver (one-shot)",
|
|
587
|
-
);
|
|
588
|
-
});
|
|
589
|
-
|
|
590
|
-
test("auto/phases.ts: selectAndApplyModel called exactly once and before updateProgressWidget (#2907)", () => {
|
|
591
|
-
const src = readFileSync(
|
|
592
|
-
resolve(import.meta.dirname, "..", "auto", "phases.ts"),
|
|
593
|
-
"utf-8",
|
|
594
|
-
);
|
|
595
|
-
// Extract the runUnitPhase function body
|
|
596
|
-
const fnStart = src.indexOf("export async function runUnitPhase");
|
|
597
|
-
assert.ok(fnStart > 0, "runUnitPhase should exist in phases.ts");
|
|
598
|
-
const fnBody = src.slice(fnStart, fnStart + 16000);
|
|
599
|
-
|
|
600
|
-
// selectAndApplyModel must appear exactly once
|
|
601
|
-
const allOccurrences = [...fnBody.matchAll(/selectAndApplyModel\(/g)];
|
|
602
|
-
assert.equal(
|
|
603
|
-
allOccurrences.length,
|
|
604
|
-
1,
|
|
605
|
-
`selectAndApplyModel should be called exactly once in runUnitPhase, found ${allOccurrences.length} calls`,
|
|
606
|
-
);
|
|
607
|
-
|
|
608
|
-
// selectAndApplyModel must appear BEFORE updateProgressWidget
|
|
609
|
-
const modelIdx = fnBody.indexOf("selectAndApplyModel(");
|
|
610
|
-
const widgetIdx = fnBody.indexOf("updateProgressWidget(");
|
|
611
|
-
assert.ok(modelIdx > 0, "selectAndApplyModel should exist in runUnitPhase");
|
|
612
|
-
assert.ok(widgetIdx > 0, "updateProgressWidget should exist in runUnitPhase");
|
|
613
|
-
assert.ok(
|
|
614
|
-
modelIdx < widgetIdx,
|
|
615
|
-
"selectAndApplyModel must be called BEFORE updateProgressWidget (#2899/#2907)",
|
|
616
|
-
);
|
|
617
|
-
});
|
|
557
|
+
// NOTE: the "while keyword", "one-shot null-before-resolve", and
|
|
558
|
+
// "selectAndApplyModel before updateProgressWidget" source-grep tests
|
|
559
|
+
// previously here were deleted as tautological (readFileSync + substring
|
|
560
|
+
// match). The one-shot pattern is already covered behaviourally by the
|
|
561
|
+
// "double resolveAgentEnd only resolves once" test above, which drives the
|
|
562
|
+
// real resolveAgentEnd/runUnit flow and asserts on the observable promise
|
|
563
|
+
// outcome. The phases.ts ordering contract is tracked via a follow-up
|
|
564
|
+
// issue proposing extraction of a pure `dispatchOrder` helper (per the
|
|
565
|
+
// #4832/PR #4859 precedent) so it can be tested behaviourally.
|
|
618
566
|
|
|
619
567
|
// ─── autoLoop tests (T02) ─────────────────────────────────────────────────
|
|
620
568
|
|
|
@@ -1451,217 +1399,18 @@ test("autoLoop exits when no active milestone found", async (t) => {
|
|
|
1451
1399
|
);
|
|
1452
1400
|
});
|
|
1453
1401
|
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
const src = readFileSync(
|
|
1467
|
-
resolve(import.meta.dirname, "..", "auto", "loop.ts"),
|
|
1468
|
-
"utf-8",
|
|
1469
|
-
);
|
|
1470
|
-
assert.ok(
|
|
1471
|
-
src.includes("deps: LoopDeps"),
|
|
1472
|
-
"autoLoop should accept a deps: LoopDeps parameter",
|
|
1473
|
-
);
|
|
1474
|
-
});
|
|
1475
|
-
|
|
1476
|
-
test("autoLoop contains while (s.active) loop", () => {
|
|
1477
|
-
const src = readFileSync(
|
|
1478
|
-
resolve(import.meta.dirname, "..", "auto", "loop.ts"),
|
|
1479
|
-
"utf-8",
|
|
1480
|
-
);
|
|
1481
|
-
assert.ok(
|
|
1482
|
-
src.includes("while (s.active)"),
|
|
1483
|
-
"autoLoop should contain a while (s.active) loop",
|
|
1484
|
-
);
|
|
1485
|
-
});
|
|
1486
|
-
|
|
1487
|
-
// ── T03: End-to-end wiring structural assertions ─────────────────────────────
|
|
1488
|
-
|
|
1489
|
-
test("auto-loop.ts barrel re-exports autoLoop, runUnit, and resolveAgentEnd", () => {
|
|
1490
|
-
const barrel = readFileSync(
|
|
1491
|
-
resolve(import.meta.dirname, "..", "auto-loop.ts"),
|
|
1492
|
-
"utf-8",
|
|
1493
|
-
);
|
|
1494
|
-
assert.ok(
|
|
1495
|
-
barrel.includes("autoLoop"),
|
|
1496
|
-
"barrel must re-export autoLoop",
|
|
1497
|
-
);
|
|
1498
|
-
assert.ok(
|
|
1499
|
-
barrel.includes("runUnit"),
|
|
1500
|
-
"barrel must re-export runUnit",
|
|
1501
|
-
);
|
|
1502
|
-
assert.ok(
|
|
1503
|
-
barrel.includes("resolveAgentEnd"),
|
|
1504
|
-
"barrel must re-export resolveAgentEnd",
|
|
1505
|
-
);
|
|
1506
|
-
// Verify the actual function declarations exist in the submodules
|
|
1507
|
-
const loopSrc = readFileSync(
|
|
1508
|
-
resolve(import.meta.dirname, "..", "auto", "loop.ts"),
|
|
1509
|
-
"utf-8",
|
|
1510
|
-
);
|
|
1511
|
-
assert.ok(
|
|
1512
|
-
loopSrc.includes("export async function autoLoop"),
|
|
1513
|
-
"auto/loop.ts must define autoLoop",
|
|
1514
|
-
);
|
|
1515
|
-
const runUnitSrc = readFileSync(
|
|
1516
|
-
resolve(import.meta.dirname, "..", "auto", "run-unit.ts"),
|
|
1517
|
-
"utf-8",
|
|
1518
|
-
);
|
|
1519
|
-
assert.ok(
|
|
1520
|
-
runUnitSrc.includes("export async function runUnit"),
|
|
1521
|
-
"auto/run-unit.ts must define runUnit",
|
|
1522
|
-
);
|
|
1523
|
-
const resolveSrc = readFileSync(
|
|
1524
|
-
resolve(import.meta.dirname, "..", "auto", "resolve.ts"),
|
|
1525
|
-
"utf-8",
|
|
1526
|
-
);
|
|
1527
|
-
assert.ok(
|
|
1528
|
-
resolveSrc.includes("export function resolveAgentEnd"),
|
|
1529
|
-
"auto/resolve.ts must define resolveAgentEnd",
|
|
1530
|
-
);
|
|
1531
|
-
});
|
|
1532
|
-
|
|
1533
|
-
test("auto.ts startAuto dispatches through the UOK kernel wrapper with explicit kernel and legacy paths", () => {
|
|
1534
|
-
const src = readFileSync(
|
|
1535
|
-
resolve(import.meta.dirname, "..", "auto.ts"),
|
|
1536
|
-
"utf-8",
|
|
1537
|
-
);
|
|
1538
|
-
// Find the startAuto function body
|
|
1539
|
-
const fnIdx = src.indexOf("export async function startAuto");
|
|
1540
|
-
assert.ok(fnIdx > -1, "startAuto must exist in auto.ts");
|
|
1541
|
-
const fnEnd = src.indexOf("\n// ─── ", fnIdx + 100);
|
|
1542
|
-
const fnBlock =
|
|
1543
|
-
fnEnd > -1 ? src.slice(fnIdx, fnEnd) : src.slice(fnIdx, fnIdx + 5000);
|
|
1544
|
-
assert.ok(
|
|
1545
|
-
fnBlock.includes("runAutoLoopWithUok("),
|
|
1546
|
-
"startAuto must dispatch through runAutoLoopWithUok()",
|
|
1547
|
-
);
|
|
1548
|
-
assert.ok(
|
|
1549
|
-
fnBlock.includes("runKernelLoop: runUokKernelLoop"),
|
|
1550
|
-
"startAuto must wire the explicit UOK kernel loop path",
|
|
1551
|
-
);
|
|
1552
|
-
assert.ok(
|
|
1553
|
-
fnBlock.includes("runLegacyLoop: runLegacyAutoLoop"),
|
|
1554
|
-
"startAuto must preserve explicit legacy fallback dispatch",
|
|
1555
|
-
);
|
|
1556
|
-
});
|
|
1557
|
-
|
|
1558
|
-
test("startAuto calls selfHealRuntimeRecords before autoLoop (#1727)", { skip: "selfHealRuntimeRecords moved to crash-recovery pipeline in v3" }, () => {
|
|
1559
|
-
const src = readFileSync(
|
|
1560
|
-
resolve(import.meta.dirname, "..", "auto.ts"),
|
|
1561
|
-
"utf-8",
|
|
1562
|
-
);
|
|
1563
|
-
const fnIdx = src.indexOf("export async function startAuto");
|
|
1564
|
-
assert.ok(fnIdx > -1, "startAuto must exist in auto.ts");
|
|
1565
|
-
const fnEnd = src.indexOf("\n// ─── ", fnIdx + 100);
|
|
1566
|
-
const fnBlock =
|
|
1567
|
-
fnEnd > -1 ? src.slice(fnIdx, fnEnd) : src.slice(fnIdx, fnIdx + 5000);
|
|
1568
|
-
|
|
1569
|
-
// Both autoLoop call sites must be preceded by selfHealRuntimeRecords
|
|
1570
|
-
const healIdx = fnBlock.indexOf("selfHealRuntimeRecords");
|
|
1571
|
-
const loopIdx = fnBlock.indexOf("autoLoop(");
|
|
1572
|
-
assert.ok(healIdx > -1, "startAuto must call selfHealRuntimeRecords");
|
|
1573
|
-
assert.ok(healIdx < loopIdx, "selfHealRuntimeRecords must be called before autoLoop");
|
|
1574
|
-
|
|
1575
|
-
// Verify the second autoLoop call site also has selfHeal before it (if present)
|
|
1576
|
-
const secondLoopIdx = fnBlock.indexOf("autoLoop(", loopIdx + 1);
|
|
1577
|
-
const secondHealIdx = fnBlock.indexOf("selfHealRuntimeRecords", healIdx + 1);
|
|
1578
|
-
assert.ok(
|
|
1579
|
-
secondLoopIdx === -1 || (secondHealIdx > -1 && secondHealIdx < secondLoopIdx),
|
|
1580
|
-
"if a second autoLoop call exists, it must also be preceded by selfHealRuntimeRecords",
|
|
1581
|
-
);
|
|
1582
|
-
});
|
|
1583
|
-
|
|
1584
|
-
test("startAuto guards against concurrent invocation (#2923)", () => {
|
|
1585
|
-
const src = readFileSync(
|
|
1586
|
-
resolve(import.meta.dirname, "..", "auto.ts"),
|
|
1587
|
-
"utf-8",
|
|
1588
|
-
);
|
|
1589
|
-
const fnIdx = src.indexOf("export async function startAuto");
|
|
1590
|
-
assert.ok(fnIdx > -1, "startAuto must exist in auto.ts");
|
|
1591
|
-
// The guard must appear before any other logic in the function body
|
|
1592
|
-
const fnBody = src.slice(fnIdx, fnIdx + 500);
|
|
1593
|
-
const activeGuard = fnBody.indexOf("if (s.active)");
|
|
1594
|
-
assert.ok(activeGuard > -1, "startAuto must check s.active to prevent concurrent auto-loops");
|
|
1595
|
-
const returnIdx = fnBody.indexOf("return;", activeGuard);
|
|
1596
|
-
assert.ok(
|
|
1597
|
-
returnIdx > -1 && returnIdx < activeGuard + 120,
|
|
1598
|
-
"s.active guard must early-return to prevent a second concurrent loop",
|
|
1599
|
-
);
|
|
1600
|
-
});
|
|
1601
|
-
|
|
1602
|
-
test("agent_end handler calls resolveAgentEnd (not the legacy auto.ts path)", () => {
|
|
1603
|
-
const hooksSrc = readFileSync(
|
|
1604
|
-
resolve(import.meta.dirname, "..", "bootstrap", "register-hooks.ts"),
|
|
1605
|
-
"utf-8",
|
|
1606
|
-
);
|
|
1607
|
-
// Verify the agent_end hook is registered
|
|
1608
|
-
const handlerIdx = hooksSrc.indexOf('pi.on("agent_end"');
|
|
1609
|
-
assert.ok(handlerIdx > -1, "register-hooks.ts must have an agent_end handler");
|
|
1610
|
-
|
|
1611
|
-
const recoverySrc = readFileSync(
|
|
1612
|
-
resolve(import.meta.dirname, "..", "bootstrap", "agent-end-recovery.ts"),
|
|
1613
|
-
"utf-8",
|
|
1614
|
-
);
|
|
1615
|
-
assert.ok(
|
|
1616
|
-
recoverySrc.includes("resolveAgentEnd(event)"),
|
|
1617
|
-
"agent_end success path must call resolveAgentEnd(event) instead of legacy wrappers",
|
|
1618
|
-
);
|
|
1619
|
-
assert.ok(
|
|
1620
|
-
recoverySrc.includes("isSessionSwitchInFlight()"),
|
|
1621
|
-
"agent_end handler must ignore session-switch agent_end events from cmdCtx.newSession()",
|
|
1622
|
-
);
|
|
1623
|
-
});
|
|
1624
|
-
|
|
1625
|
-
test("auto-verification.ts runPostUnitVerification does not take dispatchNextUnit callback", () => {
|
|
1626
|
-
const src = readFileSync(
|
|
1627
|
-
resolve(import.meta.dirname, "..", "auto-verification.ts"),
|
|
1628
|
-
"utf-8",
|
|
1629
|
-
);
|
|
1630
|
-
const fnIdx = src.indexOf("export async function runPostUnitVerification");
|
|
1631
|
-
assert.ok(fnIdx > -1, "runPostUnitVerification must exist");
|
|
1632
|
-
const sigEnd = src.indexOf("): Promise<VerificationResult>", fnIdx);
|
|
1633
|
-
const signature = src.slice(fnIdx, sigEnd);
|
|
1634
|
-
assert.ok(
|
|
1635
|
-
!signature.includes("dispatchNextUnit"),
|
|
1636
|
-
"runPostUnitVerification must not take a dispatchNextUnit callback parameter",
|
|
1637
|
-
);
|
|
1638
|
-
assert.ok(
|
|
1639
|
-
!signature.includes("startDispatchGapWatchdog"),
|
|
1640
|
-
"runPostUnitVerification must not take a startDispatchGapWatchdog callback parameter",
|
|
1641
|
-
);
|
|
1642
|
-
});
|
|
1643
|
-
|
|
1644
|
-
test("auto-timeout-recovery.ts calls resolveAgentEnd instead of dispatchNextUnit", () => {
|
|
1645
|
-
const src = readFileSync(
|
|
1646
|
-
resolve(import.meta.dirname, "..", "auto-timeout-recovery.ts"),
|
|
1647
|
-
"utf-8",
|
|
1648
|
-
);
|
|
1649
|
-
assert.ok(
|
|
1650
|
-
!src.includes("await dispatchNextUnit"),
|
|
1651
|
-
"auto-timeout-recovery.ts must not call dispatchNextUnit",
|
|
1652
|
-
);
|
|
1653
|
-
// After PR #4716, advance branches go through bumpAndResolveSynthetic()
|
|
1654
|
-
// (which bumps the turn epoch and calls resolveAgentEnd atomically).
|
|
1655
|
-
// Either direct resolveAgentEnd() or the helper satisfies the invariant:
|
|
1656
|
-
// the loop must be re-iterated on timeout recovery.
|
|
1657
|
-
const reIteratesLoop =
|
|
1658
|
-
src.includes("resolveAgentEnd(") ||
|
|
1659
|
-
src.includes("bumpAndResolveSynthetic(");
|
|
1660
|
-
assert.ok(
|
|
1661
|
-
reIteratesLoop,
|
|
1662
|
-
"auto-timeout-recovery.ts must call resolveAgentEnd (directly or via bumpAndResolveSynthetic) to re-iterate the loop on timeout recovery",
|
|
1663
|
-
);
|
|
1664
|
-
});
|
|
1402
|
+
// NOTE: The T03 "wiring structural assertions" block (barrel re-exports,
|
|
1403
|
+
// LoopDeps-interface-declared, while-loop keyword, UOK kernel wrapper,
|
|
1404
|
+
// selfHeal ordering, s.active concurrent guard, agent_end handler call
|
|
1405
|
+
// shape, runPostUnitVerification signature, auto-timeout-recovery call
|
|
1406
|
+
// shape) was a pure source-grep chain — readFileSync + includes/indexOf —
|
|
1407
|
+
// so it asserted on code shape rather than runtime behaviour. The symbols
|
|
1408
|
+
// named in those assertions are ALREADY imported at the top of this file;
|
|
1409
|
+
// if the production barrel drops any of them, this file fails to import
|
|
1410
|
+
// and every test here fails cold. That import-time check is the real
|
|
1411
|
+
// behavioural contract. The ordering/signature contracts (UOK dispatch,
|
|
1412
|
+
// concurrent guard, agent_end wiring) are tracked as follow-up issues for
|
|
1413
|
+
// pure-helper extraction per the #4832/PR #4859 precedent.
|
|
1665
1414
|
|
|
1666
1415
|
// ── Stuck counter tests ──────────────────────────────────────────────────────
|
|
1667
1416
|
|
|
@@ -1960,26 +1709,10 @@ test("detectStuck: truncates long error strings", () => {
|
|
|
1960
1709
|
assert.ok(result!.reason.length < 300, "reason should be truncated");
|
|
1961
1710
|
});
|
|
1962
1711
|
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
resolve(import.meta.dirname, "..", "auto", "phases.ts"),
|
|
1968
|
-
"utf-8",
|
|
1969
|
-
);
|
|
1970
|
-
assert.ok(
|
|
1971
|
-
src.includes('"stuck-detected"'),
|
|
1972
|
-
"auto/phases.ts must log phase: 'stuck-detected' when stuck detection fires",
|
|
1973
|
-
);
|
|
1974
|
-
assert.ok(
|
|
1975
|
-
src.includes('"stuck-counter-reset"'),
|
|
1976
|
-
"auto/phases.ts must log phase: 'stuck-counter-reset' when recovery resets on new unit",
|
|
1977
|
-
);
|
|
1978
|
-
assert.ok(
|
|
1979
|
-
src.includes("detectStuck"),
|
|
1980
|
-
"auto/phases.ts must use detectStuck for sliding window analysis",
|
|
1981
|
-
);
|
|
1982
|
-
});
|
|
1712
|
+
// NOTE: the "stuck-detected" / "stuck-counter-reset" debug-log grep was
|
|
1713
|
+
// removed — that string test never exercised the detector. detectStuck
|
|
1714
|
+
// itself is tested behaviourally above against the real implementation
|
|
1715
|
+
// imported from auto-loop.js.
|
|
1983
1716
|
|
|
1984
1717
|
// ── Lifecycle test (S05/T02) ─────────────────────────────────────────────────
|
|
1985
1718
|
|
|
@@ -15,6 +15,7 @@ import assert from 'node:assert/strict';
|
|
|
15
15
|
import { readFileSync } from 'node:fs';
|
|
16
16
|
import { fileURLToPath } from 'node:url';
|
|
17
17
|
import { dirname, join } from 'node:path';
|
|
18
|
+
import { extractSourceRegion } from "./test-helpers.ts";
|
|
18
19
|
|
|
19
20
|
const __filename = fileURLToPath(import.meta.url);
|
|
20
21
|
const __dirname = dirname(__filename);
|
|
@@ -43,7 +44,9 @@ describe('auto-remediate stale slice status (#3673)', () => {
|
|
|
43
44
|
assert.match(before, /try\s*\{/,
|
|
44
45
|
'updateSliceStatus should be inside a try block');
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
// Bound the region to stop before the rogue fallback so /catch/ only
|
|
48
|
+
// matches this try block's catch, not an unrelated later one.
|
|
49
|
+
const after = extractSourceRegion(source, 'updateSliceStatus(mid, sid', 'rogues.push({');
|
|
47
50
|
assert.match(after, /catch/,
|
|
48
51
|
'try block should have a catch for fallback');
|
|
49
52
|
});
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Regression tests for PR #4288 — auto-retry bug, .mcp.json churn, and MCP
|
|
3
|
-
* worktree routing fixes.
|
|
3
|
+
* worktree routing fixes (behaviour subset).
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* The remaining 13 structural assertions (source-grep against register-hooks,
|
|
6
|
+
* auto-recovery, workflow-tools) were removed in favour of follow-up issues
|
|
7
|
+
* tracking a pure-helper extraction per the #4832/PR #4859 precedent. This
|
|
8
|
+
* file retains only the real behaviour tests against the public API of
|
|
9
|
+
* evidence-collector.
|
|
10
|
+
*
|
|
11
|
+
* Follow-ups filed for the removed coverage — see PR body.
|
|
10
12
|
*
|
|
11
13
|
* Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
12
14
|
*/
|
|
13
15
|
|
|
14
16
|
import { describe, it, beforeEach } from "node:test";
|
|
15
17
|
import assert from "node:assert/strict";
|
|
16
|
-
import { readFileSync } from "node:fs";
|
|
17
|
-
import { resolve } from "node:path";
|
|
18
18
|
|
|
19
19
|
import {
|
|
20
20
|
resetEvidence,
|
|
@@ -24,8 +24,6 @@ import {
|
|
|
24
24
|
type BashEvidence,
|
|
25
25
|
} from "../safety/evidence-collector.js";
|
|
26
26
|
|
|
27
|
-
// ─── 1. evidence-collector: functional ─────────────────────────────────────
|
|
28
|
-
|
|
29
27
|
describe("evidence-collector: toolCallId-based matching (A-3)", () => {
|
|
30
28
|
beforeEach(() => {
|
|
31
29
|
resetEvidence();
|
|
@@ -42,14 +40,9 @@ describe("evidence-collector: toolCallId-based matching (A-3)", () => {
|
|
|
42
40
|
});
|
|
43
41
|
|
|
44
42
|
it("matches results to the correct entry by toolCallId, not insertion order", () => {
|
|
45
|
-
// Simulate two parallel bash calls dispatched in order tc-1, tc-2.
|
|
46
43
|
recordToolCall("tc-1", "bash", { command: "slow-thing" });
|
|
47
44
|
recordToolCall("tc-2", "bash", { command: "fast-thing" });
|
|
48
45
|
|
|
49
|
-
// Results arrive out of order: tc-2 first (fast), then tc-1 (slow).
|
|
50
|
-
// With the old empty-string-matching strategy, tc-2's result would be
|
|
51
|
-
// stapled to tc-1's entry because findLastUnresolved scanned backwards
|
|
52
|
-
// for empty ids. Now we match by id directly.
|
|
53
46
|
recordToolResult("tc-2", "bash", "Command exited with code 0\nfast-output", false);
|
|
54
47
|
recordToolResult("tc-1", "bash", "Command exited with code 1\nslow-failure", true);
|
|
55
48
|
|
|
@@ -59,9 +52,6 @@ describe("evidence-collector: toolCallId-based matching (A-3)", () => {
|
|
|
59
52
|
|
|
60
53
|
assert.ok(tc1, "tc-1 entry must exist");
|
|
61
54
|
assert.ok(tc2, "tc-2 entry must exist");
|
|
62
|
-
|
|
63
|
-
// The original command stays attached to the entry it was recorded with,
|
|
64
|
-
// and the result matches the id it was reported for.
|
|
65
55
|
assert.equal(tc1.command, "slow-thing");
|
|
66
56
|
assert.equal(tc1.exitCode, 1);
|
|
67
57
|
assert.ok(tc1.outputSnippet.includes("slow-failure"));
|
|
@@ -78,7 +68,6 @@ describe("evidence-collector: toolCallId-based matching (A-3)", () => {
|
|
|
78
68
|
const entries = getEvidence() as readonly BashEvidence[];
|
|
79
69
|
assert.equal(entries.length, 1);
|
|
80
70
|
assert.equal(entries[0].toolCallId, "tc-1");
|
|
81
|
-
// tc-1 must be untouched — no result was reported for it.
|
|
82
71
|
assert.equal(entries[0].exitCode, -1);
|
|
83
72
|
assert.equal(entries[0].outputSnippet, "");
|
|
84
73
|
});
|
|
@@ -95,178 +84,3 @@ describe("evidence-collector: toolCallId-based matching (A-3)", () => {
|
|
|
95
84
|
assert.equal(entries[1].toolCallId, "tc-edit");
|
|
96
85
|
});
|
|
97
86
|
});
|
|
98
|
-
|
|
99
|
-
// ─── 2. register-hooks: MCP auto-prep gated inside auto-worktrees (A-1) ────
|
|
100
|
-
|
|
101
|
-
describe("register-hooks: skip prepareWorkflowMcpForProject inside auto-worktrees (A-1)", () => {
|
|
102
|
-
const src = readFileSync(
|
|
103
|
-
resolve(process.cwd(), "src", "resources", "extensions", "gsd", "bootstrap", "register-hooks.ts"),
|
|
104
|
-
"utf-8",
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
it("session_start hook is gated on isInAutoWorktree", () => {
|
|
108
|
-
const idx = src.indexOf('pi.on("session_start"');
|
|
109
|
-
assert.ok(idx !== -1, "session_start handler must exist");
|
|
110
|
-
const block = src.slice(idx, idx + 2500);
|
|
111
|
-
assert.ok(
|
|
112
|
-
block.includes("isInAutoWorktree"),
|
|
113
|
-
"session_start must consult isInAutoWorktree before preparing MCP",
|
|
114
|
-
);
|
|
115
|
-
assert.ok(
|
|
116
|
-
block.includes("prepareWorkflowMcpForProject"),
|
|
117
|
-
"session_start still prepares MCP for non-worktree paths",
|
|
118
|
-
);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it("session_switch hook is gated on isInAutoWorktree", () => {
|
|
122
|
-
const idx = src.indexOf('pi.on("session_switch"');
|
|
123
|
-
assert.ok(idx !== -1, "session_switch handler must exist");
|
|
124
|
-
const block = src.slice(idx, idx + 2500);
|
|
125
|
-
assert.ok(
|
|
126
|
-
block.includes("isInAutoWorktree"),
|
|
127
|
-
"session_switch must consult isInAutoWorktree before preparing MCP",
|
|
128
|
-
);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it("tool_call hook forwards event.toolCallId into safetyRecordToolCall (A-3)", () => {
|
|
132
|
-
// Find the call site (skip the import line by looking for the opening paren).
|
|
133
|
-
const idx = src.indexOf("safetyRecordToolCall(");
|
|
134
|
-
assert.ok(idx !== -1, "safetyRecordToolCall call must exist");
|
|
135
|
-
const line = src.slice(idx, src.indexOf("\n", idx));
|
|
136
|
-
assert.ok(
|
|
137
|
-
line.includes("event.toolCallId"),
|
|
138
|
-
"safetyRecordToolCall must receive event.toolCallId as the first argument",
|
|
139
|
-
);
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// ─── 3. auto-recovery: verify-fail instrumentation ─────────────────────────
|
|
144
|
-
|
|
145
|
-
describe("verifyExpectedArtifact: verify-fail exit-point logging (Phase B diag)", () => {
|
|
146
|
-
const src = readFileSync(
|
|
147
|
-
resolve(process.cwd(), "src", "resources", "extensions", "gsd", "auto-recovery.ts"),
|
|
148
|
-
"utf-8",
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
it("logs a verify-fail warning on the null-absPath exit", () => {
|
|
152
|
-
assert.ok(
|
|
153
|
-
src.includes('verify-fail ${unitType} ${unitId}: resolveExpectedArtifactPath returned null'),
|
|
154
|
-
"null-absPath branch must emit a diagnostic line",
|
|
155
|
-
);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it("logs a verify-fail warning on the existsSync-false exit", () => {
|
|
159
|
-
assert.ok(
|
|
160
|
-
src.includes("verify-fail ${unitType} ${unitId}: existsSync false"),
|
|
161
|
-
"existsSync-false branch must emit a diagnostic line",
|
|
162
|
-
);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it("logs a verify-fail warning on the plan-slice no-task-entry exit", () => {
|
|
166
|
-
assert.ok(
|
|
167
|
-
src.includes("verify-fail ${unitType} ${unitId}: plan has no task checkbox/heading"),
|
|
168
|
-
"plan-slice no-task branch must emit a diagnostic line",
|
|
169
|
-
);
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it("plan-slice task-plan-files check fails fast on missing tasks dir (hardening)", () => {
|
|
173
|
-
// The original check silently passed when resolveTasksDir returned null.
|
|
174
|
-
// The new check returns false with a diagnostic, which is correct — if
|
|
175
|
-
// the tool successfully planned tasks, the tasks/ dir must exist.
|
|
176
|
-
const idx = src.indexOf('verify-fail ${unitType} ${unitId}: resolveTasksDir returned null');
|
|
177
|
-
assert.ok(
|
|
178
|
-
idx !== -1,
|
|
179
|
-
"resolveTasksDir-null branch must emit a diagnostic and return false",
|
|
180
|
-
);
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
// ─── 4. workflow-tools (mcp-server): guard + optional projectDir + routing ─
|
|
185
|
-
|
|
186
|
-
describe("mcp-server workflow-tools: projectDir routing (Phase B root cause)", () => {
|
|
187
|
-
const src = readFileSync(
|
|
188
|
-
resolve(process.cwd(), "packages", "mcp-server", "src", "workflow-tools.ts"),
|
|
189
|
-
"utf-8",
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
it("projectDirParam is optional and documents the default", () => {
|
|
193
|
-
const idx = src.indexOf("const projectDirParam");
|
|
194
|
-
assert.ok(idx !== -1, "projectDirParam definition must exist");
|
|
195
|
-
const block = src.slice(idx, idx + 600);
|
|
196
|
-
assert.ok(
|
|
197
|
-
block.includes(".optional()"),
|
|
198
|
-
"projectDirParam must be optional so the agent stops deliberating",
|
|
199
|
-
);
|
|
200
|
-
assert.ok(
|
|
201
|
-
/Omit this field/i.test(block),
|
|
202
|
-
"description must tell the agent to omit the field",
|
|
203
|
-
);
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
it("parseWorkflowArgs defaults projectDir to process.cwd() when omitted", () => {
|
|
207
|
-
const idx = src.indexOf("function parseWorkflowArgs");
|
|
208
|
-
assert.ok(idx !== -1, "parseWorkflowArgs must exist");
|
|
209
|
-
const block = src.slice(idx, idx + 1500);
|
|
210
|
-
assert.ok(
|
|
211
|
-
block.includes("parsed.projectDir ?? process.cwd()"),
|
|
212
|
-
"parseWorkflowArgs must fall back to process.cwd() when projectDir is omitted",
|
|
213
|
-
);
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
it("validateProjectDir accepts external-state worktree paths via .gsd symlink target", () => {
|
|
217
|
-
const idx = src.indexOf("function validateProjectDir");
|
|
218
|
-
assert.ok(idx !== -1, "validateProjectDir must exist");
|
|
219
|
-
const block = src.slice(idx, idx + 2500);
|
|
220
|
-
assert.ok(
|
|
221
|
-
block.includes("resolveExternalStateRoot"),
|
|
222
|
-
"validateProjectDir must consult resolveExternalStateRoot for external-state layouts",
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
const helperIdx = src.indexOf("function resolveExternalStateRoot");
|
|
226
|
-
assert.ok(helperIdx !== -1, "resolveExternalStateRoot helper must exist");
|
|
227
|
-
const helperBlock = src.slice(helperIdx, helperIdx + 600);
|
|
228
|
-
assert.ok(
|
|
229
|
-
helperBlock.includes("realpathSync"),
|
|
230
|
-
"resolveExternalStateRoot must use realpathSync to follow the symlink",
|
|
231
|
-
);
|
|
232
|
-
assert.ok(
|
|
233
|
-
/join\([^)]*\.gsd/.test(helperBlock),
|
|
234
|
-
"resolveExternalStateRoot must resolve <allowedRoot>/.gsd",
|
|
235
|
-
);
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
it("parseWorkflowArgs routes tool writes to the active worktree when one exists", () => {
|
|
239
|
-
// This is the Phase B root-cause fix: when the tool call is scoped to a
|
|
240
|
-
// milestone that has an auto-worktree at <projectRoot>/.gsd/worktrees/<MID>/,
|
|
241
|
-
// tool writes must go to the worktree .gsd rather than the shared project .gsd.
|
|
242
|
-
const parseIdx = src.indexOf("function parseWorkflowArgs");
|
|
243
|
-
const parseBlock = src.slice(parseIdx, parseIdx + 2500);
|
|
244
|
-
assert.ok(
|
|
245
|
-
parseBlock.includes("resolveActiveWorktreeBasePath"),
|
|
246
|
-
"parseWorkflowArgs must consult resolveActiveWorktreeBasePath",
|
|
247
|
-
);
|
|
248
|
-
assert.ok(
|
|
249
|
-
parseBlock.includes("extractMilestoneId"),
|
|
250
|
-
"parseWorkflowArgs must extract the milestoneId to locate the worktree",
|
|
251
|
-
);
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
it("resolveActiveWorktreeBasePath checks .git presence to avoid hijacking stray directories", () => {
|
|
255
|
-
const idx = src.indexOf("function resolveActiveWorktreeBasePath");
|
|
256
|
-
assert.ok(idx !== -1, "resolveActiveWorktreeBasePath helper must exist");
|
|
257
|
-
const block = src.slice(idx, idx + 1200);
|
|
258
|
-
assert.ok(
|
|
259
|
-
block.includes('existsSync(join(wtPath, ".git"))'),
|
|
260
|
-
"resolveActiveWorktreeBasePath must verify a .git file exists in the worktree",
|
|
261
|
-
);
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
it("extractMilestoneId handles camelCase, snake_case, and short aliases", () => {
|
|
265
|
-
const idx = src.indexOf("function extractMilestoneId");
|
|
266
|
-
assert.ok(idx !== -1, "extractMilestoneId helper must exist");
|
|
267
|
-
const block = src.slice(idx, idx + 600);
|
|
268
|
-
assert.ok(block.includes("milestoneId"), "must check milestoneId");
|
|
269
|
-
assert.ok(block.includes("milestone_id"), "must check milestone_id");
|
|
270
|
-
assert.ok(block.includes("mid"), "must check mid");
|
|
271
|
-
});
|
|
272
|
-
});
|
|
@@ -10,6 +10,7 @@ import assert from "node:assert/strict";
|
|
|
10
10
|
import { readFileSync } from "node:fs";
|
|
11
11
|
import { join, dirname } from "node:path";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
|
+
import { extractSourceRegion } from "./test-helpers.ts";
|
|
13
14
|
|
|
14
15
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
16
|
const sourceFile = join(__dirname, "..", "auto-start.ts");
|
|
@@ -37,7 +38,7 @@ describe("auto-start cleanStaleRuntimeUnits DB gating (#4663)", () => {
|
|
|
37
38
|
test("cleanStaleRuntimeUnits predicate consults DB status when available", () => {
|
|
38
39
|
const cleanIdx = source.indexOf("cleanStaleRuntimeUnits(");
|
|
39
40
|
assert.ok(cleanIdx > -1);
|
|
40
|
-
const snippet = source
|
|
41
|
+
const snippet = extractSourceRegion(source, "cleanStaleRuntimeUnits(");
|
|
41
42
|
assert.match(
|
|
42
43
|
snippet,
|
|
43
44
|
/isDbAvailable\(\)/,
|
|
@@ -53,7 +54,7 @@ describe("auto-start cleanStaleRuntimeUnits DB gating (#4663)", () => {
|
|
|
53
54
|
test("cleanStaleRuntimeUnits predicate still falls back to SUMMARY-file when DB unavailable", () => {
|
|
54
55
|
const cleanIdx = source.indexOf("cleanStaleRuntimeUnits(");
|
|
55
56
|
assert.ok(cleanIdx > -1);
|
|
56
|
-
const snippet = source
|
|
57
|
+
const snippet = extractSourceRegion(source, "cleanStaleRuntimeUnits(");
|
|
57
58
|
assert.match(
|
|
58
59
|
snippet,
|
|
59
60
|
/resolveMilestoneFile\(base,\s*mid,\s*["']SUMMARY["']\)/,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {createTestContext, extractSourceRegion } from "./test-helpers.ts";
|
|
5
5
|
|
|
6
6
|
const { assertTrue, report } = createTestContext();
|
|
7
7
|
|
|
@@ -13,7 +13,7 @@ console.log("\n=== #2841: cold DB opened before initial deriveState ===");
|
|
|
13
13
|
const helperIdx = src.indexOf("async function openProjectDbIfPresent");
|
|
14
14
|
assertTrue(helperIdx >= 0, "auto-start.ts defines a helper for pre-derive DB open (#2841)");
|
|
15
15
|
|
|
16
|
-
const helperRegion = helperIdx >= 0 ? src
|
|
16
|
+
const helperRegion = helperIdx >= 0 ? extractSourceRegion(src, "async function openProjectDbIfPresent") : "";
|
|
17
17
|
assertTrue(
|
|
18
18
|
helperRegion.includes("resolveProjectRootDbPath(basePath)"),
|
|
19
19
|
"pre-derive DB helper resolves the project-root DB path (#2841)",
|