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
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Full-screen TUI overlay showing real-time parallel worker progress.
|
|
5
|
-
* Opened via `/gsd parallel watch`, Ctrl+Alt+P (⌃⌥P on macOS),
|
|
6
|
-
* or Ctrl+Shift+P fallback.
|
|
7
|
-
* Reads the same data sources as `scripts/parallel-monitor.mjs` but
|
|
8
|
-
* renders as a native pi-tui overlay with theme integration.
|
|
9
|
-
*/
|
|
1
|
+
// Project/App: GSD-2
|
|
2
|
+
// File Purpose: Parallel worker monitor overlay with width-safe operations-console rendering.
|
|
10
3
|
import { existsSync, statSync, readFileSync, openSync, readSync, closeSync, readdirSync } from "node:fs";
|
|
11
4
|
import { join } from "node:path";
|
|
12
5
|
import { spawnSync } from "node:child_process";
|
|
@@ -14,6 +7,7 @@ import { matchesKey, Key } from "@gsd/pi-tui";
|
|
|
14
7
|
import { formatDuration } from "../shared/mod.js";
|
|
15
8
|
import { formattedShortcutPair } from "./shortcut-defs.js";
|
|
16
9
|
import { resolveGsdPathContract } from "./paths.js";
|
|
10
|
+
import { renderBar, renderKeyHints, renderProgressBar, safeLine, statusGlyph, } from "./tui/render-kit.js";
|
|
17
11
|
// ─── Data Helpers ─────────────────────────────────────────────────────────
|
|
18
12
|
function readJsonSafe(filePath) {
|
|
19
13
|
try {
|
|
@@ -226,17 +220,6 @@ function unitTypeLabel(unitType) {
|
|
|
226
220
|
};
|
|
227
221
|
return labels[unitType || ""] || (unitType || "---").toUpperCase().slice(0, 5);
|
|
228
222
|
}
|
|
229
|
-
function progressBar(done, total, width) {
|
|
230
|
-
if (total === 0)
|
|
231
|
-
return "░".repeat(width);
|
|
232
|
-
const filled = Math.round((done / total) * width);
|
|
233
|
-
return "█".repeat(filled) + "░".repeat(width - filled);
|
|
234
|
-
}
|
|
235
|
-
function healthGlyph(alive, heartbeatAge) {
|
|
236
|
-
if (!alive)
|
|
237
|
-
return "○";
|
|
238
|
-
return "●";
|
|
239
|
-
}
|
|
240
223
|
// ─── Overlay Class ────────────────────────────────────────────────────────
|
|
241
224
|
export class ParallelMonitorOverlay {
|
|
242
225
|
tui;
|
|
@@ -247,6 +230,7 @@ export class ParallelMonitorOverlay {
|
|
|
247
230
|
workers = [];
|
|
248
231
|
events = [];
|
|
249
232
|
cachedLines;
|
|
233
|
+
cachedWidth;
|
|
250
234
|
scrollOffset = 0;
|
|
251
235
|
disposed = false;
|
|
252
236
|
resizeHandler = null;
|
|
@@ -279,6 +263,7 @@ export class ParallelMonitorOverlay {
|
|
|
279
263
|
}
|
|
280
264
|
this.events = this.events.slice(-10);
|
|
281
265
|
this.cachedLines = undefined;
|
|
266
|
+
this.cachedWidth = undefined;
|
|
282
267
|
this.tui.requestRender();
|
|
283
268
|
}
|
|
284
269
|
dispose() {
|
|
@@ -313,13 +298,14 @@ export class ParallelMonitorOverlay {
|
|
|
313
298
|
}
|
|
314
299
|
invalidate() {
|
|
315
300
|
this.cachedLines = undefined;
|
|
301
|
+
this.cachedWidth = undefined;
|
|
316
302
|
}
|
|
317
303
|
render(width) {
|
|
318
|
-
if (this.cachedLines)
|
|
304
|
+
if (this.cachedLines && this.cachedWidth === width)
|
|
319
305
|
return this.cachedLines;
|
|
320
306
|
const t = this.theme;
|
|
321
307
|
const lines = [];
|
|
322
|
-
const w = Math.max(
|
|
308
|
+
const w = Math.max(1, width);
|
|
323
309
|
// Header
|
|
324
310
|
const totalCost = this.workers.reduce((s, wk) => s + wk.cost, 0);
|
|
325
311
|
const aliveCount = this.workers.filter((wk) => wk.alive).length;
|
|
@@ -328,7 +314,7 @@ export class ParallelMonitorOverlay {
|
|
|
328
314
|
lines.push(t.fg("muted", ` ${now} │ ${aliveCount}/${this.workers.length} alive │ Total: `) +
|
|
329
315
|
t.bold(`$${totalCost.toFixed(2)}`) +
|
|
330
316
|
t.fg("muted", " │ 5s refresh"));
|
|
331
|
-
lines.push(t
|
|
317
|
+
lines.push(renderBar(t, w));
|
|
332
318
|
if (this.workers.length === 0) {
|
|
333
319
|
lines.push("");
|
|
334
320
|
lines.push(t.fg("warning", " No parallel workers found."));
|
|
@@ -339,7 +325,7 @@ export class ParallelMonitorOverlay {
|
|
|
339
325
|
lines.push("");
|
|
340
326
|
// Health + ID + state
|
|
341
327
|
const healthColor = wk.alive ? "success" : "error";
|
|
342
|
-
const glyph =
|
|
328
|
+
const glyph = statusGlyph(t, wk.alive ? "active" : "idle");
|
|
343
329
|
const stateText = wk.alive
|
|
344
330
|
? t.fg("success", "RUNNING")
|
|
345
331
|
: t.fg("error", t.bold("DEAD"));
|
|
@@ -374,21 +360,25 @@ export class ParallelMonitorOverlay {
|
|
|
374
360
|
});
|
|
375
361
|
lines.push(` ${t.fg("muted", "slices")} ${chips.join(" ")}`);
|
|
376
362
|
// Task progress bar
|
|
377
|
-
const
|
|
363
|
+
const barWidth = Math.max(6, Math.min(25, w - 32));
|
|
364
|
+
const bar = renderProgressBar(t, wk.doneTasks, wk.totalTasks, barWidth, {
|
|
365
|
+
filledChar: "█",
|
|
366
|
+
emptyChar: "░",
|
|
367
|
+
emptyColor: "dim",
|
|
368
|
+
});
|
|
378
369
|
const pct = wk.totalTasks > 0 ? Math.round((wk.doneTasks / wk.totalTasks) * 100) : 0;
|
|
379
|
-
lines.push(` ${t.fg("muted", "tasks")} ${
|
|
370
|
+
lines.push(` ${t.fg("muted", "tasks")} ${bar} ${wk.doneTasks}/${wk.totalTasks} ` +
|
|
380
371
|
t.fg("muted", `(${pct}%) │ slices done ${wk.doneSlices}/${wk.totalSlices}`));
|
|
381
372
|
}
|
|
382
373
|
// Errors
|
|
383
374
|
for (const err of wk.errors.slice(-2)) {
|
|
384
|
-
|
|
385
|
-
lines.push(` ${t.fg("error", "⚠ " + truncated)}`);
|
|
375
|
+
lines.push(` ${t.fg("error", "! " + err)}`);
|
|
386
376
|
}
|
|
387
377
|
}
|
|
388
378
|
}
|
|
389
379
|
// Event feed
|
|
390
380
|
lines.push("");
|
|
391
|
-
lines.push(t
|
|
381
|
+
lines.push(renderBar(t, w));
|
|
392
382
|
lines.push(` ${t.bold("Recent Events")}`);
|
|
393
383
|
if (this.events.length === 0) {
|
|
394
384
|
lines.push(t.fg("muted", " No events yet..."));
|
|
@@ -396,8 +386,7 @@ export class ParallelMonitorOverlay {
|
|
|
396
386
|
else {
|
|
397
387
|
for (const evt of this.events.slice(-8)) {
|
|
398
388
|
const mid = evt.match(/^✓ (M\d+)\//)?.[1] || "";
|
|
399
|
-
|
|
400
|
-
lines.push(` ${t.fg("muted", "│")} ${t.fg("accent", mid)} ${truncated.replace(/^✓ M\d+\//, "")}`);
|
|
389
|
+
lines.push(` ${t.fg("muted", "│")} ${t.fg("accent", mid)} ${evt.replace(/^✓ M\d+\//, "")}`);
|
|
401
390
|
}
|
|
402
391
|
}
|
|
403
392
|
// Footer
|
|
@@ -411,13 +400,16 @@ export class ParallelMonitorOverlay {
|
|
|
411
400
|
}
|
|
412
401
|
lines.push(` ${t.bold("Total: $" + this.workers.reduce((s, wk) => s + wk.cost, 0).toFixed(2))}`);
|
|
413
402
|
}
|
|
414
|
-
lines.push(t
|
|
403
|
+
lines.push(renderKeyHints(t, [`ESC/q/${formattedShortcutPair("parallel")} close`, "↑↓ scroll"], w));
|
|
415
404
|
// Apply scroll — use terminal rows as height estimate
|
|
416
405
|
const termHeight = process.stdout.rows || 40;
|
|
417
406
|
const maxScroll = Math.max(0, lines.length - termHeight);
|
|
418
407
|
this.scrollOffset = Math.min(Math.max(this.scrollOffset, 0), maxScroll);
|
|
419
|
-
const visible = lines
|
|
408
|
+
const visible = lines
|
|
409
|
+
.slice(this.scrollOffset, this.scrollOffset + termHeight)
|
|
410
|
+
.map((line) => safeLine(line, w));
|
|
420
411
|
this.cachedLines = visible;
|
|
412
|
+
this.cachedWidth = width;
|
|
421
413
|
return visible;
|
|
422
414
|
}
|
|
423
415
|
}
|
|
@@ -22,22 +22,24 @@ Use `subagent` only for fresh-context review when useful: reviewer for cross-cut
|
|
|
22
22
|
|
|
23
23
|
1. Use the inlined Slice Summary and UAT templates.
|
|
24
24
|
2. {{skillActivation}}
|
|
25
|
-
3. Run all slice-level verification checks from the slice plan
|
|
26
|
-
4.
|
|
27
|
-
5.
|
|
28
|
-
6.
|
|
29
|
-
7. If
|
|
30
|
-
8.
|
|
31
|
-
9.
|
|
32
|
-
10.
|
|
33
|
-
11.
|
|
34
|
-
12.
|
|
35
|
-
13.
|
|
25
|
+
3. Run all slice-level verification checks from the slice plan through the closeout-safe verification surface (`gsd_exec` / Context Mode verification evidence); refresh current state if needed. Do not use direct `bash` for verification commands.
|
|
26
|
+
4. Complete the slice only when every required verification check passes. If verification fails or the fix requires source changes, do **not** edit source files in this unit and do **not** call `gsd_slice_complete`.
|
|
27
|
+
5. For task-specific failures, call `gsd_task_reopen` with the failing completed task and a concrete reason so execution can redo the work. For plan-invalidating failures, call `gsd_replan_slice` with the blocker and updated execution tasks. Then stop with: "Slice {{sliceId}} needs execution follow-up."
|
|
28
|
+
6. Task summaries use a flat file layout under `tasks/` such as `T01-SUMMARY.md`, not inside per-task subdirectories like `tasks/T01/SUMMARY.md`. Never use `tasks/*/SUMMARY.md`.
|
|
29
|
+
7. If observability/diagnostics were planned, verify them unless the slice is simple.
|
|
30
|
+
8. Address every gate in Gates to Close. Q8 maps to **Operational Readiness**: health signal, failure signal, recovery procedure, monitoring gaps. Empty sections are recorded as omitted.
|
|
31
|
+
9. If requirement status changed, call `gsd_requirement_update`; do not write `.gsd/REQUIREMENTS.md` directly.
|
|
32
|
+
10. Prepare `gsd_slice_complete` content with camelCase fields `milestoneId`, `sliceId`, `sliceTitle`, `oneLiner`, `narrative`, `verification`, and `uatContent`.
|
|
33
|
+
11. Draft concrete UAT with preconditions, numbered steps, expected outcomes, edge cases, UAT Type, and Not Proven By This UAT.
|
|
34
|
+
12. Review the inlined task-summary excerpts for DECISIONS.md and KNOWLEDGE.md-worthy decisions, patterns, and gotchas. Read full `*-SUMMARY.md` files only when an excerpt is absent, truncated, or lacks the specific evidence needed for the slice narrative. Capture significant items with `capture_thought`; do not append knowledge files directly.
|
|
35
|
+
13. When verification passes, call `gsd_slice_complete`. The DB-backed tool is the canonical write path. Do **not** manually write `{{sliceSummaryPath}}`. Do **not** manually write `{{sliceUatPath}}`. Do not edit roadmap checkboxes; the tool renders files and updates projections.
|
|
36
|
+
14. Do not run git commands.
|
|
37
|
+
15. Update `.gsd/PROJECT.md` with a full `write` only if the current project state needs refresh.
|
|
36
38
|
|
|
37
39
|
**Autonomous execution:** no human is available. Do not call `ask_user_questions` or `secure_env_collect`; make reasonable assumptions and document them.
|
|
38
40
|
|
|
39
41
|
**File system safety:** if re-reading task summaries, use `find .gsd/milestones/{{milestoneId}}/slices/{{sliceId}}/tasks -name "*-SUMMARY.md"` or `ls .gsd/milestones/{{milestoneId}}/slices/{{sliceId}}/tasks/*-SUMMARY.md`. Never pass `{{slicePath}}` or any directory path directly to the `read` tool.
|
|
40
42
|
|
|
41
|
-
**You MUST call `gsd_slice_complete` with summary and UAT content before finishing.**
|
|
43
|
+
**You MUST call `gsd_slice_complete` with summary and UAT content before finishing only after verification passes.**
|
|
42
44
|
|
|
43
45
|
When done, say: "Slice {{sliceId}} complete."
|
|
@@ -129,7 +129,16 @@ If ANY box is unchecked, **STOP**. Do NOT emit the ready phrase. Emit the missin
|
|
|
129
129
|
|
|
130
130
|
Do not announce the ready phrase as something you are "about to" do. The ready phrase is a post-write signal, not an intent signal.
|
|
131
131
|
|
|
132
|
-
After completing steps 1–7 above,
|
|
132
|
+
After completing steps 1–7 above, end with this concise user-facing handoff:
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
Milestone {{milestoneId}} ready.
|
|
136
|
+
|
|
137
|
+
Next steps:
|
|
138
|
+
- Run `/gsd auto` to start execution if it does not begin automatically.
|
|
139
|
+
- Use `/gsd status` or `/gsd visualize` to inspect the roadmap.
|
|
140
|
+
- Use `/gsd notifications` to review or configure delivery alerts.
|
|
141
|
+
```
|
|
133
142
|
|
|
134
143
|
### Multi-Milestone
|
|
135
144
|
|
|
@@ -215,7 +224,16 @@ If ANY box is unchecked, **STOP**. Do NOT emit the ready phrase. Emit the missin
|
|
|
215
224
|
|
|
216
225
|
Do not announce the ready phrase as something you are "about to" do. The ready phrase is a post-write signal, not an intent signal.
|
|
217
226
|
|
|
218
|
-
After completing every step above,
|
|
227
|
+
After completing every step above, end with this concise user-facing handoff:
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
Milestone {{milestoneId}} ready.
|
|
231
|
+
|
|
232
|
+
Next steps:
|
|
233
|
+
- Run `/gsd auto` to start execution if it does not begin automatically.
|
|
234
|
+
- Use `/gsd status` or `/gsd visualize` to inspect the roadmap.
|
|
235
|
+
- Use `/gsd notifications` to review or configure delivery alerts.
|
|
236
|
+
```
|
|
219
237
|
|
|
220
238
|
## Critical Rules
|
|
221
239
|
|
|
@@ -252,7 +252,16 @@ If ANY box is unchecked, **STOP**. Do NOT emit the ready phrase. Emit the missin
|
|
|
252
252
|
|
|
253
253
|
Do not announce the ready phrase as something you are "about to" do. It is a post-write signal, not intent.
|
|
254
254
|
|
|
255
|
-
After completing steps 1–7 above,
|
|
255
|
+
After completing steps 1–7 above, end with this concise user-facing handoff:
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
Milestone {{milestoneId}} ready.
|
|
259
|
+
|
|
260
|
+
Next steps:
|
|
261
|
+
- Run `/gsd auto` to start execution if it does not begin automatically.
|
|
262
|
+
- Use `/gsd status` or `/gsd visualize` to inspect the roadmap.
|
|
263
|
+
- Use `/gsd notifications` to review or configure delivery alerts.
|
|
264
|
+
```
|
|
256
265
|
|
|
257
266
|
### Multi-Milestone
|
|
258
267
|
|
|
@@ -345,6 +354,15 @@ If ANY box is unchecked, **STOP**. Do NOT emit the ready phrase. Emit the missin
|
|
|
345
354
|
|
|
346
355
|
Do not announce the ready phrase as something you are "about to" do. It is a post-write signal, not intent.
|
|
347
356
|
|
|
348
|
-
After completing all phases above,
|
|
357
|
+
After completing all phases above, end with this concise user-facing handoff:
|
|
358
|
+
|
|
359
|
+
```
|
|
360
|
+
Milestone M001 ready.
|
|
361
|
+
|
|
362
|
+
Next steps:
|
|
363
|
+
- Run `/gsd auto` to start execution if it does not begin automatically.
|
|
364
|
+
- Use `/gsd status` or `/gsd visualize` to inspect the roadmap.
|
|
365
|
+
- Use `/gsd notifications` to review or configure delivery alerts.
|
|
366
|
+
```
|
|
349
367
|
|
|
350
368
|
{{inlinedTemplates}}
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
// Project/App: GSD-2
|
|
2
2
|
// File Purpose: ADR-015 Recovery Classification module for runtime failure taxonomy.
|
|
3
3
|
import { classifyError, isTransient } from "./error-classifier.js";
|
|
4
|
+
import { ReconciliationFailedError } from "./state-reconciliation.js";
|
|
4
5
|
export function classifyFailure(input) {
|
|
5
6
|
const message = errorMessage(input.error);
|
|
6
|
-
|
|
7
|
+
// ADR-017: ReconciliationFailedError is a typed throw from the State
|
|
8
|
+
// Reconciliation Module. Recognize it by class regardless of caller-supplied
|
|
9
|
+
// failureKind so the taxonomy stays consistent.
|
|
10
|
+
const failureKind = input.error instanceof ReconciliationFailedError
|
|
11
|
+
? "reconciliation-drift"
|
|
12
|
+
: input.failureKind ?? inferFailureKind(message);
|
|
7
13
|
switch (failureKind) {
|
|
8
14
|
case "tool-schema":
|
|
9
15
|
return {
|
|
@@ -45,6 +51,14 @@ export function classifyFailure(input) {
|
|
|
45
51
|
exitReason: "verification-drift",
|
|
46
52
|
remediation: "Inspect the verification artifact and reconcile the state snapshot before resuming.",
|
|
47
53
|
};
|
|
54
|
+
case "reconciliation-drift":
|
|
55
|
+
return {
|
|
56
|
+
failureKind,
|
|
57
|
+
action: "escalate",
|
|
58
|
+
reason: `Reconciliation drift${unitSuffix(input)}: ${message}`,
|
|
59
|
+
exitReason: "reconciliation-drift",
|
|
60
|
+
remediation: "Inspect the persistent or repair-failed drift kinds reported by the State Reconciliation Module before resuming.",
|
|
61
|
+
};
|
|
48
62
|
case "provider": {
|
|
49
63
|
const providerClass = classifyError(message, input.retryAfterMs);
|
|
50
64
|
return {
|
|
@@ -521,6 +521,46 @@ export function readSessionLockData(basePath) {
|
|
|
521
521
|
export function isSessionLockProcessAlive(data) {
|
|
522
522
|
return isPidAlive(data.pid);
|
|
523
523
|
}
|
|
524
|
+
/**
|
|
525
|
+
* ADR-017 raw primitive: remove orphaned lock artifacts (lock dir + lock file)
|
|
526
|
+
* when the recorded PID is dead or no metadata is present. Mirrors the
|
|
527
|
+
* pre-flight cleanup logic in acquireSessionLock so the stale-worker drift
|
|
528
|
+
* handler can clear the orphan proactively without going through the full
|
|
529
|
+
* acquire path. No-op when the lock is held by an alive process.
|
|
530
|
+
*
|
|
531
|
+
* Returns true when artifacts were removed (drift was present).
|
|
532
|
+
*/
|
|
533
|
+
export function removeStaleSessionLock(basePath) {
|
|
534
|
+
const lp = lockPath(basePath);
|
|
535
|
+
const gsdDir = gsdRoot(basePath);
|
|
536
|
+
const lockTarget = effectiveLockTarget(gsdDir);
|
|
537
|
+
const lockDir = lockTarget + ".lock";
|
|
538
|
+
const existingData = readExistingLockData(lp);
|
|
539
|
+
const isOrphan = !existingData ||
|
|
540
|
+
(typeof existingData.pid === "number" && !isPidAlive(existingData.pid));
|
|
541
|
+
if (!isOrphan)
|
|
542
|
+
return false;
|
|
543
|
+
let removed = false;
|
|
544
|
+
if (existsSync(lockDir)) {
|
|
545
|
+
try {
|
|
546
|
+
rmSync(lockDir, { recursive: true, force: true });
|
|
547
|
+
removed = true;
|
|
548
|
+
}
|
|
549
|
+
catch {
|
|
550
|
+
/* best-effort */
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
if (existsSync(lp)) {
|
|
554
|
+
try {
|
|
555
|
+
unlinkSync(lp);
|
|
556
|
+
removed = true;
|
|
557
|
+
}
|
|
558
|
+
catch {
|
|
559
|
+
/* best-effort */
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
return removed;
|
|
563
|
+
}
|
|
524
564
|
/**
|
|
525
565
|
* Returns true if we currently hold a session lock for the given path.
|
|
526
566
|
*/
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// Project/App: GSD-2
|
|
2
|
+
// File Purpose: ADR-017 missing-completion-timestamp drift handler. Detects
|
|
3
|
+
// tasks/slices/milestones marked complete (status = 'complete' | 'done') in
|
|
4
|
+
// the DB but whose `completed_at` column is null, and where the on-disk
|
|
5
|
+
// SUMMARY.md attests to completion. Backfills `completed_at` from the
|
|
6
|
+
// SUMMARY.md mtime — deterministic and idempotent (re-running yields the
|
|
7
|
+
// same value).
|
|
8
|
+
import { existsSync, statSync } from "node:fs";
|
|
9
|
+
import { getMilestone, getMilestoneSlices, getSliceTasks, isDbAvailable, updateMilestoneStatus, updateSliceStatus, updateTaskStatus, } from "../../gsd-db.js";
|
|
10
|
+
import { resolveMilestoneFile, resolveSliceFile, resolveTaskFile, } from "../../paths.js";
|
|
11
|
+
const COMPLETE_STATUSES = new Set(["complete", "done"]);
|
|
12
|
+
function summaryMtimeIso(path) {
|
|
13
|
+
try {
|
|
14
|
+
return statSync(path).mtime.toISOString();
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function detectMissingCompletionTimestampDrift(state, ctx) {
|
|
21
|
+
if (!isDbAvailable())
|
|
22
|
+
return [];
|
|
23
|
+
const mid = state.activeMilestone?.id;
|
|
24
|
+
if (!mid)
|
|
25
|
+
return [];
|
|
26
|
+
const milestone = getMilestone(mid);
|
|
27
|
+
if (!milestone)
|
|
28
|
+
return [];
|
|
29
|
+
const drifts = [];
|
|
30
|
+
// Milestone-level
|
|
31
|
+
if (COMPLETE_STATUSES.has(milestone.status) &&
|
|
32
|
+
milestone.completed_at === null) {
|
|
33
|
+
const summary = resolveMilestoneFile(ctx.basePath, mid, "SUMMARY");
|
|
34
|
+
if (summary && existsSync(summary)) {
|
|
35
|
+
drifts.push({
|
|
36
|
+
kind: "missing-completion-timestamp",
|
|
37
|
+
entity: "milestone",
|
|
38
|
+
ids: [mid],
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Slice and task levels iterate independently — tasks can complete before
|
|
43
|
+
// the parent slice closes, so task drift must be checked even when the
|
|
44
|
+
// slice is still pending.
|
|
45
|
+
for (const slice of getMilestoneSlices(mid)) {
|
|
46
|
+
if (COMPLETE_STATUSES.has(slice.status) &&
|
|
47
|
+
slice.completed_at === null) {
|
|
48
|
+
const summary = resolveSliceFile(ctx.basePath, mid, slice.id, "SUMMARY");
|
|
49
|
+
if (summary && existsSync(summary)) {
|
|
50
|
+
drifts.push({
|
|
51
|
+
kind: "missing-completion-timestamp",
|
|
52
|
+
entity: "slice",
|
|
53
|
+
ids: [`${mid}/${slice.id}`],
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
for (const task of getSliceTasks(mid, slice.id)) {
|
|
58
|
+
if (!COMPLETE_STATUSES.has(task.status))
|
|
59
|
+
continue;
|
|
60
|
+
if (task.completed_at !== null)
|
|
61
|
+
continue;
|
|
62
|
+
const taskSummary = resolveTaskFile(ctx.basePath, mid, slice.id, task.id, "SUMMARY");
|
|
63
|
+
if (taskSummary && existsSync(taskSummary)) {
|
|
64
|
+
drifts.push({
|
|
65
|
+
kind: "missing-completion-timestamp",
|
|
66
|
+
entity: "task",
|
|
67
|
+
ids: [`${mid}/${slice.id}/${task.id}`],
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return drifts;
|
|
73
|
+
}
|
|
74
|
+
export function repairMissingCompletionTimestamp(record, ctx) {
|
|
75
|
+
const composite = record.ids[0];
|
|
76
|
+
if (!composite)
|
|
77
|
+
return;
|
|
78
|
+
const parts = composite.split("/");
|
|
79
|
+
if (record.entity === "milestone") {
|
|
80
|
+
const [mid] = parts;
|
|
81
|
+
if (!mid)
|
|
82
|
+
return;
|
|
83
|
+
const milestone = getMilestone(mid);
|
|
84
|
+
if (!milestone ||
|
|
85
|
+
milestone.completed_at !== null ||
|
|
86
|
+
!COMPLETE_STATUSES.has(milestone.status))
|
|
87
|
+
return;
|
|
88
|
+
const summary = resolveMilestoneFile(ctx.basePath, mid, "SUMMARY");
|
|
89
|
+
const ts = summary ? summaryMtimeIso(summary) : null;
|
|
90
|
+
if (!ts)
|
|
91
|
+
return;
|
|
92
|
+
updateMilestoneStatus(mid, milestone.status, ts);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (record.entity === "slice") {
|
|
96
|
+
const [mid, sid] = parts;
|
|
97
|
+
if (!mid || !sid)
|
|
98
|
+
return;
|
|
99
|
+
const slice = getMilestoneSlices(mid).find((s) => s.id === sid);
|
|
100
|
+
if (!slice ||
|
|
101
|
+
slice.completed_at !== null ||
|
|
102
|
+
!COMPLETE_STATUSES.has(slice.status))
|
|
103
|
+
return;
|
|
104
|
+
const summary = resolveSliceFile(ctx.basePath, mid, sid, "SUMMARY");
|
|
105
|
+
const ts = summary ? summaryMtimeIso(summary) : null;
|
|
106
|
+
if (!ts)
|
|
107
|
+
return;
|
|
108
|
+
updateSliceStatus(mid, sid, slice.status, ts);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (record.entity === "task") {
|
|
112
|
+
const [mid, sid, tid] = parts;
|
|
113
|
+
if (!mid || !sid || !tid)
|
|
114
|
+
return;
|
|
115
|
+
const task = getSliceTasks(mid, sid).find((t) => t.id === tid);
|
|
116
|
+
if (!task ||
|
|
117
|
+
task.completed_at !== null ||
|
|
118
|
+
!COMPLETE_STATUSES.has(task.status))
|
|
119
|
+
return;
|
|
120
|
+
const summary = resolveTaskFile(ctx.basePath, mid, sid, tid, "SUMMARY");
|
|
121
|
+
const ts = summary ? summaryMtimeIso(summary) : null;
|
|
122
|
+
if (!ts)
|
|
123
|
+
return;
|
|
124
|
+
updateTaskStatus(mid, sid, tid, task.status, ts);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
export const completionTimestampHandler = {
|
|
128
|
+
kind: "missing-completion-timestamp",
|
|
129
|
+
detect: detectMissingCompletionTimestampDrift,
|
|
130
|
+
repair: repairMissingCompletionTimestamp,
|
|
131
|
+
};
|