gsd-pi 2.44.0-dev.d25d507 → 2.45.0-dev.1afbdaa
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/help-text.js +1 -1
- package/dist/loader.js +34 -0
- package/dist/resources/extensions/gsd/activity-log.js +7 -0
- package/dist/resources/extensions/gsd/auto/infra-errors.js +3 -0
- package/dist/resources/extensions/gsd/auto/phases.js +63 -77
- package/dist/resources/extensions/gsd/auto/run-unit.js +6 -3
- package/dist/resources/extensions/gsd/auto/session.js +0 -11
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +112 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +25 -96
- package/dist/resources/extensions/gsd/auto-prompts.js +24 -1
- package/dist/resources/extensions/gsd/auto-start.js +23 -5
- package/dist/resources/extensions/gsd/auto-timers.js +57 -3
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +4 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +14 -10
- package/dist/resources/extensions/gsd/auto.js +42 -60
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +170 -11
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +18 -0
- package/dist/resources/extensions/gsd/commands/catalog.js +7 -1
- package/dist/resources/extensions/gsd/commands/context.js +0 -4
- package/dist/resources/extensions/gsd/commands/handlers/core.js +2 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +10 -0
- package/dist/resources/extensions/gsd/commands/handlers/parallel.js +1 -1
- package/dist/resources/extensions/gsd/commands-mcp-status.js +187 -0
- package/dist/resources/extensions/gsd/crash-recovery.js +2 -4
- package/dist/resources/extensions/gsd/dashboard-overlay.js +0 -44
- package/dist/resources/extensions/gsd/db-writer.js +40 -22
- package/dist/resources/extensions/gsd/doctor-checks.js +167 -2
- package/dist/resources/extensions/gsd/doctor.js +13 -3
- package/dist/resources/extensions/gsd/git-service.js +8 -3
- package/dist/resources/extensions/gsd/gsd-db.js +28 -4
- package/dist/resources/extensions/gsd/guided-flow.js +1 -2
- package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
- package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +5 -18
- package/dist/resources/extensions/gsd/preferences-types.js +2 -2
- package/dist/resources/extensions/gsd/preferences.js +8 -4
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +21 -10
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +10 -23
- package/dist/resources/extensions/gsd/prompts/discuss.md +2 -2
- package/dist/resources/extensions/gsd/prompts/execute-task.md +5 -15
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +5 -3
- package/dist/resources/extensions/gsd/prompts/queue.md +2 -2
- package/dist/resources/extensions/gsd/prompts/quick-task.md +2 -0
- package/dist/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +3 -14
- package/dist/resources/extensions/gsd/prompts/research-slice.md +3 -3
- package/dist/resources/extensions/gsd/prompts/rethink.md +83 -0
- package/dist/resources/extensions/gsd/prompts/system.md +1 -1
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
- package/dist/resources/extensions/gsd/provider-error-pause.js +7 -0
- package/dist/resources/extensions/gsd/repo-identity.js +45 -7
- package/dist/resources/extensions/gsd/rethink.js +115 -0
- package/dist/resources/extensions/gsd/session-lock.js +1 -3
- package/dist/resources/extensions/gsd/state.js +48 -3
- package/dist/resources/extensions/gsd/sync-lock.js +89 -0
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +61 -11
- package/dist/resources/extensions/gsd/tools/complete-slice.js +56 -11
- package/dist/resources/extensions/gsd/tools/complete-task.js +50 -2
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +37 -1
- package/dist/resources/extensions/gsd/tools/plan-slice.js +31 -1
- package/dist/resources/extensions/gsd/tools/plan-task.js +28 -1
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +32 -2
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +86 -0
- package/dist/resources/extensions/gsd/tools/reopen-task.js +90 -0
- package/dist/resources/extensions/gsd/tools/replan-slice.js +34 -2
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +88 -0
- package/dist/resources/extensions/gsd/unit-ownership.js +85 -0
- package/dist/resources/extensions/gsd/workflow-events.js +102 -0
- package/dist/resources/extensions/gsd/workflow-logger.js +193 -0
- package/dist/resources/extensions/gsd/workflow-manifest.js +244 -0
- package/dist/resources/extensions/gsd/workflow-migration.js +280 -0
- package/dist/resources/extensions/gsd/workflow-projections.js +373 -0
- package/dist/resources/extensions/gsd/workflow-reconcile.js +411 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +34 -3
- package/dist/resources/extensions/gsd/worktree-resolver.js +43 -0
- package/dist/resources/extensions/gsd/write-intercept.js +84 -0
- package/dist/resources/extensions/mcp-client/index.js +14 -0
- package/dist/resources/extensions/voice/index.js +11 -16
- package/dist/resources/extensions/voice/linux-ready.js +67 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- 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/page.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- 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 +2 -2
- 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 +2 -2
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- 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 +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/page.js +1 -1
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/4024.11ca5c01938e5948.js +9 -0
- package/dist/web/standalone/.next/static/chunks/{3721.bf31263de6d5fa46.js → 485.243af25f0cdf50d6.js} +2 -2
- package/dist/web/standalone/.next/static/chunks/app/{page-b9367c5ae13b99c6.js → page-6654a8cca61a3d1c.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/webpack-0a4cd455ec4197d2.js +1 -0
- package/dist/web/standalone/.next/static/css/dd4ae3f58ac9b600.css +1 -0
- package/package.json +2 -1
- package/packages/native/dist/stream-process/index.js +2 -2
- package/packages/native/src/__tests__/stream-process.test.mjs +34 -0
- package/packages/native/src/stream-process/index.ts +2 -2
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +3 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +15 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +2 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts +4 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +10 -5
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js +185 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +15 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.js +41 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +239 -10
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +13 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +40 -3
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-commands.test.js +206 -195
- package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +6 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +17 -0
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +32 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -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 +8 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +15 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +40 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +1 -0
- 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 +4 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +5 -2
- 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 +13 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +17 -8
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -3
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/auth-storage.ts +15 -1
- package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +2 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -1
- package/packages/pi-coding-agent/src/core/lifecycle-hooks.test.ts +227 -0
- package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +11 -5
- package/packages/pi-coding-agent/src/core/local-model-check.ts +45 -0
- package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +297 -11
- package/packages/pi-coding-agent/src/core/model-registry.ts +51 -4
- package/packages/pi-coding-agent/src/core/package-commands.test.ts +227 -205
- package/packages/pi-coding-agent/src/core/settings-manager.ts +9 -0
- package/packages/pi-coding-agent/src/main.ts +19 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +38 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +10 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +48 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +3 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +18 -3
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +16 -7
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +8 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/activity-log.ts +1 -0
- package/src/resources/extensions/gsd/auto/infra-errors.ts +3 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -19
- package/src/resources/extensions/gsd/auto/phases.ts +69 -91
- package/src/resources/extensions/gsd/auto/run-unit.ts +6 -3
- package/src/resources/extensions/gsd/auto/session.ts +0 -18
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +131 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +0 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +25 -106
- package/src/resources/extensions/gsd/auto-prompts.ts +24 -1
- package/src/resources/extensions/gsd/auto-start.ts +26 -5
- package/src/resources/extensions/gsd/auto-timers.ts +64 -3
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +5 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +17 -11
- package/src/resources/extensions/gsd/auto.ts +44 -86
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +162 -11
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +22 -0
- package/src/resources/extensions/gsd/commands/catalog.ts +7 -1
- package/src/resources/extensions/gsd/commands/context.ts +0 -5
- package/src/resources/extensions/gsd/commands/handlers/core.ts +2 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +10 -0
- package/src/resources/extensions/gsd/commands/handlers/parallel.ts +1 -1
- package/src/resources/extensions/gsd/commands-mcp-status.ts +247 -0
- package/src/resources/extensions/gsd/crash-recovery.ts +1 -5
- package/src/resources/extensions/gsd/dashboard-overlay.ts +0 -50
- package/src/resources/extensions/gsd/db-writer.ts +41 -27
- package/src/resources/extensions/gsd/doctor-checks.ts +180 -2
- package/src/resources/extensions/gsd/doctor-types.ts +7 -1
- package/src/resources/extensions/gsd/doctor.ts +13 -4
- package/src/resources/extensions/gsd/git-service.ts +6 -2
- package/src/resources/extensions/gsd/gsd-db.ts +32 -4
- package/src/resources/extensions/gsd/guided-flow.ts +1 -2
- package/src/resources/extensions/gsd/journal.ts +6 -1
- package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
- package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +5 -21
- package/src/resources/extensions/gsd/preferences-types.ts +2 -2
- package/src/resources/extensions/gsd/preferences.ts +7 -3
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +21 -10
- package/src/resources/extensions/gsd/prompts/complete-slice.md +10 -23
- package/src/resources/extensions/gsd/prompts/discuss.md +2 -2
- package/src/resources/extensions/gsd/prompts/execute-task.md +5 -15
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +5 -3
- package/src/resources/extensions/gsd/prompts/queue.md +2 -2
- package/src/resources/extensions/gsd/prompts/quick-task.md +2 -0
- package/src/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
- package/src/resources/extensions/gsd/prompts/replan-slice.md +3 -14
- package/src/resources/extensions/gsd/prompts/research-slice.md +3 -3
- package/src/resources/extensions/gsd/prompts/rethink.md +83 -0
- package/src/resources/extensions/gsd/prompts/system.md +1 -1
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
- package/src/resources/extensions/gsd/provider-error-pause.ts +9 -0
- package/src/resources/extensions/gsd/repo-identity.ts +46 -7
- package/src/resources/extensions/gsd/rethink.ts +154 -0
- package/src/resources/extensions/gsd/session-lock.ts +0 -4
- package/src/resources/extensions/gsd/state.ts +49 -1
- package/src/resources/extensions/gsd/sync-lock.ts +94 -0
- package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +5 -13
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +6 -10
- package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +88 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +96 -0
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +264 -228
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +317 -250
- package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +114 -0
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +2 -8
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +0 -3
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +121 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +60 -0
- package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/infra-error.test.ts +20 -2
- package/src/resources/extensions/gsd/tests/inherited-repo-home-dir.test.ts +121 -0
- package/src/resources/extensions/gsd/tests/integration-proof.test.ts +15 -24
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -3
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +8 -9
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +42 -3
- package/src/resources/extensions/gsd/tests/parallel-budget-atomicity.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +0 -7
- package/src/resources/extensions/gsd/tests/parallel-merge.test.ts +7 -8
- package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +20 -24
- package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +0 -2
- package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +9 -6
- package/src/resources/extensions/gsd/tests/post-mutation-hook.test.ts +171 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +7 -9
- package/src/resources/extensions/gsd/tests/projection-regression.test.ts +174 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +26 -21
- package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +176 -0
- package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +155 -0
- package/src/resources/extensions/gsd/tests/reopen-task.test.ts +165 -0
- package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +1 -4
- package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +67 -0
- package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +2 -3
- package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +108 -0
- package/src/resources/extensions/gsd/tests/sync-lock.test.ts +122 -0
- package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/unit-ownership.test.ts +175 -0
- package/src/resources/extensions/gsd/tests/workflow-events.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +275 -0
- package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +171 -0
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +220 -0
- package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +65 -0
- package/src/resources/extensions/gsd/tests/write-intercept.test.ts +76 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +74 -11
- package/src/resources/extensions/gsd/tools/complete-slice.ts +68 -11
- package/src/resources/extensions/gsd/tools/complete-task.ts +63 -1
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +45 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +40 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +37 -1
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +39 -1
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +125 -0
- package/src/resources/extensions/gsd/tools/reopen-task.ts +129 -0
- package/src/resources/extensions/gsd/tools/replan-slice.ts +41 -1
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +127 -0
- package/src/resources/extensions/gsd/types.ts +8 -0
- package/src/resources/extensions/gsd/unit-ownership.ts +104 -0
- package/src/resources/extensions/gsd/workflow-events.ts +154 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +243 -0
- package/src/resources/extensions/gsd/workflow-manifest.ts +334 -0
- package/src/resources/extensions/gsd/workflow-migration.ts +345 -0
- package/src/resources/extensions/gsd/workflow-projections.ts +425 -0
- package/src/resources/extensions/gsd/workflow-reconcile.ts +503 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +41 -5
- package/src/resources/extensions/gsd/worktree-resolver.ts +44 -0
- package/src/resources/extensions/gsd/write-intercept.ts +90 -0
- package/src/resources/extensions/mcp-client/index.ts +20 -0
- package/src/resources/extensions/voice/index.ts +11 -21
- package/src/resources/extensions/voice/linux-ready.ts +87 -0
- package/src/resources/extensions/voice/tests/linux-ready.test.ts +124 -0
- package/dist/web/standalone/.next/static/chunks/4024.0de81b543b28b9fe.js +0 -9
- package/dist/web/standalone/.next/static/chunks/webpack-9014b5adb127a98a.js +0 -1
- package/dist/web/standalone/.next/static/css/8a727f372cf53002.css +0 -1
- /package/dist/web/standalone/.next/static/{tokoGmfkYfWf1_Yl_Gz7i → j-BskPs0nxxPeYY-bSrab}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{tokoGmfkYfWf1_Yl_Gz7i → j-BskPs0nxxPeYY-bSrab}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test for #2322: recoveryAttempts persists across re-dispatches,
|
|
3
|
+
* causing instant task skip.
|
|
4
|
+
*
|
|
5
|
+
* When a unit hits recovery limits and is later re-dispatched, the
|
|
6
|
+
* recoveryAttempts counter from the prior execution carries over because
|
|
7
|
+
* the dispatch-time writeUnitRuntimeRecord call does not reset it.
|
|
8
|
+
* This causes the next execution to be instantly skipped with no steering
|
|
9
|
+
* message or second chance.
|
|
10
|
+
*
|
|
11
|
+
* The fix: include `recoveryAttempts: 0` in the dispatch-time runtime
|
|
12
|
+
* record write in runUnitPhase.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { mkdtempSync, mkdirSync, rmSync } from "node:fs";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
import { tmpdir } from "node:os";
|
|
18
|
+
import {
|
|
19
|
+
writeUnitRuntimeRecord,
|
|
20
|
+
readUnitRuntimeRecord,
|
|
21
|
+
} from "../unit-runtime.ts";
|
|
22
|
+
import { createTestContext } from "./test-helpers.ts";
|
|
23
|
+
|
|
24
|
+
const { assertEq, assertTrue, report } = createTestContext();
|
|
25
|
+
|
|
26
|
+
// ═══ Setup ════════════════════════════════════════════════════════════════════
|
|
27
|
+
|
|
28
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-recovery-reset-test-"));
|
|
29
|
+
mkdirSync(join(base, ".gsd", "runtime", "units"), { recursive: true });
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
// ═══ #2322: recoveryAttempts should reset on re-dispatch ═══════════════════
|
|
33
|
+
|
|
34
|
+
{
|
|
35
|
+
console.log("\n=== #2322: recoveryAttempts should reset on re-dispatch ===");
|
|
36
|
+
|
|
37
|
+
const unitType = "execute-task";
|
|
38
|
+
const unitId = "M001/S01/T01";
|
|
39
|
+
const startedAt1 = Date.now() - 10000;
|
|
40
|
+
|
|
41
|
+
// Simulate first dispatch — clean state
|
|
42
|
+
writeUnitRuntimeRecord(base, unitType, unitId, startedAt1, {
|
|
43
|
+
phase: "dispatched",
|
|
44
|
+
wrapupWarningSent: false,
|
|
45
|
+
timeoutAt: null,
|
|
46
|
+
lastProgressAt: startedAt1,
|
|
47
|
+
progressCount: 0,
|
|
48
|
+
lastProgressKind: "dispatch",
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Simulate timeout recovery incrementing recoveryAttempts
|
|
52
|
+
writeUnitRuntimeRecord(base, unitType, unitId, startedAt1, {
|
|
53
|
+
phase: "recovered",
|
|
54
|
+
recoveryAttempts: 1,
|
|
55
|
+
lastRecoveryReason: "hard",
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const afterRecovery = readUnitRuntimeRecord(base, unitType, unitId);
|
|
59
|
+
assertEq(afterRecovery?.recoveryAttempts, 1, "recoveryAttempts should be 1 after recovery");
|
|
60
|
+
assertEq(afterRecovery?.lastRecoveryReason, "hard", "lastRecoveryReason should be 'hard'");
|
|
61
|
+
|
|
62
|
+
// Simulate re-dispatch (second execution of same unit).
|
|
63
|
+
// This is what runUnitPhase should do at dispatch time — explicitly reset
|
|
64
|
+
// recoveryAttempts so the new execution gets its full recovery budget.
|
|
65
|
+
const startedAt2 = Date.now();
|
|
66
|
+
writeUnitRuntimeRecord(base, unitType, unitId, startedAt2, {
|
|
67
|
+
phase: "dispatched",
|
|
68
|
+
wrapupWarningSent: false,
|
|
69
|
+
timeoutAt: null,
|
|
70
|
+
lastProgressAt: startedAt2,
|
|
71
|
+
progressCount: 0,
|
|
72
|
+
lastProgressKind: "dispatch",
|
|
73
|
+
recoveryAttempts: 0, // FIX: must be explicitly reset
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const afterRedispatch = readUnitRuntimeRecord(base, unitType, unitId);
|
|
77
|
+
assertEq(
|
|
78
|
+
afterRedispatch?.recoveryAttempts,
|
|
79
|
+
0,
|
|
80
|
+
"recoveryAttempts should be 0 after re-dispatch (was carried over from prior execution)",
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ═══ Verify the BUG scenario: omitting recoveryAttempts carries it over ═══
|
|
85
|
+
|
|
86
|
+
{
|
|
87
|
+
console.log("\n=== #2322: demonstrates bug — omitting recoveryAttempts carries it over ===");
|
|
88
|
+
|
|
89
|
+
const unitType = "execute-task";
|
|
90
|
+
const unitId = "M001/S01/T02";
|
|
91
|
+
const startedAt1 = Date.now() - 10000;
|
|
92
|
+
|
|
93
|
+
// First dispatch
|
|
94
|
+
writeUnitRuntimeRecord(base, unitType, unitId, startedAt1, {
|
|
95
|
+
phase: "dispatched",
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Timeout bumps recoveryAttempts to 1
|
|
99
|
+
writeUnitRuntimeRecord(base, unitType, unitId, startedAt1, {
|
|
100
|
+
recoveryAttempts: 1,
|
|
101
|
+
lastRecoveryReason: "hard",
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Re-dispatch WITHOUT resetting recoveryAttempts (the bug)
|
|
105
|
+
const startedAt2 = Date.now();
|
|
106
|
+
writeUnitRuntimeRecord(base, unitType, unitId, startedAt2, {
|
|
107
|
+
phase: "dispatched",
|
|
108
|
+
wrapupWarningSent: false,
|
|
109
|
+
timeoutAt: null,
|
|
110
|
+
lastProgressAt: startedAt2,
|
|
111
|
+
progressCount: 0,
|
|
112
|
+
lastProgressKind: "dispatch",
|
|
113
|
+
// recoveryAttempts: NOT included — this is the bug
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const afterBuggyRedispatch = readUnitRuntimeRecord(base, unitType, unitId);
|
|
117
|
+
// This DEMONSTRATES the bug: recoveryAttempts is still 1
|
|
118
|
+
assertEq(
|
|
119
|
+
afterBuggyRedispatch?.recoveryAttempts,
|
|
120
|
+
1,
|
|
121
|
+
"BUG DEMO: recoveryAttempts carries over when not explicitly reset",
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ═══ Hard timeout maxRecoveryAttempts=1 — second dispatch must get full budget ═══
|
|
126
|
+
|
|
127
|
+
{
|
|
128
|
+
console.log("\n=== #2322: second dispatch gets full hard-timeout budget after reset ===");
|
|
129
|
+
|
|
130
|
+
const unitType = "execute-task";
|
|
131
|
+
const unitId = "M001/S01/T03";
|
|
132
|
+
|
|
133
|
+
// First dispatch
|
|
134
|
+
const start1 = Date.now() - 20000;
|
|
135
|
+
writeUnitRuntimeRecord(base, unitType, unitId, start1, {
|
|
136
|
+
phase: "dispatched",
|
|
137
|
+
recoveryAttempts: 0,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Hard timeout recovery — exhausts the budget (maxRecoveryAttempts=1 for hard)
|
|
141
|
+
writeUnitRuntimeRecord(base, unitType, unitId, start1, {
|
|
142
|
+
phase: "recovered",
|
|
143
|
+
recoveryAttempts: 1,
|
|
144
|
+
lastRecoveryReason: "hard",
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const afterExhausted = readUnitRuntimeRecord(base, unitType, unitId);
|
|
148
|
+
assertEq(afterExhausted?.recoveryAttempts, 1, "budget exhausted after hard recovery");
|
|
149
|
+
|
|
150
|
+
// Second dispatch with fix: reset recoveryAttempts
|
|
151
|
+
const start2 = Date.now();
|
|
152
|
+
writeUnitRuntimeRecord(base, unitType, unitId, start2, {
|
|
153
|
+
phase: "dispatched",
|
|
154
|
+
wrapupWarningSent: false,
|
|
155
|
+
timeoutAt: null,
|
|
156
|
+
lastProgressAt: start2,
|
|
157
|
+
progressCount: 0,
|
|
158
|
+
lastProgressKind: "dispatch",
|
|
159
|
+
recoveryAttempts: 0,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const afterReset = readUnitRuntimeRecord(base, unitType, unitId);
|
|
163
|
+
assertEq(afterReset?.recoveryAttempts, 0, "second dispatch has full recovery budget");
|
|
164
|
+
|
|
165
|
+
// Now a hard timeout should be recoverable (0 < 1)
|
|
166
|
+
assertTrue(
|
|
167
|
+
(afterReset?.recoveryAttempts ?? 0) < 1,
|
|
168
|
+
"hard recovery should be allowed (recoveryAttempts < maxRecoveryAttempts)",
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
} finally {
|
|
173
|
+
rmSync(base, { recursive: true, force: true });
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
report();
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// GSD — reopen-slice handler tests
|
|
2
|
+
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
3
|
+
|
|
4
|
+
import test from 'node:test';
|
|
5
|
+
import assert from 'node:assert/strict';
|
|
6
|
+
import { mkdtempSync, mkdirSync, rmSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { tmpdir } from 'node:os';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
openDatabase,
|
|
12
|
+
closeDatabase,
|
|
13
|
+
insertMilestone,
|
|
14
|
+
insertSlice,
|
|
15
|
+
insertTask,
|
|
16
|
+
getSlice,
|
|
17
|
+
getSliceTasks,
|
|
18
|
+
} from '../gsd-db.ts';
|
|
19
|
+
import { handleReopenSlice } from '../tools/reopen-slice.ts';
|
|
20
|
+
|
|
21
|
+
function makeTmpBase(): string {
|
|
22
|
+
const base = mkdtempSync(join(tmpdir(), 'gsd-reopen-slice-'));
|
|
23
|
+
mkdirSync(join(base, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'tasks'), { recursive: true });
|
|
24
|
+
return base;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function cleanup(base: string): void {
|
|
28
|
+
try { closeDatabase(); } catch { /* noop */ }
|
|
29
|
+
try { rmSync(base, { recursive: true, force: true }); } catch { /* noop */ }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function seedCompleteSlice(): void {
|
|
33
|
+
insertMilestone({ id: 'M001', title: 'Test Milestone', status: 'active' });
|
|
34
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Test Slice', status: 'complete' });
|
|
35
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'Task One', status: 'complete' });
|
|
36
|
+
insertTask({ id: 'T02', sliceId: 'S01', milestoneId: 'M001', title: 'Task Two', status: 'complete' });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ─── Success path ────────────────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
test('handleReopenSlice: resets a complete slice to in_progress and all tasks to pending', async () => {
|
|
42
|
+
const base = makeTmpBase();
|
|
43
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
44
|
+
try {
|
|
45
|
+
seedCompleteSlice();
|
|
46
|
+
|
|
47
|
+
const result = await handleReopenSlice({
|
|
48
|
+
milestoneId: 'M001',
|
|
49
|
+
sliceId: 'S01',
|
|
50
|
+
reason: 'need to redo after requirements change',
|
|
51
|
+
}, base);
|
|
52
|
+
|
|
53
|
+
assert.ok(!('error' in result), `unexpected error: ${'error' in result ? result.error : ''}`);
|
|
54
|
+
assert.equal(result.sliceId, 'S01');
|
|
55
|
+
assert.equal(result.tasksReset, 2, 'should report 2 tasks reset');
|
|
56
|
+
|
|
57
|
+
const slice = getSlice('M001', 'S01');
|
|
58
|
+
assert.ok(slice, 'slice should still exist');
|
|
59
|
+
assert.equal(slice!.status, 'in_progress', 'slice status should be in_progress');
|
|
60
|
+
|
|
61
|
+
const tasks = getSliceTasks('M001', 'S01');
|
|
62
|
+
assert.equal(tasks.length, 2, 'both tasks should still exist');
|
|
63
|
+
assert.ok(tasks.every(t => t.status === 'pending'), 'all tasks should be pending');
|
|
64
|
+
} finally {
|
|
65
|
+
cleanup(base);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('handleReopenSlice: works with a single task', async () => {
|
|
70
|
+
const base = makeTmpBase();
|
|
71
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
72
|
+
try {
|
|
73
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
74
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', status: 'complete' });
|
|
75
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', status: 'complete' });
|
|
76
|
+
|
|
77
|
+
const result = await handleReopenSlice({ milestoneId: 'M001', sliceId: 'S01' }, base);
|
|
78
|
+
|
|
79
|
+
assert.ok(!('error' in result));
|
|
80
|
+
assert.equal(result.tasksReset, 1);
|
|
81
|
+
} finally {
|
|
82
|
+
cleanup(base);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// ─── Failure paths ───────────────────────────────────────────────────────
|
|
87
|
+
|
|
88
|
+
test('handleReopenSlice: rejects empty sliceId', async () => {
|
|
89
|
+
const base = makeTmpBase();
|
|
90
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
91
|
+
try {
|
|
92
|
+
const result = await handleReopenSlice({ milestoneId: 'M001', sliceId: '' }, base);
|
|
93
|
+
assert.ok('error' in result);
|
|
94
|
+
assert.match(result.error, /sliceId/);
|
|
95
|
+
} finally {
|
|
96
|
+
cleanup(base);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('handleReopenSlice: rejects non-existent milestone', async () => {
|
|
101
|
+
const base = makeTmpBase();
|
|
102
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
103
|
+
try {
|
|
104
|
+
const result = await handleReopenSlice({ milestoneId: 'M999', sliceId: 'S01' }, base);
|
|
105
|
+
assert.ok('error' in result);
|
|
106
|
+
assert.match(result.error, /milestone not found/);
|
|
107
|
+
} finally {
|
|
108
|
+
cleanup(base);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('handleReopenSlice: rejects slice in a closed milestone', async () => {
|
|
113
|
+
const base = makeTmpBase();
|
|
114
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
115
|
+
try {
|
|
116
|
+
insertMilestone({ id: 'M001', title: 'Done', status: 'complete' });
|
|
117
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', status: 'complete' });
|
|
118
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', status: 'complete' });
|
|
119
|
+
|
|
120
|
+
const result = await handleReopenSlice({ milestoneId: 'M001', sliceId: 'S01' }, base);
|
|
121
|
+
assert.ok('error' in result);
|
|
122
|
+
assert.match(result.error, /closed milestone/);
|
|
123
|
+
} finally {
|
|
124
|
+
cleanup(base);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('handleReopenSlice: rejects reopening a slice that is not complete', async () => {
|
|
129
|
+
const base = makeTmpBase();
|
|
130
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
131
|
+
try {
|
|
132
|
+
insertMilestone({ id: 'M001', title: 'Active', status: 'active' });
|
|
133
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', status: 'in_progress' });
|
|
134
|
+
|
|
135
|
+
const result = await handleReopenSlice({ milestoneId: 'M001', sliceId: 'S01' }, base);
|
|
136
|
+
assert.ok('error' in result);
|
|
137
|
+
assert.match(result.error, /not complete/);
|
|
138
|
+
} finally {
|
|
139
|
+
cleanup(base);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test('handleReopenSlice: rejects non-existent slice', async () => {
|
|
144
|
+
const base = makeTmpBase();
|
|
145
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
146
|
+
try {
|
|
147
|
+
insertMilestone({ id: 'M001', title: 'Active', status: 'active' });
|
|
148
|
+
|
|
149
|
+
const result = await handleReopenSlice({ milestoneId: 'M001', sliceId: 'S99' }, base);
|
|
150
|
+
assert.ok('error' in result);
|
|
151
|
+
assert.match(result.error, /slice not found/);
|
|
152
|
+
} finally {
|
|
153
|
+
cleanup(base);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// GSD — reopen-task handler tests
|
|
2
|
+
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
3
|
+
|
|
4
|
+
import test from 'node:test';
|
|
5
|
+
import assert from 'node:assert/strict';
|
|
6
|
+
import { mkdtempSync, mkdirSync, rmSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { tmpdir } from 'node:os';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
openDatabase,
|
|
12
|
+
closeDatabase,
|
|
13
|
+
insertMilestone,
|
|
14
|
+
insertSlice,
|
|
15
|
+
insertTask,
|
|
16
|
+
getTask,
|
|
17
|
+
} from '../gsd-db.ts';
|
|
18
|
+
import { handleReopenTask } from '../tools/reopen-task.ts';
|
|
19
|
+
|
|
20
|
+
function makeTmpBase(): string {
|
|
21
|
+
const base = mkdtempSync(join(tmpdir(), 'gsd-reopen-task-'));
|
|
22
|
+
mkdirSync(join(base, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'tasks'), { recursive: true });
|
|
23
|
+
return base;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function cleanup(base: string): void {
|
|
27
|
+
try { closeDatabase(); } catch { /* noop */ }
|
|
28
|
+
try { rmSync(base, { recursive: true, force: true }); } catch { /* noop */ }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function seedCompleteTask(): void {
|
|
32
|
+
insertMilestone({ id: 'M001', title: 'Test Milestone', status: 'active' });
|
|
33
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Test Slice', status: 'in_progress' });
|
|
34
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'Task One', status: 'complete' });
|
|
35
|
+
insertTask({ id: 'T02', sliceId: 'S01', milestoneId: 'M001', title: 'Task Two', status: 'pending' });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ─── Success path ────────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
test('handleReopenTask: resets a complete task to pending', async () => {
|
|
41
|
+
const base = makeTmpBase();
|
|
42
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
43
|
+
try {
|
|
44
|
+
seedCompleteTask();
|
|
45
|
+
|
|
46
|
+
const result = await handleReopenTask({
|
|
47
|
+
milestoneId: 'M001',
|
|
48
|
+
sliceId: 'S01',
|
|
49
|
+
taskId: 'T01',
|
|
50
|
+
reason: 'verification failed after merge',
|
|
51
|
+
}, base);
|
|
52
|
+
|
|
53
|
+
assert.ok(!('error' in result), `unexpected error: ${'error' in result ? result.error : ''}`);
|
|
54
|
+
assert.equal(result.taskId, 'T01');
|
|
55
|
+
|
|
56
|
+
const task = getTask('M001', 'S01', 'T01');
|
|
57
|
+
assert.ok(task, 'task should still exist');
|
|
58
|
+
assert.equal(task!.status, 'pending', 'task status should be reset to pending');
|
|
59
|
+
} finally {
|
|
60
|
+
cleanup(base);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('handleReopenTask: does not affect other tasks in the slice', async () => {
|
|
65
|
+
const base = makeTmpBase();
|
|
66
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
67
|
+
try {
|
|
68
|
+
seedCompleteTask();
|
|
69
|
+
|
|
70
|
+
await handleReopenTask({ milestoneId: 'M001', sliceId: 'S01', taskId: 'T01' }, base);
|
|
71
|
+
|
|
72
|
+
const t02 = getTask('M001', 'S01', 'T02');
|
|
73
|
+
assert.ok(t02, 'T02 should still exist');
|
|
74
|
+
assert.equal(t02!.status, 'pending', 'T02 status should be unchanged');
|
|
75
|
+
} finally {
|
|
76
|
+
cleanup(base);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// ─── Failure paths ───────────────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
test('handleReopenTask: rejects empty taskId', async () => {
|
|
83
|
+
const base = makeTmpBase();
|
|
84
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
85
|
+
try {
|
|
86
|
+
const result = await handleReopenTask({ milestoneId: 'M001', sliceId: 'S01', taskId: '' }, base);
|
|
87
|
+
assert.ok('error' in result);
|
|
88
|
+
assert.match(result.error, /taskId/);
|
|
89
|
+
} finally {
|
|
90
|
+
cleanup(base);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('handleReopenTask: rejects non-existent milestone', async () => {
|
|
95
|
+
const base = makeTmpBase();
|
|
96
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
97
|
+
try {
|
|
98
|
+
const result = await handleReopenTask({ milestoneId: 'M999', sliceId: 'S01', taskId: 'T01' }, base);
|
|
99
|
+
assert.ok('error' in result);
|
|
100
|
+
assert.match(result.error, /milestone not found/);
|
|
101
|
+
} finally {
|
|
102
|
+
cleanup(base);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('handleReopenTask: rejects task in a closed milestone', async () => {
|
|
107
|
+
const base = makeTmpBase();
|
|
108
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
109
|
+
try {
|
|
110
|
+
insertMilestone({ id: 'M001', title: 'Done', status: 'complete' });
|
|
111
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', status: 'complete' });
|
|
112
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', status: 'complete' });
|
|
113
|
+
|
|
114
|
+
const result = await handleReopenTask({ milestoneId: 'M001', sliceId: 'S01', taskId: 'T01' }, base);
|
|
115
|
+
assert.ok('error' in result);
|
|
116
|
+
assert.match(result.error, /closed milestone/);
|
|
117
|
+
} finally {
|
|
118
|
+
cleanup(base);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test('handleReopenTask: rejects task inside a closed slice', async () => {
|
|
123
|
+
const base = makeTmpBase();
|
|
124
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
125
|
+
try {
|
|
126
|
+
insertMilestone({ id: 'M001', title: 'Active', status: 'active' });
|
|
127
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', status: 'complete' });
|
|
128
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', status: 'complete' });
|
|
129
|
+
|
|
130
|
+
const result = await handleReopenTask({ milestoneId: 'M001', sliceId: 'S01', taskId: 'T01' }, base);
|
|
131
|
+
assert.ok('error' in result);
|
|
132
|
+
assert.match(result.error, /closed slice/);
|
|
133
|
+
} finally {
|
|
134
|
+
cleanup(base);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('handleReopenTask: rejects reopening a task that is not complete', async () => {
|
|
139
|
+
const base = makeTmpBase();
|
|
140
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
141
|
+
try {
|
|
142
|
+
seedCompleteTask();
|
|
143
|
+
|
|
144
|
+
const result = await handleReopenTask({ milestoneId: 'M001', sliceId: 'S01', taskId: 'T02' }, base);
|
|
145
|
+
assert.ok('error' in result);
|
|
146
|
+
assert.match(result.error, /not complete/);
|
|
147
|
+
} finally {
|
|
148
|
+
cleanup(base);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test('handleReopenTask: rejects non-existent task', async () => {
|
|
153
|
+
const base = makeTmpBase();
|
|
154
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
155
|
+
try {
|
|
156
|
+
insertMilestone({ id: 'M001', title: 'Active', status: 'active' });
|
|
157
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', status: 'in_progress' });
|
|
158
|
+
|
|
159
|
+
const result = await handleReopenTask({ milestoneId: 'M001', sliceId: 'S01', taskId: 'T99' }, base);
|
|
160
|
+
assert.ok('error' in result);
|
|
161
|
+
assert.match(result.error, /task not found/);
|
|
162
|
+
} finally {
|
|
163
|
+
cleanup(base);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
@@ -103,7 +103,7 @@ describe('session-lock-regression', async () => {
|
|
|
103
103
|
try {
|
|
104
104
|
acquireSessionLock(base);
|
|
105
105
|
|
|
106
|
-
updateSessionLock(base, 'execute-task', 'M001/S01/T01',
|
|
106
|
+
updateSessionLock(base, 'execute-task', 'M001/S01/T01', '/tmp/session.json');
|
|
107
107
|
|
|
108
108
|
const data = readSessionLockData(base);
|
|
109
109
|
assert.ok(data !== null, 'lock data readable after update');
|
|
@@ -111,7 +111,6 @@ describe('session-lock-regression', async () => {
|
|
|
111
111
|
assert.deepStrictEqual(data.pid, process.pid, 'lock data has correct PID');
|
|
112
112
|
assert.deepStrictEqual(data.unitType, 'execute-task', 'lock data has correct unit type');
|
|
113
113
|
assert.deepStrictEqual(data.unitId, 'M001/S01/T01', 'lock data has correct unit ID');
|
|
114
|
-
assert.deepStrictEqual(data.completedUnits, 5, 'lock data has correct completed count');
|
|
115
114
|
assert.deepStrictEqual(data.sessionFile, '/tmp/session.json', 'lock data has session file');
|
|
116
115
|
}
|
|
117
116
|
|
|
@@ -136,7 +135,6 @@ describe('session-lock-regression', async () => {
|
|
|
136
135
|
unitType: 'execute-task',
|
|
137
136
|
unitId: 'M001/S01/T01',
|
|
138
137
|
unitStartedAt: new Date(Date.now() - 3600000).toISOString(),
|
|
139
|
-
completedUnits: 3,
|
|
140
138
|
};
|
|
141
139
|
writeFileSync(lockFile, JSON.stringify(staleLock, null, 2));
|
|
142
140
|
|
|
@@ -233,7 +231,6 @@ describe('session-lock-regression', async () => {
|
|
|
233
231
|
unitType: 'execute-task',
|
|
234
232
|
unitId: 'M001/S01/T01',
|
|
235
233
|
unitStartedAt: new Date().toISOString(),
|
|
236
|
-
completedUnits: 0,
|
|
237
234
|
}, null, 2));
|
|
238
235
|
|
|
239
236
|
const status = getSessionLockStatus(base);
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stop-auto-merge-back.test.ts — Regression test for #2317.
|
|
3
|
+
*
|
|
4
|
+
* When auto-mode stops after a milestone is complete, stopAuto should trigger
|
|
5
|
+
* merge-back (mergeAndExit) instead of just exiting the worktree with
|
|
6
|
+
* preserveBranch: true. Otherwise milestone code stays stranded on the
|
|
7
|
+
* worktree branch and never reaches main.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import test from "node:test";
|
|
11
|
+
import assert from "node:assert/strict";
|
|
12
|
+
import { readFileSync } from "node:fs";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
|
|
15
|
+
// ─── Source analysis: stopAuto calls mergeAndExit for complete milestones ────
|
|
16
|
+
|
|
17
|
+
const autoSrcPath = join(import.meta.dirname, "..", "auto.ts");
|
|
18
|
+
const autoSrc = readFileSync(autoSrcPath, "utf-8");
|
|
19
|
+
|
|
20
|
+
test("#2317: stopAuto should check milestone completion status before choosing exit strategy", () => {
|
|
21
|
+
// stopAuto Step 4 should NOT unconditionally call exitMilestone(preserveBranch: true).
|
|
22
|
+
// It should check if the milestone is complete and call mergeAndExit instead.
|
|
23
|
+
|
|
24
|
+
// Find the Step 4 section
|
|
25
|
+
const step4Idx = autoSrc.indexOf("Step 4: Auto-worktree exit");
|
|
26
|
+
assert.ok(step4Idx !== -1, "Step 4 comment exists in stopAuto");
|
|
27
|
+
|
|
28
|
+
// Extract a reasonable window around Step 4 (up to Step 5)
|
|
29
|
+
const step5Idx = autoSrc.indexOf("Step 5:", step4Idx);
|
|
30
|
+
const step4Block = autoSrc.slice(step4Idx, step5Idx);
|
|
31
|
+
|
|
32
|
+
// The fix: Step 4 should call mergeAndExit when milestone is complete
|
|
33
|
+
assert.ok(
|
|
34
|
+
step4Block.includes("mergeAndExit"),
|
|
35
|
+
"Step 4 should call mergeAndExit for completed milestones",
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("#2317: stopAuto should detect milestone completion via SUMMARY file or DB", () => {
|
|
40
|
+
const step4Idx = autoSrc.indexOf("Step 4: Auto-worktree exit");
|
|
41
|
+
const step5Idx = autoSrc.indexOf("Step 5:", step4Idx);
|
|
42
|
+
const step4Block = autoSrc.slice(step4Idx, step5Idx);
|
|
43
|
+
|
|
44
|
+
// Should check completion status — either via SUMMARY file, DB getMilestone, or phase
|
|
45
|
+
const checksCompletion =
|
|
46
|
+
step4Block.includes("SUMMARY") ||
|
|
47
|
+
step4Block.includes("getMilestone") ||
|
|
48
|
+
step4Block.includes("complete") ||
|
|
49
|
+
step4Block.includes("isMilestoneComplete");
|
|
50
|
+
|
|
51
|
+
assert.ok(
|
|
52
|
+
checksCompletion,
|
|
53
|
+
"Step 4 should check if milestone is complete before deciding exit strategy",
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("#2317: stopAuto still preserves branch for incomplete milestones", () => {
|
|
58
|
+
const step4Idx = autoSrc.indexOf("Step 4: Auto-worktree exit");
|
|
59
|
+
const step5Idx = autoSrc.indexOf("Step 5:", step4Idx);
|
|
60
|
+
const step4Block = autoSrc.slice(step4Idx, step5Idx);
|
|
61
|
+
|
|
62
|
+
// preserveBranch should still be used as fallback for non-complete milestones
|
|
63
|
+
assert.ok(
|
|
64
|
+
step4Block.includes("preserveBranch"),
|
|
65
|
+
"Step 4 should still preserve branch for incomplete milestones (fallback path)",
|
|
66
|
+
);
|
|
67
|
+
});
|
|
@@ -64,7 +64,7 @@ test("stopAutoRemote cleans up stale lock (dead PID) and returns found:false", (
|
|
|
64
64
|
const base = makeTmpBase();
|
|
65
65
|
try {
|
|
66
66
|
// Write a lock with a PID that doesn't exist
|
|
67
|
-
writeLock(base, "execute-task", "M001/S01/T01"
|
|
67
|
+
writeLock(base, "execute-task", "M001/S01/T01");
|
|
68
68
|
// Overwrite PID to a dead one
|
|
69
69
|
const lock = readCrashLock(base)!;
|
|
70
70
|
const staleData = { ...lock, pid: 999999999 };
|
|
@@ -111,7 +111,6 @@ test("stopAutoRemote sends SIGTERM to a live process and returns found:true", {
|
|
|
111
111
|
unitType: "execute-task",
|
|
112
112
|
unitId: "M001/S01/T01",
|
|
113
113
|
unitStartedAt: new Date().toISOString(),
|
|
114
|
-
completedUnits: 0,
|
|
115
114
|
};
|
|
116
115
|
writeFileSync(join(base, ".gsd", "auto.lock"), JSON.stringify(lockData, null, 2), "utf-8");
|
|
117
116
|
|
|
@@ -143,7 +142,7 @@ test("lock file should be discoverable at project root, not worktree path", () =
|
|
|
143
142
|
|
|
144
143
|
try {
|
|
145
144
|
// Simulate: auto-mode writes lock to project root (the fix)
|
|
146
|
-
writeLock(projectRoot, "execute-task", "M001/S01/T01"
|
|
145
|
+
writeLock(projectRoot, "execute-task", "M001/S01/T01");
|
|
147
146
|
|
|
148
147
|
// Second terminal checks project root — should find the lock
|
|
149
148
|
const lock = readCrashLock(projectRoot);
|