gsd-pi 2.81.0 → 2.82.0
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 +36 -24
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/gsd/auto/loop.js +111 -8
- package/dist/resources/extensions/gsd/auto/phases.js +190 -97
- package/dist/resources/extensions/gsd/auto/run-unit.js +66 -3
- package/dist/resources/extensions/gsd/auto/session.js +9 -0
- package/dist/resources/extensions/gsd/auto/verification-retry-policy.js +43 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +182 -178
- package/dist/resources/extensions/gsd/auto-dispatch.js +14 -11
- package/dist/resources/extensions/gsd/auto-post-unit.js +7 -1
- package/dist/resources/extensions/gsd/auto-recovery.js +6 -181
- package/dist/resources/extensions/gsd/auto-runtime-state.js +5 -0
- package/dist/resources/extensions/gsd/auto-start.js +20 -23
- package/dist/resources/extensions/gsd/auto-unit-closeout.js +33 -5
- package/dist/resources/extensions/gsd/auto-verification.js +12 -6
- package/dist/resources/extensions/gsd/auto-worktree.js +8 -0
- package/dist/resources/extensions/gsd/auto.js +265 -76
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +13 -6
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +8 -2
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +4 -8
- package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +4 -10
- package/dist/resources/extensions/gsd/commands/handlers/parallel.js +9 -0
- package/dist/resources/extensions/gsd/git-service.js +2 -1
- package/dist/resources/extensions/gsd/gsd-db.js +7 -23
- package/dist/resources/extensions/gsd/health-widget-core.js +1 -1
- package/dist/resources/extensions/gsd/health-widget.js +4 -10
- package/dist/resources/extensions/gsd/markdown-renderer.js +0 -95
- package/dist/resources/extensions/gsd/native-git-bridge.js +14 -14
- package/dist/resources/extensions/gsd/notification-overlay.js +35 -40
- package/dist/resources/extensions/gsd/parallel-merge.js +53 -30
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +25 -33
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +14 -12
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +20 -2
- package/dist/resources/extensions/gsd/prompts/discuss.md +20 -2
- package/dist/resources/extensions/gsd/recovery-classification.js +15 -1
- package/dist/resources/extensions/gsd/session-lock.js +40 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +131 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/merge-state.js +247 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +50 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +87 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/sketch-flag.js +50 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +124 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-worker.js +32 -0
- package/dist/resources/extensions/gsd/state-reconciliation/errors.js +41 -0
- package/dist/resources/extensions/gsd/state-reconciliation/index.js +99 -0
- package/dist/resources/extensions/gsd/state-reconciliation/registry.js +24 -0
- package/dist/resources/extensions/gsd/state-reconciliation/spawn-gate.js +43 -0
- package/dist/resources/extensions/gsd/state-reconciliation/types.js +3 -0
- package/dist/resources/extensions/gsd/state-reconciliation.js +5 -26
- package/dist/resources/extensions/gsd/tui/render-kit.js +74 -0
- package/dist/resources/extensions/gsd/watch/header-renderer.js +92 -69
- package/dist/resources/extensions/gsd/watch/splash-palette.js +10 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +2 -2
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +722 -316
- package/dist/resources/extensions/gsd/worktree-telemetry.js +3 -1
- 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 +9 -9
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- 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 +9 -9
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/welcome-screen.d.ts +0 -7
- package/dist/welcome-screen.js +60 -69
- package/package.json +1 -1
- package/packages/daemon/package.json +2 -2
- package/packages/mcp-server/package.json +2 -2
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.js +47 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +76 -9
- 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/__tests__/user-message-design.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.js +40 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts +0 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +30 -29
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js +10 -3
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +13 -13
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts +1 -3
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +58 -3
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts +2 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js +12 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +14 -41
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +0 -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 +86 -82
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.d.ts +35 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.js +152 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.d.ts +16 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.js +73 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +12 -8
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.js +17 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +105 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +27 -26
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +9 -6
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/assistant-message-design.test.ts +56 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +113 -9
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/user-message-design.test.ts +48 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.test.ts +10 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +43 -42
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +14 -14
- package/packages/pi-coding-agent/src/modes/interactive/components/bash-execution.ts +64 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/diff.ts +13 -7
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +15 -42
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +84 -104
- package/packages/pi-coding-agent/src/modes/interactive/components/transcript-design.ts +196 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tui-style-kit.ts +94 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +14 -9
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme-highlight.test.ts +23 -0
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +106 -1
- package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +27 -26
- package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +9 -6
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +14 -1
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
- package/packages/pi-tui/dist/overlay-layout.d.ts.map +1 -1
- package/packages/pi-tui/dist/overlay-layout.js +9 -6
- package/packages/pi-tui/dist/overlay-layout.js.map +1 -1
- package/packages/pi-tui/package.json +1 -1
- package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +20 -1
- package/packages/pi-tui/src/overlay-layout.ts +10 -7
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/packages/rpc-client/package.json +1 -1
- package/pkg/dist/modes/interactive/theme/theme-highlight.test.d.ts +2 -0
- package/pkg/dist/modes/interactive/theme/theme-highlight.test.d.ts.map +1 -0
- package/pkg/dist/modes/interactive/theme/theme-highlight.test.js +17 -0
- package/pkg/dist/modes/interactive/theme/theme-highlight.test.js.map +1 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme.js +105 -1
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
- package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/themes.js +27 -26
- package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto/loop-deps.ts +9 -5
- package/src/resources/extensions/gsd/auto/loop.ts +113 -9
- package/src/resources/extensions/gsd/auto/phases.ts +144 -19
- package/src/resources/extensions/gsd/auto/run-unit.ts +69 -4
- package/src/resources/extensions/gsd/auto/session.ts +10 -0
- package/src/resources/extensions/gsd/auto/verification-retry-policy.ts +82 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +230 -183
- package/src/resources/extensions/gsd/auto-dispatch.ts +15 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +7 -1
- package/src/resources/extensions/gsd/auto-recovery.ts +7 -209
- package/src/resources/extensions/gsd/auto-runtime-state.ts +5 -0
- package/src/resources/extensions/gsd/auto-start.ts +22 -22
- package/src/resources/extensions/gsd/auto-unit-closeout.ts +51 -0
- package/src/resources/extensions/gsd/auto-verification.ts +12 -6
- package/src/resources/extensions/gsd/auto-worktree.ts +8 -0
- package/src/resources/extensions/gsd/auto.ts +295 -75
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +21 -6
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +6 -2
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +5 -8
- package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +4 -10
- package/src/resources/extensions/gsd/commands/handlers/parallel.ts +12 -0
- package/src/resources/extensions/gsd/git-service.ts +2 -0
- package/src/resources/extensions/gsd/gsd-db.ts +7 -23
- package/src/resources/extensions/gsd/health-widget-core.ts +1 -1
- package/src/resources/extensions/gsd/health-widget.ts +6 -10
- package/src/resources/extensions/gsd/journal.ts +2 -0
- package/src/resources/extensions/gsd/markdown-renderer.ts +4 -95
- package/src/resources/extensions/gsd/native-git-bridge.ts +14 -13
- package/src/resources/extensions/gsd/notification-overlay.ts +50 -46
- package/src/resources/extensions/gsd/parallel-merge.ts +61 -34
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +33 -35
- package/src/resources/extensions/gsd/prompts/complete-slice.md +14 -12
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +20 -2
- package/src/resources/extensions/gsd/prompts/discuss.md +20 -2
- package/src/resources/extensions/gsd/recovery-classification.ts +18 -1
- package/src/resources/extensions/gsd/session-lock.ts +41 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +172 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/merge-state.ts +337 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +69 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +109 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/sketch-flag.ts +68 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +185 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/stale-worker.ts +46 -0
- package/src/resources/extensions/gsd/state-reconciliation/errors.ts +67 -0
- package/src/resources/extensions/gsd/state-reconciliation/index.ts +142 -0
- package/src/resources/extensions/gsd/state-reconciliation/registry.ts +27 -0
- package/src/resources/extensions/gsd/state-reconciliation/spawn-gate.ts +60 -0
- package/src/resources/extensions/gsd/state-reconciliation/types.ts +83 -0
- package/src/resources/extensions/gsd/state-reconciliation.ts +21 -53
- package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +654 -176
- package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +291 -4
- package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +16 -1
- package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/auto-unit-closeout.test.ts +68 -0
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +28 -1
- package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +20 -2
- package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/header-renderer.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +14 -4
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/integration/integration-proof.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +116 -24
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +46 -11
- package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +78 -41
- package/src/resources/extensions/gsd/tests/notifications-handler.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +12 -217
- package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +38 -6
- package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +24 -1
- package/src/resources/extensions/gsd/tests/resume-dispatch-worktree.test.ts +7 -3
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +6 -3
- package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +24 -0
- package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +65 -58
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +952 -0
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/tui-header-lifecycle.test.ts +121 -1
- package/src/resources/extensions/gsd/tests/tui-render-kit.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/verification-retry-policy.test.ts +83 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +6 -0
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +158 -58
- package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +572 -118
- package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +59 -2
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +18 -0
- package/src/resources/extensions/gsd/tui/render-kit.ts +109 -0
- package/src/resources/extensions/gsd/watch/header-renderer.ts +121 -79
- package/src/resources/extensions/gsd/watch/splash-palette.ts +11 -0
- package/src/resources/extensions/gsd/workflow-mcp.ts +2 -2
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +1151 -524
- package/src/resources/extensions/gsd/worktree-telemetry.ts +7 -2
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +0 -1544
- /package/dist/web/standalone/.next/static/{drLMkgfHQ8lzS229_HWYR → S44UQTFCUdA44dkjfYt6S}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{drLMkgfHQ8lzS229_HWYR → S44UQTFCUdA44dkjfYt6S}/_ssgManifest.js +0 -0
|
@@ -124,6 +124,8 @@ test("buildMinimalAutoGsdToolSet includes closeout tool for complete-slice", ()
|
|
|
124
124
|
"gsd_milestone_status",
|
|
125
125
|
"gsd_checkpoint_db",
|
|
126
126
|
"gsd_task_complete",
|
|
127
|
+
"gsd_task_reopen",
|
|
128
|
+
"gsd_replan_slice",
|
|
127
129
|
"gsd_slice_complete",
|
|
128
130
|
"gsd_complete_slice",
|
|
129
131
|
"memory_query",
|
|
@@ -131,6 +133,8 @@ test("buildMinimalAutoGsdToolSet includes closeout tool for complete-slice", ()
|
|
|
131
133
|
], "complete-slice");
|
|
132
134
|
|
|
133
135
|
assert.ok(result.includes("gsd_slice_complete"));
|
|
136
|
+
assert.ok(result.includes("gsd_task_reopen"));
|
|
137
|
+
assert.ok(result.includes("gsd_replan_slice"));
|
|
134
138
|
assert.ok(result.includes("subagent"));
|
|
135
139
|
assert.ok(result.includes("capture_thought"));
|
|
136
140
|
assert.ok(!result.includes("gsd_task_complete"));
|
|
@@ -10,8 +10,9 @@ import assert from "node:assert/strict";
|
|
|
10
10
|
import { mkdirSync, rmSync } from "node:fs";
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
import { tmpdir } from "node:os";
|
|
13
|
+
import { visibleWidth } from "@gsd/pi-tui";
|
|
13
14
|
|
|
14
|
-
import { updateProgressWidget } from "../auto-dashboard.ts";
|
|
15
|
+
import { setCompletionProgressWidget, updateProgressWidget } from "../auto-dashboard.ts";
|
|
15
16
|
import type { GSDState } from "../types.ts";
|
|
16
17
|
|
|
17
18
|
interface CapturedSetHeader {
|
|
@@ -46,6 +47,15 @@ const baseAccessors = {
|
|
|
46
47
|
getCurrentDispatchedModelId: () => null,
|
|
47
48
|
};
|
|
48
49
|
|
|
50
|
+
function assertLinesFit(lines: string[], width: number): void {
|
|
51
|
+
for (const line of lines) {
|
|
52
|
+
assert.ok(
|
|
53
|
+
visibleWidth(line) <= width,
|
|
54
|
+
`line exceeds width ${width}: ${visibleWidth(line)} "${line}"`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
49
59
|
// ── Header lifecycle ────────────────────────────────────────────────────
|
|
50
60
|
|
|
51
61
|
test("updateProgressWidget installs an EMPTY-rendering header (not undefined) — addresses codex P1 finding that setHeader(undefined) restores the built-in logo+instructions header", (t) => {
|
|
@@ -208,3 +218,113 @@ test("auto-dashboard widget render output omits Ctrl+N guidance when isStepMode
|
|
|
208
218
|
|
|
209
219
|
if (component.dispose) component.dispose();
|
|
210
220
|
});
|
|
221
|
+
|
|
222
|
+
test("auto-dashboard widget render output fits common terminal widths", (t) => {
|
|
223
|
+
const dir = makeTempDir("width-safe");
|
|
224
|
+
mkdirSync(join(dir, ".gsd"), { recursive: true });
|
|
225
|
+
t.after(() => cleanup(dir));
|
|
226
|
+
|
|
227
|
+
let widgetFactory: ((tui: unknown, theme: unknown) => any) | undefined;
|
|
228
|
+
|
|
229
|
+
updateProgressWidget(
|
|
230
|
+
{
|
|
231
|
+
hasUI: true,
|
|
232
|
+
ui: {
|
|
233
|
+
setWidget(_key: string, factory: any) { widgetFactory = factory; },
|
|
234
|
+
setHeader() {},
|
|
235
|
+
setStatus() {},
|
|
236
|
+
},
|
|
237
|
+
} as any,
|
|
238
|
+
"execute-task",
|
|
239
|
+
"M001/S01/T01",
|
|
240
|
+
baseState,
|
|
241
|
+
{ ...baseAccessors, getBasePath: () => dir },
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
assert.ok(widgetFactory);
|
|
245
|
+
|
|
246
|
+
const component = widgetFactory!(
|
|
247
|
+
{ requestRender() {} },
|
|
248
|
+
{
|
|
249
|
+
fg: (_color: string, text: string) => text,
|
|
250
|
+
bold: (text: string) => text,
|
|
251
|
+
},
|
|
252
|
+
);
|
|
253
|
+
t.after(() => component.dispose?.());
|
|
254
|
+
|
|
255
|
+
for (const width of [40, 80, 120]) {
|
|
256
|
+
assertLinesFit(component.render(width), width);
|
|
257
|
+
component.invalidate();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (component.dispose) component.dispose();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test("completion dashboard keeps final milestone roll-up in the progress widget", (t) => {
|
|
264
|
+
const dir = makeTempDir("completion-widget");
|
|
265
|
+
mkdirSync(join(dir, ".gsd"), { recursive: true });
|
|
266
|
+
t.after(() => cleanup(dir));
|
|
267
|
+
|
|
268
|
+
let widgetFactory: ((tui: unknown, theme: unknown) => any) | undefined;
|
|
269
|
+
|
|
270
|
+
setCompletionProgressWidget(
|
|
271
|
+
{
|
|
272
|
+
hasUI: true,
|
|
273
|
+
ui: {
|
|
274
|
+
setWidget(_key: string, factory: any) { widgetFactory = factory; },
|
|
275
|
+
setHeader() {},
|
|
276
|
+
setStatus() {},
|
|
277
|
+
},
|
|
278
|
+
} as any,
|
|
279
|
+
{
|
|
280
|
+
milestoneId: "M003",
|
|
281
|
+
milestoneTitle: "Budget tracking",
|
|
282
|
+
oneLiner: "Added milestone budget warning output and provider roll-up details.",
|
|
283
|
+
successCriteriaResults: "Budget warnings appear at the end of milestone completion.",
|
|
284
|
+
requirementOutcomes: "Users can see what shipped without opening a fresh session.",
|
|
285
|
+
keyFiles: ["src/resources/extensions/gsd/auto-dashboard.ts", "src/resources/extensions/gsd/auto.ts"],
|
|
286
|
+
keyDecisions: ["Keep completion closeout in the same TUI surface."],
|
|
287
|
+
followUps: "None.",
|
|
288
|
+
reason: "Milestone M003 complete",
|
|
289
|
+
startedAt: Date.now() - 90_000,
|
|
290
|
+
totalCost: 21.29,
|
|
291
|
+
totalTokens: 1_000_000,
|
|
292
|
+
unitCount: 8,
|
|
293
|
+
cacheHitRate: 100,
|
|
294
|
+
contextPercent: 0.9,
|
|
295
|
+
contextWindow: 1_000_000,
|
|
296
|
+
completedSlices: 3,
|
|
297
|
+
totalSlices: 3,
|
|
298
|
+
basePath: dir,
|
|
299
|
+
},
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
assert.ok(widgetFactory, "completion widget factory must be installed");
|
|
303
|
+
|
|
304
|
+
const fakeTui = { requestRender() {} };
|
|
305
|
+
const fakeTheme = {
|
|
306
|
+
fg: (_color: string, text: string) => text,
|
|
307
|
+
bold: (text: string) => text,
|
|
308
|
+
};
|
|
309
|
+
const component = widgetFactory!(fakeTui, fakeTheme);
|
|
310
|
+
const output = component.render(140).join("\n");
|
|
311
|
+
|
|
312
|
+
assert.match(output, /Milestone M003 roll-up/);
|
|
313
|
+
assert.match(output, /Budget tracking/);
|
|
314
|
+
assert.match(output, /Outcome/);
|
|
315
|
+
assert.match(output, /Added milestone budget warning output/);
|
|
316
|
+
assert.match(output, /What changed/);
|
|
317
|
+
assert.match(output, /Budget warnings appear/);
|
|
318
|
+
assert.match(output, /Users can see what shipped/);
|
|
319
|
+
assert.match(output, /Keep completion closeout/);
|
|
320
|
+
assert.match(output, /Verification/);
|
|
321
|
+
assert.match(output, /Files: src\/resources\/extensions\/gsd\/auto-dashboard\.ts/);
|
|
322
|
+
assert.match(output, /Run totals 3\/3 slices/);
|
|
323
|
+
assert.match(output, /100% cache hit/);
|
|
324
|
+
assert.match(output, /\$21\.29/);
|
|
325
|
+
assert.match(output, /1\.0M tokens/);
|
|
326
|
+
assert.match(output, /8 units/);
|
|
327
|
+
assert.doesNotMatch(output, /COMPLETE-MILESTONE/);
|
|
328
|
+
|
|
329
|
+
if (component.dispose) component.dispose();
|
|
330
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Project/App: GSD-2
|
|
2
|
+
// File Purpose: Unit tests for shared GSD TUI render helpers.
|
|
3
|
+
|
|
4
|
+
import { describe, test } from "node:test";
|
|
5
|
+
import assert from "node:assert/strict";
|
|
6
|
+
|
|
7
|
+
import { visibleWidth } from "@gsd/pi-tui";
|
|
8
|
+
import {
|
|
9
|
+
padRightVisible,
|
|
10
|
+
renderFrame,
|
|
11
|
+
renderKeyHints,
|
|
12
|
+
renderProgressBar,
|
|
13
|
+
rightAlign,
|
|
14
|
+
safeLine,
|
|
15
|
+
wrapVisibleText,
|
|
16
|
+
type ThemeLike,
|
|
17
|
+
} from "../tui/render-kit.ts";
|
|
18
|
+
|
|
19
|
+
const theme: ThemeLike = {
|
|
20
|
+
fg: (_color: string, text: string) => text,
|
|
21
|
+
bold: (text: string) => text,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function assertWidth(lines: string[], width: number): void {
|
|
25
|
+
for (const line of lines) {
|
|
26
|
+
assert.ok(
|
|
27
|
+
visibleWidth(line) <= width,
|
|
28
|
+
`line exceeds width ${width}: ${visibleWidth(line)} "${line}"`,
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
describe("tui render kit", () => {
|
|
34
|
+
test("safeLine clamps visible width", () => {
|
|
35
|
+
assert.equal(visibleWidth(safeLine("abcdef", 4)), 4);
|
|
36
|
+
assert.equal(safeLine("abcdef", 0), "");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("padRightVisible fills exact visible width", () => {
|
|
40
|
+
const line = padRightVisible("abc", 8);
|
|
41
|
+
assert.equal(visibleWidth(line), 8);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("rightAlign keeps output within width", () => {
|
|
45
|
+
for (const width of [10, 40, 80]) {
|
|
46
|
+
assertWidth([rightAlign("left side with overflow", "right side", width)], width);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("wrapVisibleText clamps long words and ansi-aware content", () => {
|
|
51
|
+
const lines = wrapVisibleText("https://example.com/" + "a".repeat(120), 24);
|
|
52
|
+
assert.ok(lines.length > 0);
|
|
53
|
+
assertWidth(lines, 24);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("renderFrame keeps borders and rows within width", () => {
|
|
57
|
+
for (const width of [3, 40, 80]) {
|
|
58
|
+
assertWidth(renderFrame(theme, ["row", "long ".repeat(40)], width), width);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("renderKeyHints and renderProgressBar fit caller budgets", () => {
|
|
63
|
+
assert.ok(visibleWidth(renderKeyHints(theme, ["↑↓ scroll", "esc close"], 12)) <= 12);
|
|
64
|
+
assert.equal(visibleWidth(renderProgressBar(theme, 2, 4, 16)), 16);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Project/App: GSD-2
|
|
2
|
+
// File Purpose: Unit tests for auto-mode verification retry backoff decisions.
|
|
3
|
+
|
|
4
|
+
import test from "node:test";
|
|
5
|
+
import assert from "node:assert/strict";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
decideVerificationRetry,
|
|
9
|
+
hashVerificationFailureContext,
|
|
10
|
+
verificationRetryDelayMs,
|
|
11
|
+
verificationRetryKey,
|
|
12
|
+
VERIFICATION_RETRY_MAX_DELAY_MS,
|
|
13
|
+
} from "../auto/verification-retry-policy.ts";
|
|
14
|
+
|
|
15
|
+
test("verificationRetryDelayMs uses exponential backoff with cap", () => {
|
|
16
|
+
assert.deepEqual(verificationRetryDelayMs(1, () => 0.5), {
|
|
17
|
+
delayMs: 2_000,
|
|
18
|
+
baseDelayMs: 2_000,
|
|
19
|
+
});
|
|
20
|
+
assert.deepEqual(verificationRetryDelayMs(2, () => 0.5), {
|
|
21
|
+
delayMs: 4_000,
|
|
22
|
+
baseDelayMs: 4_000,
|
|
23
|
+
});
|
|
24
|
+
assert.deepEqual(verificationRetryDelayMs(3, () => 0.5), {
|
|
25
|
+
delayMs: 8_000,
|
|
26
|
+
baseDelayMs: 8_000,
|
|
27
|
+
});
|
|
28
|
+
assert.deepEqual(verificationRetryDelayMs(99, () => 0.5), {
|
|
29
|
+
delayMs: VERIFICATION_RETRY_MAX_DELAY_MS,
|
|
30
|
+
baseDelayMs: VERIFICATION_RETRY_MAX_DELAY_MS,
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("decideVerificationRetry pauses on duplicate failure context", () => {
|
|
35
|
+
const failureContext = "lint failed\n";
|
|
36
|
+
const failureHash = hashVerificationFailureContext(failureContext);
|
|
37
|
+
const decision = decideVerificationRetry({
|
|
38
|
+
unitType: "execute-task",
|
|
39
|
+
retryInfo: {
|
|
40
|
+
unitId: "M001/S01/T01",
|
|
41
|
+
failureContext,
|
|
42
|
+
attempt: 2,
|
|
43
|
+
},
|
|
44
|
+
previousFailureHash: failureHash,
|
|
45
|
+
random: () => 0.5,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
assert.deepEqual(decision, {
|
|
49
|
+
action: "pause",
|
|
50
|
+
reason: "duplicate-failure-context",
|
|
51
|
+
key: verificationRetryKey("execute-task", "M001/S01/T01"),
|
|
52
|
+
failureHash,
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("decideVerificationRetry delays changed failure context", () => {
|
|
57
|
+
const decision = decideVerificationRetry({
|
|
58
|
+
unitType: "execute-task",
|
|
59
|
+
retryInfo: {
|
|
60
|
+
unitId: "M001/S01/T01",
|
|
61
|
+
failureContext: "test failed differently",
|
|
62
|
+
attempt: 2,
|
|
63
|
+
},
|
|
64
|
+
previousFailureHash: hashVerificationFailureContext("test failed"),
|
|
65
|
+
random: () => 0.5,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
assert.equal(decision.action, "delay");
|
|
69
|
+
assert.equal(decision.key, verificationRetryKey("execute-task", "M001/S01/T01"));
|
|
70
|
+
assert.equal(decision.delayMs, 4_000);
|
|
71
|
+
assert.equal(decision.baseDelayMs, 4_000);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("decideVerificationRetry pauses when retry context is missing", () => {
|
|
75
|
+
assert.deepEqual(
|
|
76
|
+
decideVerificationRetry({
|
|
77
|
+
unitType: "execute-task",
|
|
78
|
+
retryInfo: null,
|
|
79
|
+
previousFailureHash: undefined,
|
|
80
|
+
}),
|
|
81
|
+
{ action: "pause", reason: "missing-retry-context" },
|
|
82
|
+
);
|
|
83
|
+
});
|
|
@@ -35,6 +35,12 @@ test("auto execute-task requires canonical task completion tool", () => {
|
|
|
35
35
|
assert.deepEqual(getRequiredWorkflowToolsForAutoUnit("execute-task"), ["gsd_task_complete"]);
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
+
test("complete-slice requires closeout and execution handoff tools", () => {
|
|
39
|
+
const expected = ["gsd_slice_complete", "gsd_task_reopen", "gsd_replan_slice"];
|
|
40
|
+
assert.deepEqual(getRequiredWorkflowToolsForGuidedUnit("complete-slice"), expected);
|
|
41
|
+
assert.deepEqual(getRequiredWorkflowToolsForAutoUnit("complete-slice"), expected);
|
|
42
|
+
});
|
|
43
|
+
|
|
38
44
|
test("deep project setup units declare required workflow MCP tools", () => {
|
|
39
45
|
assert.deepEqual(getRequiredWorkflowToolsForGuidedUnit("discuss-project"), [
|
|
40
46
|
"ask_user_questions",
|
|
@@ -1,20 +1,57 @@
|
|
|
1
1
|
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
-
import { mkdtempSync, rmSync, readFileSync, readdirSync } from "node:fs";
|
|
3
|
+
import { mkdirSync, mkdtempSync, rmSync, readFileSync, readdirSync, realpathSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
|
+
import { execFileSync } from "node:child_process";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Initialize the temp dir as a real git repo with a `.gsd/preferences.md`
|
|
10
|
+
* declaring the requested isolation mode. Required after ADR-016 phase 2 /
|
|
11
|
+
* C1+C2+C3 inlined the worktree-manager + cache + preferences primitives —
|
|
12
|
+
* tests can no longer stub them via deps.
|
|
13
|
+
*/
|
|
14
|
+
function initGitRepoIn(base: string, isolation: "worktree" | "branch" | "none"): void {
|
|
15
|
+
const git = (args: string[]): void => {
|
|
16
|
+
execFileSync("git", args, { cwd: base, stdio: "pipe" });
|
|
17
|
+
};
|
|
18
|
+
git(["init", "-b", "main"]);
|
|
19
|
+
git(["config", "user.email", "test@test.com"]);
|
|
20
|
+
git(["config", "user.name", "Test"]);
|
|
21
|
+
writeFileSync(join(base, "README.md"), "# test\n");
|
|
22
|
+
writeFileSync(join(base, ".gitignore"), ".gsd/worktrees/\n");
|
|
23
|
+
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
24
|
+
writeFileSync(
|
|
25
|
+
join(base, ".gsd", "preferences.md"),
|
|
26
|
+
`## Git\n- isolation: ${isolation}\n`,
|
|
27
|
+
);
|
|
28
|
+
git(["add", "."]);
|
|
29
|
+
git(["commit", "-m", "init"]);
|
|
30
|
+
}
|
|
6
31
|
import {
|
|
7
32
|
WorktreeLifecycle,
|
|
8
33
|
type WorktreeLifecycleDeps,
|
|
9
34
|
type NotifyCtx,
|
|
10
35
|
} from "../worktree-lifecycle.js";
|
|
11
36
|
import { WorktreeStateProjection } from "../worktree-state-projection.js";
|
|
37
|
+
import { type TaskCommitContext } from "../worktree.js";
|
|
12
38
|
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
39
|
+
// ADR-016 phase 2 / C-track retired all worktree-manager + cache + prefs
|
|
40
|
+
// fields from `WorktreeLifecycleDeps`. Tests still pass them as overrides
|
|
41
|
+
// via the structural-typing escape hatch — listed here as optional so
|
|
42
|
+
// fixtures can stub or omit them.
|
|
17
43
|
type LegacyTestDeps = WorktreeLifecycleDeps & {
|
|
44
|
+
enterAutoWorktree?: (basePath: string, milestoneId: string) => string;
|
|
45
|
+
createAutoWorktree?: (basePath: string, milestoneId: string) => string;
|
|
46
|
+
enterBranchModeForMilestone?: (basePath: string, milestoneId: string) => void;
|
|
47
|
+
getAutoWorktreePath?: (basePath: string, milestoneId: string) => string | null;
|
|
48
|
+
isInAutoWorktree?: (basePath: string) => boolean;
|
|
49
|
+
autoWorktreeBranch?: (milestoneId: string) => string;
|
|
50
|
+
teardownAutoWorktree?: (
|
|
51
|
+
basePath: string,
|
|
52
|
+
milestoneId: string,
|
|
53
|
+
opts?: { preserveBranch?: boolean },
|
|
54
|
+
) => void;
|
|
18
55
|
shouldUseWorktreeIsolation?: () => boolean;
|
|
19
56
|
syncWorktreeStateBack?: (
|
|
20
57
|
mainBasePath: string,
|
|
@@ -22,6 +59,27 @@ type LegacyTestDeps = WorktreeLifecycleDeps & {
|
|
|
22
59
|
milestoneId: string,
|
|
23
60
|
) => { synced: string[] };
|
|
24
61
|
captureIntegrationBranch?: (basePath: string, mid: string | undefined) => void;
|
|
62
|
+
autoCommitCurrentBranch?: (
|
|
63
|
+
basePath: string,
|
|
64
|
+
unitType: string,
|
|
65
|
+
unitId: string,
|
|
66
|
+
taskContext?: TaskCommitContext,
|
|
67
|
+
) => string | null;
|
|
68
|
+
getCurrentBranch?: (basePath: string) => string;
|
|
69
|
+
checkoutBranch?: (basePath: string, branch: string) => void;
|
|
70
|
+
readFileSync?: (path: string, encoding: BufferEncoding) => string;
|
|
71
|
+
getIsolationMode?: (basePath?: string) => "worktree" | "branch" | "none";
|
|
72
|
+
resolveMilestoneFile?: (
|
|
73
|
+
basePath: string,
|
|
74
|
+
milestoneId: string,
|
|
75
|
+
fileType: string,
|
|
76
|
+
) => string | null;
|
|
77
|
+
GitServiceImpl?: new (basePath: string, gitConfig: unknown) => unknown;
|
|
78
|
+
loadEffectiveGSDPreferences?: () =>
|
|
79
|
+
| { preferences?: { git?: Record<string, unknown> } }
|
|
80
|
+
| null
|
|
81
|
+
| undefined;
|
|
82
|
+
invalidateAllCaches?: () => void;
|
|
25
83
|
};
|
|
26
84
|
import { AutoSession } from "../auto/session.js";
|
|
27
85
|
import type { JournalEntry } from "../journal.js";
|
|
@@ -40,33 +98,19 @@ function makeSession(
|
|
|
40
98
|
function makeDeps(
|
|
41
99
|
overrides?: Partial<LegacyTestDeps>,
|
|
42
100
|
): LegacyTestDeps {
|
|
101
|
+
// ADR-016 phase 2 / C-track retired the worktree-manager + cache + prefs
|
|
102
|
+
// primitives from `WorktreeLifecycleDeps`. Tests in this file drive
|
|
103
|
+
// Lifecycle against real git fixtures (initGitRepoIn) — do NOT stub the
|
|
104
|
+
// C-track primitives here, or the override pattern will pre-empt the
|
|
105
|
+
// real `getAutoWorktreePath` / `createAutoWorktree` / etc. and the
|
|
106
|
+
// success/existing/failure branches won't fire as expected.
|
|
43
107
|
const deps: LegacyTestDeps = {
|
|
44
|
-
isInAutoWorktree: () => false,
|
|
45
|
-
shouldUseWorktreeIsolation: () => true,
|
|
46
|
-
getIsolationMode: () => "worktree",
|
|
47
108
|
mergeMilestoneToMain: () => ({ pushed: false, codeFilesChanged: true }),
|
|
48
|
-
syncWorktreeStateBack: () => ({ synced: [] }),
|
|
49
|
-
teardownAutoWorktree: () => {},
|
|
50
|
-
createAutoWorktree: (_basePath: string, milestoneId: string) =>
|
|
51
|
-
`/project/.gsd/worktrees/${milestoneId}`,
|
|
52
|
-
enterAutoWorktree: (_basePath: string, milestoneId: string) =>
|
|
53
|
-
`/project/.gsd/worktrees/${milestoneId}`,
|
|
54
|
-
getAutoWorktreePath: () => null,
|
|
55
|
-
autoCommitCurrentBranch: () => {},
|
|
56
|
-
getCurrentBranch: () => "main",
|
|
57
|
-
checkoutBranch: () => {},
|
|
58
|
-
autoWorktreeBranch: (milestoneId: string) => `milestone/${milestoneId}`,
|
|
59
|
-
resolveMilestoneFile: (_basePath: string, milestoneId: string) =>
|
|
60
|
-
`/project/.gsd/milestones/${milestoneId}/${milestoneId}-ROADMAP.md`,
|
|
61
|
-
readFileSync: () => "# Roadmap\n- [x] S01: Slice one\n",
|
|
62
|
-
GitServiceImpl: class {
|
|
63
|
-
constructor() {}
|
|
64
|
-
} as unknown as LegacyTestDeps["GitServiceImpl"],
|
|
65
|
-
loadEffectiveGSDPreferences: () => ({ preferences: { git: {} } }),
|
|
66
|
-
invalidateAllCaches: () => {},
|
|
67
|
-
captureIntegrationBranch: () => {},
|
|
68
|
-
enterBranchModeForMilestone: () => {},
|
|
69
109
|
worktreeProjection: new WorktreeStateProjection(),
|
|
110
|
+
// ADR-016 phase 2 / C4 (#5627): GitServiceImpl constructor → factory.
|
|
111
|
+
gitServiceFactory: () => ({}) as unknown as ReturnType<
|
|
112
|
+
WorktreeLifecycleDeps["gitServiceFactory"]
|
|
113
|
+
>,
|
|
70
114
|
...overrides,
|
|
71
115
|
};
|
|
72
116
|
return deps;
|
|
@@ -104,7 +148,9 @@ describe("worktree journal events", () => {
|
|
|
104
148
|
const originalCwd = process.cwd();
|
|
105
149
|
|
|
106
150
|
beforeEach(() => {
|
|
107
|
-
|
|
151
|
+
// realpathSync to match what `auto-worktree.ts` returns from
|
|
152
|
+
// `resolveWorktreeProjectRoot` (macOS resolves `/var` → `/private/var`).
|
|
153
|
+
tmp = realpathSync(mkdtempSync(join(tmpdir(), "wt-journal-")));
|
|
108
154
|
});
|
|
109
155
|
afterEach(() => {
|
|
110
156
|
// Restore cwd before cleanup — on Windows, rmSync fails with EPERM
|
|
@@ -114,9 +160,17 @@ describe("worktree journal events", () => {
|
|
|
114
160
|
});
|
|
115
161
|
|
|
116
162
|
test("enterMilestone emits worktree-enter on success (new worktree)", () => {
|
|
163
|
+
initGitRepoIn(tmp, "worktree");
|
|
117
164
|
const s = makeSession({ basePath: tmp, originalBasePath: tmp });
|
|
118
|
-
const
|
|
119
|
-
|
|
165
|
+
const result = new WorktreeLifecycle(s, makeDeps()).enterMilestone(
|
|
166
|
+
"M001",
|
|
167
|
+
makeNotifyCtx(),
|
|
168
|
+
);
|
|
169
|
+
assert.equal(
|
|
170
|
+
result.ok,
|
|
171
|
+
true,
|
|
172
|
+
`enterMilestone failed: ${JSON.stringify(result)}`,
|
|
173
|
+
);
|
|
120
174
|
|
|
121
175
|
const entries = readJournalEntries(tmp);
|
|
122
176
|
const enter = entries.find(e => e.eventType === "worktree-enter");
|
|
@@ -127,11 +181,19 @@ describe("worktree journal events", () => {
|
|
|
127
181
|
});
|
|
128
182
|
|
|
129
183
|
test("enterMilestone emits worktree-enter with created=false for existing worktree", () => {
|
|
184
|
+
// Pre-create the worktree on disk so the second enter goes through the
|
|
185
|
+
// existing-worktree branch in `_enterMilestoneCore`.
|
|
186
|
+
initGitRepoIn(tmp, "worktree");
|
|
187
|
+
execFileSync("git", ["checkout", "-b", "milestone/M001"], { cwd: tmp, stdio: "pipe" });
|
|
188
|
+
execFileSync("git", ["checkout", "main"], { cwd: tmp, stdio: "pipe" });
|
|
189
|
+
execFileSync(
|
|
190
|
+
"git",
|
|
191
|
+
["worktree", "add", join(tmp, ".gsd", "worktrees", "M001"), "milestone/M001"],
|
|
192
|
+
{ cwd: tmp, stdio: "pipe" },
|
|
193
|
+
);
|
|
194
|
+
|
|
130
195
|
const s = makeSession({ basePath: tmp, originalBasePath: tmp });
|
|
131
|
-
|
|
132
|
-
getAutoWorktreePath: () => "/project/.gsd/worktrees/M001",
|
|
133
|
-
});
|
|
134
|
-
new WorktreeLifecycle(s, deps).enterMilestone("M001", makeNotifyCtx());
|
|
196
|
+
new WorktreeLifecycle(s, makeDeps()).enterMilestone("M001", makeNotifyCtx());
|
|
135
197
|
|
|
136
198
|
const entries = readJournalEntries(tmp);
|
|
137
199
|
const enter = entries.find(e => e.eventType === "worktree-enter");
|
|
@@ -140,9 +202,9 @@ describe("worktree journal events", () => {
|
|
|
140
202
|
});
|
|
141
203
|
|
|
142
204
|
test("enterMilestone emits worktree-skip when isolation disabled", () => {
|
|
205
|
+
initGitRepoIn(tmp, "none");
|
|
143
206
|
const s = makeSession({ basePath: tmp, originalBasePath: tmp });
|
|
144
|
-
|
|
145
|
-
new WorktreeLifecycle(s, deps).enterMilestone("M001", makeNotifyCtx());
|
|
207
|
+
new WorktreeLifecycle(s, makeDeps()).enterMilestone("M001", makeNotifyCtx());
|
|
146
208
|
|
|
147
209
|
const entries = readJournalEntries(tmp);
|
|
148
210
|
const skip = entries.find(e => e.eventType === "worktree-skip");
|
|
@@ -152,30 +214,31 @@ describe("worktree journal events", () => {
|
|
|
152
214
|
});
|
|
153
215
|
|
|
154
216
|
test("enterMilestone emits worktree-create-failed on error", () => {
|
|
217
|
+
// Real fixture with isolation:worktree, then delete .git to force the
|
|
218
|
+
// real createAutoWorktree to throw.
|
|
219
|
+
initGitRepoIn(tmp, "worktree");
|
|
220
|
+
rmSync(join(tmp, ".git"), { recursive: true, force: true });
|
|
155
221
|
const s = makeSession({ basePath: tmp, originalBasePath: tmp });
|
|
156
|
-
|
|
157
|
-
getAutoWorktreePath: () => null,
|
|
158
|
-
createAutoWorktree: () => { throw new Error("disk full"); },
|
|
159
|
-
});
|
|
160
|
-
new WorktreeLifecycle(s, deps).enterMilestone("M001", makeNotifyCtx());
|
|
222
|
+
new WorktreeLifecycle(s, makeDeps()).enterMilestone("M001", makeNotifyCtx());
|
|
161
223
|
|
|
162
224
|
const entries = readJournalEntries(tmp);
|
|
163
225
|
const failed = entries.find(e => e.eventType === "worktree-create-failed");
|
|
164
226
|
assert.ok(failed, "worktree-create-failed event should be emitted");
|
|
165
227
|
assert.equal(failed!.data?.milestoneId, "M001");
|
|
166
|
-
assert.
|
|
228
|
+
assert.ok(failed!.data?.error, "error message should be present");
|
|
167
229
|
assert.equal(failed!.data?.fallback, "project-root");
|
|
168
230
|
});
|
|
169
231
|
|
|
170
232
|
test("mergeAndExit emits worktree-merge-start", () => {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
233
|
+
initGitRepoIn(tmp, "worktree");
|
|
234
|
+
execFileSync("git", ["checkout", "-b", "milestone/M001"], { cwd: tmp, stdio: "pipe" });
|
|
235
|
+
execFileSync("git", ["checkout", "main"], { cwd: tmp, stdio: "pipe" });
|
|
236
|
+
const wt = join(tmp, ".gsd", "worktrees", "M001");
|
|
237
|
+
execFileSync("git", ["worktree", "add", wt, "milestone/M001"], { cwd: tmp, stdio: "pipe" });
|
|
238
|
+
|
|
239
|
+
const s = makeSession({ basePath: wt, originalBasePath: tmp });
|
|
240
|
+
const deps = makeDeps();
|
|
241
|
+
process.chdir(wt);
|
|
179
242
|
new WorktreeLifecycle(s, deps).exitMilestone(
|
|
180
243
|
"M001",
|
|
181
244
|
{ merge: true },
|
|
@@ -189,14 +252,51 @@ describe("worktree journal events", () => {
|
|
|
189
252
|
assert.equal(start!.data?.mode, "worktree");
|
|
190
253
|
});
|
|
191
254
|
|
|
192
|
-
test("
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
255
|
+
test("exitMilestone propagates codeFilesChanged from merge result", () => {
|
|
256
|
+
initGitRepoIn(tmp, "worktree");
|
|
257
|
+
execFileSync("git", ["checkout", "-b", "milestone/M001"], { cwd: tmp, stdio: "pipe" });
|
|
258
|
+
execFileSync("git", ["checkout", "main"], { cwd: tmp, stdio: "pipe" });
|
|
259
|
+
const wt = join(tmp, ".gsd", "worktrees", "M001");
|
|
260
|
+
execFileSync("git", ["worktree", "add", wt, "milestone/M001"], { cwd: tmp, stdio: "pipe" });
|
|
261
|
+
mkdirSync(join(tmp, ".gsd", "milestones", "M001"), { recursive: true });
|
|
262
|
+
writeFileSync(
|
|
263
|
+
join(tmp, ".gsd", "milestones", "M001", "M001-ROADMAP.md"),
|
|
264
|
+
"# M001\n- [x] S01: Slice one\n",
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
const s = makeSession({ basePath: wt, originalBasePath: tmp });
|
|
268
|
+
const deps = makeDeps({
|
|
269
|
+
mergeMilestoneToMain: () => ({ pushed: false, codeFilesChanged: true }),
|
|
270
|
+
});
|
|
271
|
+
process.chdir(wt);
|
|
272
|
+
|
|
273
|
+
const result = new WorktreeLifecycle(s, deps).exitMilestone(
|
|
274
|
+
"M001",
|
|
275
|
+
{ merge: true },
|
|
276
|
+
makeNotifyCtx(),
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
assert.deepEqual(result, {
|
|
280
|
+
ok: true,
|
|
281
|
+
merged: true,
|
|
282
|
+
codeFilesChanged: true,
|
|
196
283
|
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
test("mergeAndExit emits worktree-merge-failed on error", () => {
|
|
287
|
+
initGitRepoIn(tmp, "worktree");
|
|
288
|
+
execFileSync("git", ["checkout", "-b", "milestone/M001"], { cwd: tmp, stdio: "pipe" });
|
|
289
|
+
execFileSync("git", ["checkout", "main"], { cwd: tmp, stdio: "pipe" });
|
|
290
|
+
const wt = join(tmp, ".gsd", "worktrees", "M001");
|
|
291
|
+
execFileSync("git", ["worktree", "add", wt, "milestone/M001"], { cwd: tmp, stdio: "pipe" });
|
|
292
|
+
mkdirSync(join(tmp, ".gsd", "milestones", "M001"), { recursive: true });
|
|
293
|
+
writeFileSync(
|
|
294
|
+
join(tmp, ".gsd", "milestones", "M001", "M001-ROADMAP.md"),
|
|
295
|
+
"# M001\n- [x] S01: Slice one\n",
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
const s = makeSession({ basePath: wt, originalBasePath: tmp });
|
|
197
299
|
const deps = makeDeps({
|
|
198
|
-
isInAutoWorktree: () => true,
|
|
199
|
-
getIsolationMode: () => "worktree",
|
|
200
300
|
mergeMilestoneToMain: () => { throw new Error("conflict in main"); },
|
|
201
301
|
});
|
|
202
302
|
// Since #4380, mergeAndExit re-throws all errors after emitting the journal
|