gsd-pi 2.78.1-dev.b0759e59b → 2.78.1-dev.d8826a445
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 +8 -5
- package/dist/headless-recover.d.ts +23 -0
- package/dist/headless-recover.js +93 -0
- package/dist/headless.js +9 -0
- package/dist/help-text.js +1 -0
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/browser-tools/tools/intent.js +8 -1
- package/dist/resources/extensions/gsd/auto/phases.js +7 -2
- package/dist/resources/extensions/gsd/auto/session.js +3 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +7 -58
- package/dist/resources/extensions/gsd/auto-post-unit.js +14 -28
- package/dist/resources/extensions/gsd/auto-start.js +1 -8
- package/dist/resources/extensions/gsd/auto-worktree.js +244 -216
- package/dist/resources/extensions/gsd/auto.js +86 -7
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +1 -1
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +9 -77
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +17 -16
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +67 -55
- package/dist/resources/extensions/gsd/commands-codebase.js +2 -2
- package/dist/resources/extensions/gsd/commands-handlers.js +5 -5
- package/dist/resources/extensions/gsd/commands-logs.js +2 -2
- package/dist/resources/extensions/gsd/commands-scan.js +2 -2
- package/dist/resources/extensions/gsd/commands-ship.js +2 -2
- package/dist/resources/extensions/gsd/commands-workflow-templates.js +5 -5
- package/dist/resources/extensions/gsd/db-writer.js +106 -95
- package/dist/resources/extensions/gsd/delegation-policy.js +155 -0
- package/dist/resources/extensions/gsd/dispatch-guard.js +6 -10
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +2 -2
- package/dist/resources/extensions/gsd/gsd-db.js +268 -8
- package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
- package/dist/resources/extensions/gsd/guided-flow.js +141 -32
- package/dist/resources/extensions/gsd/markdown-renderer.js +14 -51
- package/dist/resources/extensions/gsd/metrics.js +287 -1
- package/dist/resources/extensions/gsd/parallel-merge.js +14 -13
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +5 -2
- package/dist/resources/extensions/gsd/paths.js +114 -9
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +4 -4
- package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -3
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +8 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +22 -7
- package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +6 -2
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +6 -0
- package/dist/resources/extensions/gsd/queue-order.js +6 -1
- package/dist/resources/extensions/gsd/rethink.js +2 -2
- package/dist/resources/extensions/gsd/state.js +91 -372
- package/dist/resources/extensions/gsd/templates/project.md +10 -0
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +6 -5
- package/dist/resources/extensions/gsd/tools/complete-slice.js +7 -12
- package/dist/resources/extensions/gsd/tools/complete-task.js +19 -31
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +7 -5
- package/dist/resources/extensions/gsd/workflow-manifest.js +2 -1
- package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +3 -21
- package/dist/resources/extensions/gsd/workflow-mcp.js +2 -2
- package/dist/resources/extensions/gsd/workflow-reconcile.js +3 -3
- package/dist/resources/extensions/gsd/workspace.js +59 -0
- package/dist/resources/extensions/gsd/worktree-command.js +4 -3
- package/dist/resources/extensions/gsd/worktree-resolver.js +15 -2
- package/dist/resources/extensions/gsd/write-intercept.js +3 -3
- 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 +12 -12
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +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 +12 -12
- package/dist/web/standalone/.next/server/chunks/6336.js +1 -0
- package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/mcp-server/README.md +2 -11
- package/packages/mcp-server/dist/remote-questions.d.ts +27 -0
- package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -1
- package/packages/mcp-server/dist/remote-questions.js +28 -0
- package/packages/mcp-server/dist/remote-questions.js.map +1 -1
- package/packages/mcp-server/dist/server.d.ts +28 -0
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +94 -4
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +6 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +56 -2
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/mcp-server.test.ts +226 -0
- package/packages/mcp-server/src/parse-workflow-args.test.ts +80 -0
- package/packages/mcp-server/src/remote-questions.test.ts +103 -0
- package/packages/mcp-server/src/remote-questions.ts +35 -0
- package/packages/mcp-server/src/server.ts +129 -6
- package/packages/mcp-server/src/workflow-tools.ts +62 -3
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/browser-tools/tools/intent.ts +13 -2
- package/src/resources/extensions/gsd/auto/phases.ts +8 -2
- package/src/resources/extensions/gsd/auto/session.ts +4 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +14 -62
- package/src/resources/extensions/gsd/auto-post-unit.ts +15 -27
- package/src/resources/extensions/gsd/auto-start.ts +1 -8
- package/src/resources/extensions/gsd/auto-worktree.ts +286 -251
- package/src/resources/extensions/gsd/auto.ts +102 -7
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +1 -1
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +9 -84
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +17 -17
- package/src/resources/extensions/gsd/bootstrap/tests/write-gate-basepath.test.ts +103 -0
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +80 -55
- package/src/resources/extensions/gsd/commands-codebase.ts +2 -2
- package/src/resources/extensions/gsd/commands-handlers.ts +5 -5
- package/src/resources/extensions/gsd/commands-logs.ts +2 -2
- package/src/resources/extensions/gsd/commands-scan.ts +2 -2
- package/src/resources/extensions/gsd/commands-ship.ts +2 -2
- package/src/resources/extensions/gsd/commands-workflow-templates.ts +5 -5
- package/src/resources/extensions/gsd/db-writer.ts +123 -94
- package/src/resources/extensions/gsd/delegation-policy.ts +197 -0
- package/src/resources/extensions/gsd/dispatch-guard.ts +6 -11
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +2 -2
- package/src/resources/extensions/gsd/gsd-db.ts +269 -8
- package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
- package/src/resources/extensions/gsd/guided-flow.ts +181 -32
- package/src/resources/extensions/gsd/markdown-renderer.ts +13 -64
- package/src/resources/extensions/gsd/metrics.ts +321 -1
- package/src/resources/extensions/gsd/parallel-merge.ts +14 -13
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +5 -2
- package/src/resources/extensions/gsd/paths.ts +122 -9
- package/src/resources/extensions/gsd/prompts/complete-slice.md +4 -4
- package/src/resources/extensions/gsd/prompts/execute-task.md +3 -3
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +8 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +22 -7
- package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +6 -2
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +6 -0
- package/src/resources/extensions/gsd/queue-order.ts +6 -1
- package/src/resources/extensions/gsd/rethink.ts +2 -2
- package/src/resources/extensions/gsd/state.ts +91 -389
- package/src/resources/extensions/gsd/templates/project.md +10 -0
- package/src/resources/extensions/gsd/tests/artifact-corruption-2630.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +14 -14
- package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +6 -0
- package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +21 -34
- package/src/resources/extensions/gsd/tests/auto-session-scope.test.ts +331 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +176 -0
- package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +6 -7
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +8 -6
- package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +12 -27
- package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +18 -5
- package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/db-writer-path-containment.test.ts +152 -0
- package/src/resources/extensions/gsd/tests/db-writer-root-artifact.test.ts +221 -0
- package/src/resources/extensions/gsd/tests/db-writer-scope.test.ts +230 -0
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +14 -16
- package/src/resources/extensions/gsd/tests/delegation-policy.test.ts +151 -0
- package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +6 -5
- package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +10 -38
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +136 -56
- package/src/resources/extensions/gsd/tests/derive-state-draft.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +119 -61
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/dispatch-backgroundable-annotation.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +6 -20
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +4 -5
- package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +14 -15
- package/src/resources/extensions/gsd/tests/draft-promotion.test.ts +3 -23
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +11 -16
- package/src/resources/extensions/gsd/tests/escalation.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +193 -0
- package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound-corrections.test.ts +246 -0
- package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound.test.ts +218 -0
- package/src/resources/extensions/gsd/tests/gsd-db-failed-open-restore.test.ts +117 -0
- package/src/resources/extensions/gsd/tests/gsd-db-workspace-scope.test.ts +226 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/gsd-root-canonical.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/gsd-root-home-guard.test.ts +68 -5
- package/src/resources/extensions/gsd/tests/gsdroot-worktree-detection.test.ts +15 -36
- package/src/resources/extensions/gsd/tests/guided-flow-prompt-consolidation.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/handler-worktree-write-isolation.test.ts +57 -0
- package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +15 -15
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +15 -5
- package/src/resources/extensions/gsd/tests/integration/workspace-collapse-integration.test.ts +371 -0
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +14 -8
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/metrics-atomic-merge.test.ts +222 -0
- package/src/resources/extensions/gsd/tests/metrics-lock-hardening.test.ts +400 -0
- package/src/resources/extensions/gsd/tests/metrics-lock-not-acquired.test.ts +141 -0
- package/src/resources/extensions/gsd/tests/metrics-lock-retry-sleep.test.ts +287 -0
- package/src/resources/extensions/gsd/tests/metrics-prune-cache-invalidation.test.ts +149 -0
- package/src/resources/extensions/gsd/tests/metrics-scope.test.ts +378 -0
- package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +329 -0
- package/src/resources/extensions/gsd/tests/park-milestone.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/path-cache-decoupled.test.ts +209 -0
- package/src/resources/extensions/gsd/tests/path-normalization-unified.test.ts +175 -0
- package/src/resources/extensions/gsd/tests/paths-cache.test.ts +170 -0
- package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +25 -16
- package/src/resources/extensions/gsd/tests/projection-regression.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +150 -7
- package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +184 -0
- package/src/resources/extensions/gsd/tests/register-hooks-compaction-checkpoint.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +28 -16
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/resolve-ts.mjs +4 -0
- package/src/resources/extensions/gsd/tests/resume-missing-worktree-warning.test.ts +209 -0
- package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +3 -4
- package/src/resources/extensions/gsd/tests/slice-disk-reconcile.test.ts +10 -56
- package/src/resources/extensions/gsd/tests/stale-slice-rows.test.ts +15 -16
- package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +23 -27
- package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +13 -14
- package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/sync-layer-scope.test.ts +453 -0
- package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +10 -33
- package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +162 -0
- package/src/resources/extensions/gsd/tests/teardown-cleanup-parity.test.ts +102 -0
- package/src/resources/extensions/gsd/tests/teardown-failure-clears-registry.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +7 -8
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +9 -15
- package/src/resources/extensions/gsd/tests/validator-scope-parity.test.ts +239 -0
- package/src/resources/extensions/gsd/tests/workflow-logger-wiring.test.ts +12 -7
- package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +26 -3
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +9 -15
- package/src/resources/extensions/gsd/tests/workspace.test.ts +190 -0
- package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +13 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +65 -71
- package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +26 -151
- package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +35 -35
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +67 -52
- package/src/resources/extensions/gsd/tests/write-intercept.test.ts +1 -1
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +7 -5
- package/src/resources/extensions/gsd/tools/complete-slice.ts +7 -14
- package/src/resources/extensions/gsd/tools/complete-task.ts +19 -34
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +7 -5
- package/src/resources/extensions/gsd/workflow-manifest.ts +4 -1
- package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +2 -18
- package/src/resources/extensions/gsd/workflow-mcp.ts +2 -2
- package/src/resources/extensions/gsd/workflow-reconcile.ts +3 -3
- package/src/resources/extensions/gsd/workspace.ts +95 -0
- package/src/resources/extensions/gsd/worktree-command.ts +4 -3
- package/src/resources/extensions/gsd/worktree-resolver.ts +16 -2
- package/src/resources/extensions/gsd/write-intercept.ts +3 -3
- package/dist/web/standalone/.next/server/chunks/8527.js +0 -1
- /package/dist/web/standalone/.next/static/{rk1EN3FQTE6Z1yalkW_GE → AT5qi39nKXkdmQIOIoh0f}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{rk1EN3FQTE6Z1yalkW_GE → AT5qi39nKXkdmQIOIoh0f}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// GSD-2 + Regression tests for missing-worktree warning on resume (M4 fix)
|
|
2
|
+
//
|
|
3
|
+
// When paused-session.json records a worktreePath that no longer exists on disk,
|
|
4
|
+
// the resume path must emit a logWarning("session", ...) describing the situation
|
|
5
|
+
// rather than silently falling back to project-root mode.
|
|
6
|
+
//
|
|
7
|
+
// Strategy: drive the exported _warnIfWorktreeMissingForTest seam directly
|
|
8
|
+
// (mirrors the exact conditional used at the two resume sites in auto.ts),
|
|
9
|
+
// and independently verify the scope fallback via createWorkspace/scopeMilestone
|
|
10
|
+
// as in auto-session-scope.test.ts.
|
|
11
|
+
|
|
12
|
+
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
13
|
+
import assert from "node:assert/strict";
|
|
14
|
+
import { mkdtempSync, mkdirSync, rmSync, realpathSync } from "node:fs";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import { tmpdir } from "node:os";
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
logWarning,
|
|
20
|
+
peekLogs,
|
|
21
|
+
_resetLogs,
|
|
22
|
+
setStderrLoggingEnabled,
|
|
23
|
+
} from "../workflow-logger.ts";
|
|
24
|
+
import { _warnIfWorktreeMissingForTest } from "../auto.ts";
|
|
25
|
+
import { AutoSession } from "../auto/session.ts";
|
|
26
|
+
import { createWorkspace, scopeMilestone } from "../workspace.ts";
|
|
27
|
+
|
|
28
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
function makeProjectDir(): string {
|
|
31
|
+
const dir = realpathSync(mkdtempSync(join(tmpdir(), "gsd-resume-warn-test-")));
|
|
32
|
+
mkdirSync(join(dir, ".gsd", "milestones"), { recursive: true });
|
|
33
|
+
return dir;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Mirror the rebuildScope() fallback from auto.ts when worktree is missing.
|
|
37
|
+
function applyProjectRootScope(
|
|
38
|
+
s: AutoSession,
|
|
39
|
+
projectDir: string,
|
|
40
|
+
milestoneId: string,
|
|
41
|
+
): void {
|
|
42
|
+
const workspace = createWorkspace(projectDir);
|
|
43
|
+
s.scope = scopeMilestone(workspace, milestoneId);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ─── Tests ───────────────────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
describe("resume: missing worktree warning emission", () => {
|
|
49
|
+
let projectDir: string;
|
|
50
|
+
|
|
51
|
+
beforeEach(() => {
|
|
52
|
+
projectDir = makeProjectDir();
|
|
53
|
+
_resetLogs();
|
|
54
|
+
setStderrLoggingEnabled(false);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
afterEach(() => {
|
|
58
|
+
setStderrLoggingEnabled(true);
|
|
59
|
+
rmSync(projectDir, { recursive: true, force: true });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("logWarning is called when worktreePath is set but directory is missing", () => {
|
|
63
|
+
const missingPath = join(projectDir, ".gsd", "worktrees", "M001-nonexistent");
|
|
64
|
+
// missingPath was never created — existsSync returns false
|
|
65
|
+
|
|
66
|
+
const warned = _warnIfWorktreeMissingForTest(missingPath, "M001");
|
|
67
|
+
|
|
68
|
+
assert.equal(warned, true, "_warnIfWorktreeMissingForTest should return true when path is missing");
|
|
69
|
+
|
|
70
|
+
const logs = peekLogs();
|
|
71
|
+
assert.equal(logs.length, 1, "exactly one warning should be emitted");
|
|
72
|
+
assert.equal(logs[0].severity, "warn");
|
|
73
|
+
assert.equal(logs[0].component, "session");
|
|
74
|
+
assert.ok(
|
|
75
|
+
logs[0].message.includes(missingPath),
|
|
76
|
+
`warning message should include the missing path; got: ${logs[0].message}`,
|
|
77
|
+
);
|
|
78
|
+
assert.ok(
|
|
79
|
+
logs[0].message.includes("missing"),
|
|
80
|
+
"warning message should mention 'missing'",
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("logWarning message includes milestone ID", () => {
|
|
85
|
+
const missingPath = join(projectDir, ".gsd", "worktrees", "M042-gone");
|
|
86
|
+
_warnIfWorktreeMissingForTest(missingPath, "M042");
|
|
87
|
+
|
|
88
|
+
const logs = peekLogs();
|
|
89
|
+
assert.equal(logs.length, 1);
|
|
90
|
+
assert.equal(logs[0].context?.milestoneId, "M042");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("logWarning is NOT called when worktreePath is null", () => {
|
|
94
|
+
const warned = _warnIfWorktreeMissingForTest(null, "M001");
|
|
95
|
+
|
|
96
|
+
assert.equal(warned, false);
|
|
97
|
+
assert.equal(peekLogs().length, 0, "no warning when worktreePath is null");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("logWarning is NOT called when worktreePath is undefined", () => {
|
|
101
|
+
const warned = _warnIfWorktreeMissingForTest(undefined, "M001");
|
|
102
|
+
|
|
103
|
+
assert.equal(warned, false);
|
|
104
|
+
assert.equal(peekLogs().length, 0, "no warning when worktreePath is undefined");
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("logWarning is NOT called when worktreePath exists on disk", () => {
|
|
108
|
+
const existingWorktree = join(projectDir, ".gsd", "worktrees", "M001");
|
|
109
|
+
mkdirSync(existingWorktree, { recursive: true });
|
|
110
|
+
|
|
111
|
+
const warned = _warnIfWorktreeMissingForTest(existingWorktree, "M001");
|
|
112
|
+
|
|
113
|
+
assert.equal(warned, false, "no warning when path exists");
|
|
114
|
+
assert.equal(peekLogs().length, 0);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("warning message mentions project-root fallback action", () => {
|
|
118
|
+
const missingPath = join(projectDir, ".gsd", "worktrees", "M099-deleted");
|
|
119
|
+
_warnIfWorktreeMissingForTest(missingPath, "M099");
|
|
120
|
+
|
|
121
|
+
const logs = peekLogs();
|
|
122
|
+
assert.equal(logs.length, 1);
|
|
123
|
+
assert.ok(
|
|
124
|
+
logs[0].message.includes("project-root mode"),
|
|
125
|
+
"warning should mention project-root mode fallback",
|
|
126
|
+
);
|
|
127
|
+
assert.ok(
|
|
128
|
+
logs[0].message.includes("gsd-debug"),
|
|
129
|
+
"warning should suggest /gsd-debug recovery action",
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe("resume: scope fallback to project-root mode when worktree is missing", () => {
|
|
135
|
+
let s: AutoSession;
|
|
136
|
+
let projectDir: string;
|
|
137
|
+
|
|
138
|
+
beforeEach(() => {
|
|
139
|
+
projectDir = makeProjectDir();
|
|
140
|
+
s = new AutoSession();
|
|
141
|
+
_resetLogs();
|
|
142
|
+
setStderrLoggingEnabled(false);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
afterEach(() => {
|
|
146
|
+
setStderrLoggingEnabled(true);
|
|
147
|
+
rmSync(projectDir, { recursive: true, force: true });
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("scope.workspace.mode is 'project' after fallback from missing worktree", () => {
|
|
151
|
+
const mid = "M001";
|
|
152
|
+
s.originalBasePath = projectDir;
|
|
153
|
+
s.currentMilestoneId = mid;
|
|
154
|
+
|
|
155
|
+
// Simulate auto.ts resume path: worktreePath is set but missing → use projectDir
|
|
156
|
+
const missingPath = join(projectDir, ".gsd", "worktrees", mid);
|
|
157
|
+
_warnIfWorktreeMissingForTest(missingPath, mid);
|
|
158
|
+
|
|
159
|
+
// Fallback: use originalBasePath (project root)
|
|
160
|
+
applyProjectRootScope(s, projectDir, mid);
|
|
161
|
+
|
|
162
|
+
assert.ok(s.scope, "scope should be set after fallback");
|
|
163
|
+
assert.equal(s.scope.workspace.mode, "project");
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("scope.milestoneId is preserved after project-root fallback", () => {
|
|
167
|
+
const mid = "M002";
|
|
168
|
+
s.originalBasePath = projectDir;
|
|
169
|
+
s.currentMilestoneId = mid;
|
|
170
|
+
|
|
171
|
+
const missingPath = join(projectDir, ".gsd", "worktrees", mid);
|
|
172
|
+
_warnIfWorktreeMissingForTest(missingPath, mid);
|
|
173
|
+
|
|
174
|
+
applyProjectRootScope(s, projectDir, mid);
|
|
175
|
+
|
|
176
|
+
assert.ok(s.scope, "scope should be set");
|
|
177
|
+
assert.equal(s.scope.milestoneId, mid);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("does not throw when worktree path is missing and scope fallback is applied", () => {
|
|
181
|
+
const mid = "M003";
|
|
182
|
+
s.originalBasePath = projectDir;
|
|
183
|
+
s.currentMilestoneId = mid;
|
|
184
|
+
|
|
185
|
+
const missingPath = join(projectDir, ".gsd", "worktrees", mid);
|
|
186
|
+
|
|
187
|
+
assert.doesNotThrow(() => {
|
|
188
|
+
_warnIfWorktreeMissingForTest(missingPath, mid);
|
|
189
|
+
applyProjectRootScope(s, projectDir, mid);
|
|
190
|
+
}, "resume with missing worktree must not throw");
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("warning is emitted once per missing worktree — no double-emission", () => {
|
|
194
|
+
const mid = "M004";
|
|
195
|
+
const missingPath = join(projectDir, ".gsd", "worktrees", mid);
|
|
196
|
+
|
|
197
|
+
_warnIfWorktreeMissingForTest(missingPath, mid);
|
|
198
|
+
|
|
199
|
+
// Simulating the second call as would happen if the resume-re-entry site
|
|
200
|
+
// also fires (e.g. pausedSession and freshStartAssessment both carry the path)
|
|
201
|
+
_warnIfWorktreeMissingForTest(missingPath, mid);
|
|
202
|
+
|
|
203
|
+
const logs = peekLogs();
|
|
204
|
+
// Two calls → two warnings (one per site — consistent with the two sites in auto.ts)
|
|
205
|
+
assert.equal(logs.length, 2, "each call to the seam emits one warning");
|
|
206
|
+
assert.equal(logs[0].component, "session");
|
|
207
|
+
assert.equal(logs[1].component, "session");
|
|
208
|
+
});
|
|
209
|
+
});
|
|
@@ -149,7 +149,7 @@ test("rogue detection: DB not available → returns empty array (graceful degrad
|
|
|
149
149
|
}
|
|
150
150
|
});
|
|
151
151
|
|
|
152
|
-
test("rogue detection: slice summary on disk, no DB
|
|
152
|
+
test("rogue detection: slice summary on disk, no DB completion → detected as rogue without DB import", () => {
|
|
153
153
|
const basePath = createTmpBase();
|
|
154
154
|
const dbPath = join(basePath, ".gsd", "gsd.db");
|
|
155
155
|
mkdirSync(join(basePath, ".gsd"), { recursive: true });
|
|
@@ -160,10 +160,9 @@ test("rogue detection: slice summary on disk, no DB row → auto-remediated (not
|
|
|
160
160
|
const summaryPath = createSliceSummaryOnDisk(basePath, "M001", "S01");
|
|
161
161
|
assert.ok(existsSync(summaryPath), "Slice summary file should exist on disk");
|
|
162
162
|
|
|
163
|
-
// Fix #3633: stale slice DB status is auto-remediated via updateSliceStatus()
|
|
164
|
-
// instead of being reported as rogue, so rogues array should be empty.
|
|
165
163
|
const rogues = detectRogueFileWrites("complete-slice", "M001/S01", basePath);
|
|
166
|
-
assert.equal(rogues.length,
|
|
164
|
+
assert.equal(rogues.length, 1, "Should report stale disk summary instead of mutating DB");
|
|
165
|
+
assert.equal(rogues[0]?.path, summaryPath);
|
|
167
166
|
} finally {
|
|
168
167
|
closeDatabase();
|
|
169
168
|
rmSync(basePath, { recursive: true, force: true });
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* slice-disk-reconcile.test.ts — #2533
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* blocks. deriveStateFromDb must reconcile disk slices into the DB, just as
|
|
7
|
-
* it already does for milestones (#2416).
|
|
4
|
+
* DB-authoritative state: slices that exist only in ROADMAP.md are projections
|
|
5
|
+
* and must not be imported into the SQLite database by deriveStateFromDb().
|
|
8
6
|
*
|
|
9
7
|
* Scenario: M001 has a ROADMAP with S01-S04. S01 and S02 have SUMMARY files
|
|
10
8
|
* (complete on disk). S03 depends on S01. Only S04 is in the DB (depends on
|
|
@@ -70,7 +68,7 @@ const ROADMAP_CONTENT = `# M001: Test Milestone
|
|
|
70
68
|
`;
|
|
71
69
|
|
|
72
70
|
async function testMissingSlicesCauseBlock(): Promise<void> {
|
|
73
|
-
console.log("\n--- Test: missing DB slices
|
|
71
|
+
console.log("\n--- Test: missing DB slices are not imported from ROADMAP.md ---");
|
|
74
72
|
|
|
75
73
|
const base = createFixtureBase();
|
|
76
74
|
const dbPath = join(base, ".gsd", "gsd.db");
|
|
@@ -96,51 +94,11 @@ async function testMissingSlicesCauseBlock(): Promise<void> {
|
|
|
96
94
|
invalidateStateCache();
|
|
97
95
|
const state = await deriveStateFromDb(base);
|
|
98
96
|
|
|
99
|
-
// After the fix, slices S01-S03 should be reconciled into DB
|
|
100
97
|
const dbSlices = getMilestoneSlices("M001");
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
// S01 and S02 should be marked complete (have SUMMARY files)
|
|
107
|
-
const s01 = dbSlices.find(s => s.id === "S01");
|
|
108
|
-
assertTrue(s01 !== undefined, "S01 should exist in DB after reconciliation");
|
|
109
|
-
if (s01) {
|
|
110
|
-
assertEq(s01.status, "complete", "S01 should be 'complete' (has SUMMARY on disk)");
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const s02 = dbSlices.find(s => s.id === "S02");
|
|
114
|
-
assertTrue(s02 !== undefined, "S02 should exist in DB after reconciliation");
|
|
115
|
-
if (s02) {
|
|
116
|
-
assertEq(s02.status, "complete", "S02 should be 'complete' (has SUMMARY on disk)");
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// S03 should be pending (no SUMMARY)
|
|
120
|
-
const s03 = dbSlices.find(s => s.id === "S03");
|
|
121
|
-
assertTrue(s03 !== undefined, "S03 should exist in DB after reconciliation");
|
|
122
|
-
if (s03) {
|
|
123
|
-
assertEq(s03.status, "pending", "S03 should be 'pending' (no SUMMARY on disk)");
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// The state should NOT be blocked — S03 should be eligible (S01 dep satisfied)
|
|
127
|
-
assertTrue(
|
|
128
|
-
state.phase !== "blocked",
|
|
129
|
-
`Phase should not be 'blocked' after reconciliation, got '${state.phase}'`,
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
// Active slice should be S03 (S01 dep met, S03 is first incomplete with satisfied deps)
|
|
133
|
-
assertTrue(
|
|
134
|
-
state.activeSlice !== null,
|
|
135
|
-
"There should be an active slice after reconciliation",
|
|
136
|
-
);
|
|
137
|
-
if (state.activeSlice) {
|
|
138
|
-
assertEq(
|
|
139
|
-
state.activeSlice.id,
|
|
140
|
-
"S03",
|
|
141
|
-
"Active slice should be S03 (its dependency S01 is complete) (#2533)",
|
|
142
|
-
);
|
|
143
|
-
}
|
|
98
|
+
assertEq(dbSlices.length, 1, `Only DB slice S04 should remain, got ${dbSlices.length}`);
|
|
99
|
+
assertEq(dbSlices[0]?.id, "S04", "Disk-only slices are not inserted");
|
|
100
|
+
assertEq(state.phase, "blocked", "DB-only S04 remains blocked on missing DB dependency S03");
|
|
101
|
+
assertEq(state.activeSlice, null, "No active slice is inferred from roadmap-only rows");
|
|
144
102
|
} finally {
|
|
145
103
|
closeDatabase();
|
|
146
104
|
cleanup(base);
|
|
@@ -148,7 +106,7 @@ async function testMissingSlicesCauseBlock(): Promise<void> {
|
|
|
148
106
|
}
|
|
149
107
|
|
|
150
108
|
async function testSliceReconciliationIdempotent(): Promise<void> {
|
|
151
|
-
console.log("\n--- Test:
|
|
109
|
+
console.log("\n--- Test: disk-only slices remain absent on repeated derives ---");
|
|
152
110
|
|
|
153
111
|
const base = createFixtureBase();
|
|
154
112
|
const dbPath = join(base, ".gsd", "gsd.db");
|
|
@@ -178,11 +136,7 @@ async function testSliceReconciliationIdempotent(): Promise<void> {
|
|
|
178
136
|
assertEq(s01.status, "complete", "S01 status should remain 'complete' (not overwritten)");
|
|
179
137
|
}
|
|
180
138
|
|
|
181
|
-
|
|
182
|
-
assertTrue(
|
|
183
|
-
dbSlices.length === 4,
|
|
184
|
-
`Should have 4 slices after reconciliation (existing + new), got ${dbSlices.length}`,
|
|
185
|
-
);
|
|
139
|
+
assertEq(dbSlices.length, 1, `Only existing DB slice should remain, got ${dbSlices.length}`);
|
|
186
140
|
} finally {
|
|
187
141
|
closeDatabase();
|
|
188
142
|
cleanup(base);
|
|
@@ -218,7 +172,7 @@ async function testNoRoadmapSkipsReconciliation(): Promise<void> {
|
|
|
218
172
|
}
|
|
219
173
|
|
|
220
174
|
async function main(): Promise<void> {
|
|
221
|
-
console.log("\n===
|
|
175
|
+
console.log("\n=== deriveStateFromDb does not reconcile disk slices ===");
|
|
222
176
|
|
|
223
177
|
await testMissingSlicesCauseBlock();
|
|
224
178
|
await testSliceReconciliationIdempotent();
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* stale-slice-rows.test.ts
|
|
2
|
+
* stale-slice-rows.test.ts
|
|
3
3
|
*
|
|
4
|
-
* Verify that state.ts
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* doneSliceIds from stale DB rows and downstream slices stay blocked.
|
|
4
|
+
* Verify that state.ts no longer treats slice SUMMARY.md projections as
|
|
5
|
+
* authority for DB slice status. Slice rows must be updated through DB-backed
|
|
6
|
+
* completion/import APIs.
|
|
8
7
|
*/
|
|
9
8
|
|
|
10
9
|
import { describe, test } from "node:test";
|
|
@@ -16,26 +15,26 @@ import { fileURLToPath } from "node:url";
|
|
|
16
15
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
16
|
const sourceFile = join(__dirname, "..", "state.ts");
|
|
18
17
|
|
|
19
|
-
describe("stale slice row
|
|
18
|
+
describe("stale slice row DB-authoritative boundary", () => {
|
|
20
19
|
const source = readFileSync(sourceFile, "utf-8");
|
|
21
20
|
|
|
22
|
-
test("
|
|
23
|
-
assert.
|
|
21
|
+
test("does not import updateSliceStatus into state derivation", () => {
|
|
22
|
+
assert.doesNotMatch(source, /import\s*\{[^}]*updateSliceStatus[^}]*\}\s*from/);
|
|
24
23
|
});
|
|
25
24
|
|
|
26
|
-
test("
|
|
27
|
-
assert.
|
|
25
|
+
test("does not scan DB slice rows for disk SUMMARY reconciliation", () => {
|
|
26
|
+
assert.doesNotMatch(source, /dbSlice/);
|
|
28
27
|
});
|
|
29
28
|
|
|
30
|
-
test("
|
|
31
|
-
assert.
|
|
29
|
+
test("does not resolve slice SUMMARY to mutate DB state", () => {
|
|
30
|
+
assert.doesNotMatch(source, /resolveSliceFile\(basePath,\s*mid,\s*dbSlice\.id,\s*["']SUMMARY["']\)/);
|
|
32
31
|
});
|
|
33
32
|
|
|
34
|
-
test("
|
|
35
|
-
assert.
|
|
33
|
+
test("does not call updateSliceStatus from state derivation", () => {
|
|
34
|
+
assert.doesNotMatch(source, /updateSliceStatus\(/);
|
|
36
35
|
});
|
|
37
36
|
|
|
38
|
-
test("
|
|
39
|
-
assert.match(source,
|
|
37
|
+
test("documents markdown projections as non-authoritative", () => {
|
|
38
|
+
assert.match(source, /Markdown files are projections only/);
|
|
40
39
|
});
|
|
41
40
|
});
|
|
@@ -858,11 +858,11 @@ describe("state-machine-full-walkthrough", () => {
|
|
|
858
858
|
});
|
|
859
859
|
|
|
860
860
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
861
|
-
//
|
|
861
|
+
// DB-AUTHORITATIVE DERIVATION
|
|
862
862
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
863
863
|
|
|
864
|
-
describe("
|
|
865
|
-
test("DB: task with SUMMARY on disk but DB says pending →
|
|
864
|
+
describe("DB-authoritative derivation", () => {
|
|
865
|
+
test("DB: task with SUMMARY on disk but DB says pending → DB remains authoritative", async () => {
|
|
866
866
|
const base = createFixtureBase();
|
|
867
867
|
const dbPath = join(base, ".gsd", "gsd.db");
|
|
868
868
|
openDatabase(dbPath);
|
|
@@ -875,19 +875,19 @@ describe("state-machine-full-walkthrough", () => {
|
|
|
875
875
|
writeRoadmap(base, "M001", standardRoadmap());
|
|
876
876
|
writePlan(base, "M001", "S01", standardPlan());
|
|
877
877
|
|
|
878
|
-
// Write SUMMARY files on disk for both tasks
|
|
878
|
+
// Write SUMMARY files on disk for both tasks. These are projections and
|
|
879
|
+
// must not complete pending DB tasks during runtime derivation.
|
|
879
880
|
writeTaskSummary(base, "M001", "S01", "T01");
|
|
880
881
|
writeTaskSummary(base, "M001", "S01", "T02");
|
|
881
882
|
|
|
882
883
|
invalidateStateCache();
|
|
883
884
|
const state = await deriveStateFromDb(base);
|
|
884
885
|
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
assert.equal(state.phase, "summarizing", "reconciliation should advance past pending tasks");
|
|
886
|
+
assert.equal(state.phase, "executing", "disk SUMMARY projections must not complete DB tasks");
|
|
887
|
+
assert.equal(state.activeTask?.id, "T01", "first pending DB task remains active");
|
|
888
888
|
});
|
|
889
889
|
|
|
890
|
-
test("empty DB with disk milestones → disk-to-DB sync
|
|
890
|
+
test("empty DB with disk milestones → no runtime disk-to-DB sync", async () => {
|
|
891
891
|
const base = createFixtureBase();
|
|
892
892
|
writeContext(base, "M001", "# M001: Test\n\nContext.");
|
|
893
893
|
|
|
@@ -899,11 +899,10 @@ describe("state-machine-full-walkthrough", () => {
|
|
|
899
899
|
invalidateStateCache();
|
|
900
900
|
const state = await deriveState(base);
|
|
901
901
|
|
|
902
|
-
//
|
|
902
|
+
// Runtime derivation must not import disk milestones into the DB.
|
|
903
903
|
const after = getAllMilestones();
|
|
904
|
-
assert.
|
|
905
|
-
assert.equal(
|
|
906
|
-
assert.ok(state.activeMilestone !== null);
|
|
904
|
+
assert.equal(after.length, 0, "DB should remain empty without explicit migration");
|
|
905
|
+
assert.equal(state.activeMilestone, null, "disk milestone is ignored while DB is authoritative");
|
|
907
906
|
});
|
|
908
907
|
|
|
909
908
|
test("ghost milestone (empty dir) → NOT in registry", async () => {
|
|
@@ -1063,7 +1062,7 @@ describe("state-machine-full-walkthrough", () => {
|
|
|
1063
1062
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1064
1063
|
|
|
1065
1064
|
describe("Recovery: DB has slice but no task rows (partial migration)", () => {
|
|
1066
|
-
test("DB tasks empty but PLAN on disk has tasks →
|
|
1065
|
+
test("DB tasks empty but PLAN on disk has tasks → stays planning", async () => {
|
|
1067
1066
|
const base = createFixtureBase();
|
|
1068
1067
|
const dbPath = join(base, ".gsd", "gsd.db");
|
|
1069
1068
|
openDatabase(dbPath);
|
|
@@ -1078,15 +1077,13 @@ describe("state-machine-full-walkthrough", () => {
|
|
|
1078
1077
|
invalidateStateCache();
|
|
1079
1078
|
const state = await deriveStateFromDb(base);
|
|
1080
1079
|
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
assert.equal(state.phase, "executing",
|
|
1084
|
-
"reconciled plan-file tasks → executing (not stuck in planning)");
|
|
1080
|
+
assert.equal(state.phase, "planning",
|
|
1081
|
+
"PLAN.md projection must not import DB tasks during runtime derivation");
|
|
1085
1082
|
});
|
|
1086
1083
|
});
|
|
1087
1084
|
|
|
1088
1085
|
describe("Failure: partial SUMMARY reconciliation", () => {
|
|
1089
|
-
test("only one task has SUMMARY, other still pending → executing
|
|
1086
|
+
test("only one task has SUMMARY, other still pending → executing first DB-pending task", async () => {
|
|
1090
1087
|
const base = createFixtureBase();
|
|
1091
1088
|
const dbPath = join(base, ".gsd", "gsd.db");
|
|
1092
1089
|
openDatabase(dbPath);
|
|
@@ -1104,9 +1101,8 @@ describe("state-machine-full-walkthrough", () => {
|
|
|
1104
1101
|
invalidateStateCache();
|
|
1105
1102
|
const state = await deriveStateFromDb(base);
|
|
1106
1103
|
|
|
1107
|
-
// T01 reconciled to complete, T02 still pending → executing T02
|
|
1108
1104
|
assert.equal(state.phase, "executing");
|
|
1109
|
-
assert.equal(state.activeTask?.id, "
|
|
1105
|
+
assert.equal(state.activeTask?.id, "T01", "disk SUMMARY must not advance past pending DB task");
|
|
1110
1106
|
});
|
|
1111
1107
|
});
|
|
1112
1108
|
|
|
@@ -1255,7 +1251,7 @@ describe("state-machine-full-walkthrough", () => {
|
|
|
1255
1251
|
});
|
|
1256
1252
|
|
|
1257
1253
|
describe("Failure: missing task plan files in DB path", () => {
|
|
1258
|
-
test("DB has tasks but no T##-PLAN.md files →
|
|
1254
|
+
test("DB has tasks but no T##-PLAN.md files → executing phase", async () => {
|
|
1259
1255
|
const base = createFixtureBase();
|
|
1260
1256
|
const dbPath = join(base, ".gsd", "gsd.db");
|
|
1261
1257
|
openDatabase(dbPath);
|
|
@@ -1273,8 +1269,8 @@ describe("state-machine-full-walkthrough", () => {
|
|
|
1273
1269
|
invalidateStateCache();
|
|
1274
1270
|
const state = await deriveStateFromDb(base);
|
|
1275
1271
|
|
|
1276
|
-
assert.equal(state.phase, "
|
|
1277
|
-
"
|
|
1272
|
+
assert.equal(state.phase, "executing",
|
|
1273
|
+
"DB tasks are authoritative even when task plan projections are missing");
|
|
1278
1274
|
});
|
|
1279
1275
|
});
|
|
1280
1276
|
|
|
@@ -1591,7 +1587,7 @@ describe("state-machine-full-walkthrough", () => {
|
|
|
1591
1587
|
});
|
|
1592
1588
|
|
|
1593
1589
|
describe("Failure: multiple reconciliation in single derivation", () => {
|
|
1594
|
-
test("DB has 3 stale tasks, all with SUMMARY on disk →
|
|
1590
|
+
test("DB has 3 stale tasks, all with SUMMARY on disk → first DB-pending task remains active", async () => {
|
|
1595
1591
|
const base = createFixtureBase();
|
|
1596
1592
|
const dbPath = join(base, ".gsd", "gsd.db");
|
|
1597
1593
|
openDatabase(dbPath);
|
|
@@ -1641,9 +1637,9 @@ describe("state-machine-full-walkthrough", () => {
|
|
|
1641
1637
|
invalidateStateCache();
|
|
1642
1638
|
const state = await deriveStateFromDb(base);
|
|
1643
1639
|
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1640
|
+
assert.equal(state.phase, "executing",
|
|
1641
|
+
"disk SUMMARY projections must not reconcile DB task state");
|
|
1642
|
+
assert.equal(state.activeTask?.id, "T01", "first non-closed DB task remains active");
|
|
1647
1643
|
});
|
|
1648
1644
|
});
|
|
1649
1645
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// GSD Extension - Steer Worktree Path Resolution Test
|
|
2
|
-
//
|
|
3
|
-
//
|
|
2
|
+
// Worktrees share the canonical project .gsd state root. /gsd steer writes
|
|
3
|
+
// overrides to that canonical root even when invoked with a worktree path.
|
|
4
4
|
|
|
5
5
|
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
6
6
|
import assert from "node:assert/strict";
|
|
@@ -27,32 +27,31 @@ describe("steer worktree path resolution (#3476)", () => {
|
|
|
27
27
|
rmSync(projectRoot, { recursive: true, force: true });
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
test("appendOverride writes to
|
|
30
|
+
test("appendOverride writes to canonical project .gsd/ when worktree path is used", async () => {
|
|
31
31
|
await appendOverride(worktreePath, "Use Postgres instead of SQLite", "M001/S01/T01");
|
|
32
32
|
|
|
33
|
-
// Override should be in the
|
|
33
|
+
// Override should be in the canonical project .gsd/
|
|
34
34
|
const wtOverrides = join(worktreePath, ".gsd", "OVERRIDES.md");
|
|
35
|
-
|
|
35
|
+
const rootOverrides = join(projectRoot, ".gsd", "OVERRIDES.md");
|
|
36
|
+
assert.ok(!existsSync(wtOverrides), "no override file in worktree-local .gsd/");
|
|
37
|
+
assert.ok(existsSync(rootOverrides), "override file exists in project root .gsd/");
|
|
36
38
|
|
|
37
|
-
const content = readFileSync(
|
|
39
|
+
const content = readFileSync(rootOverrides, "utf-8");
|
|
38
40
|
assert.ok(content.includes("Use Postgres instead of SQLite"), "override content is correct");
|
|
39
|
-
|
|
40
|
-
// Override should NOT be in the project root .gsd/
|
|
41
|
-
const rootOverrides = join(projectRoot, ".gsd", "OVERRIDES.md");
|
|
42
|
-
assert.ok(!existsSync(rootOverrides), "no override file in project root .gsd/");
|
|
43
41
|
});
|
|
44
42
|
|
|
45
|
-
test("loadActiveOverrides reads
|
|
43
|
+
test("loadActiveOverrides reads canonical project .gsd/ when worktree path is used", async () => {
|
|
46
44
|
await appendOverride(worktreePath, "Switch to JWT auth", "M001/S02/T01");
|
|
47
45
|
|
|
48
|
-
// Loading from worktree
|
|
46
|
+
// Loading from worktree resolves to the canonical project state root.
|
|
49
47
|
const wtOverrides = await loadActiveOverrides(worktreePath);
|
|
50
48
|
assert.equal(wtOverrides.length, 1, "one active override in worktree");
|
|
51
49
|
assert.equal(wtOverrides[0].change, "Switch to JWT auth");
|
|
52
50
|
|
|
53
|
-
// Loading from project root
|
|
51
|
+
// Loading from project root sees the same canonical override.
|
|
54
52
|
const rootOverrides = await loadActiveOverrides(projectRoot);
|
|
55
|
-
assert.equal(rootOverrides.length,
|
|
53
|
+
assert.equal(rootOverrides.length, 1, "same override visible from project root");
|
|
54
|
+
assert.equal(rootOverrides[0].change, "Switch to JWT auth");
|
|
56
55
|
});
|
|
57
56
|
|
|
58
57
|
test("appendOverride falls back to project root when no worktree exists", async () => {
|
|
@@ -135,7 +135,7 @@ test("stopAutoRemote sends SIGTERM to a live process and returns found:true", {
|
|
|
135
135
|
|
|
136
136
|
// ─── Lock path: original project root vs worktree ────────────────────────
|
|
137
137
|
|
|
138
|
-
test("lock file should be discoverable
|
|
138
|
+
test("lock file should be discoverable from project root and worktree path", () => {
|
|
139
139
|
const projectRoot = makeTmpBase();
|
|
140
140
|
const worktreePath = join(projectRoot, ".gsd", "worktrees", "M001");
|
|
141
141
|
mkdirSync(join(worktreePath, ".gsd"), { recursive: true });
|
|
@@ -149,9 +149,10 @@ test("lock file should be discoverable at project root, not worktree path", () =
|
|
|
149
149
|
assert.ok(lock, "lock should be found at project root");
|
|
150
150
|
assert.equal(lock!.unitType, "execute-task");
|
|
151
151
|
|
|
152
|
-
// Worktree path
|
|
152
|
+
// Worktree path resolves to the same canonical project .gsd lock.
|
|
153
153
|
const worktreeLock = readCrashLock(worktreePath);
|
|
154
|
-
assert.
|
|
154
|
+
assert.ok(worktreeLock, "lock should be discoverable via worktree path");
|
|
155
|
+
assert.equal(worktreeLock!.unitType, "execute-task");
|
|
155
156
|
} finally {
|
|
156
157
|
cleanup(projectRoot);
|
|
157
158
|
}
|