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
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
insertSlice,
|
|
21
21
|
insertTask,
|
|
22
22
|
} from '../gsd-db.ts';
|
|
23
|
-
import { migrateHierarchyToDb } from '../md-importer.ts';
|
|
23
|
+
import { migrateFromMarkdown, migrateHierarchyToDb } from '../md-importer.ts';
|
|
24
24
|
import type { GSDState } from '../types.ts';
|
|
25
25
|
|
|
26
26
|
// ─── Fixture Helpers ───────────────────────────────────────────────────────
|
|
@@ -479,12 +479,13 @@ skills_used: []
|
|
|
479
479
|
|
|
480
480
|
// Step 2: Migrate markdown to DB
|
|
481
481
|
openDatabase(':memory:');
|
|
482
|
-
const counts =
|
|
482
|
+
const counts = migrateFromMarkdown(base);
|
|
483
483
|
|
|
484
484
|
// Verify migration populated correctly
|
|
485
|
-
assert.ok(counts.milestones >= 1, 'G-roundtrip: migrated milestones');
|
|
486
|
-
assert.ok(counts.slices >= 2, 'G-roundtrip: migrated slices');
|
|
487
|
-
assert.ok(counts.tasks >= 3, 'G-roundtrip: migrated tasks');
|
|
485
|
+
assert.ok(counts.hierarchy.milestones >= 1, 'G-roundtrip: migrated milestones');
|
|
486
|
+
assert.ok(counts.hierarchy.slices >= 2, 'G-roundtrip: migrated slices');
|
|
487
|
+
assert.ok(counts.hierarchy.tasks >= 3, 'G-roundtrip: migrated tasks');
|
|
488
|
+
assert.equal(counts.requirements, 3, 'G-roundtrip: migrated requirements');
|
|
488
489
|
|
|
489
490
|
// Step 3: Get DB-backed state
|
|
490
491
|
invalidateStateCache();
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* derive-state-db-disk-reconcile.test.ts — #2416
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* deriveStateFromDb reconciles disk milestones with DB milestones.
|
|
4
|
+
* DB-authoritative state: milestones that exist only as markdown projections
|
|
5
|
+
* are not imported by deriveStateFromDb(). Explicit migration/import is the
|
|
6
|
+
* only markdown-to-DB path.
|
|
8
7
|
*/
|
|
9
8
|
|
|
10
9
|
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
@@ -17,7 +16,6 @@ import {
|
|
|
17
16
|
closeDatabase,
|
|
18
17
|
insertMilestone,
|
|
19
18
|
insertSlice,
|
|
20
|
-
insertTask,
|
|
21
19
|
} from "../gsd-db.ts";
|
|
22
20
|
import { createTestContext } from "./test-helpers.ts";
|
|
23
21
|
|
|
@@ -58,7 +56,7 @@ const ROADMAP_CONTENT = `# M002: Disk-Only Milestone
|
|
|
58
56
|
`;
|
|
59
57
|
|
|
60
58
|
async function main(): Promise<void> {
|
|
61
|
-
console.log("\n===
|
|
59
|
+
console.log("\n=== deriveStateFromDb does not reconcile disk milestones ===");
|
|
62
60
|
|
|
63
61
|
// Set up: M001 in DB, M002 on disk only
|
|
64
62
|
const base = createFixtureBase();
|
|
@@ -81,12 +79,9 @@ async function main(): Promise<void> {
|
|
|
81
79
|
invalidateStateCache();
|
|
82
80
|
const state = await deriveStateFromDb(base);
|
|
83
81
|
|
|
84
|
-
// M002 should be visible in
|
|
82
|
+
// M002 is disk-only and should not be visible in DB-backed state.
|
|
85
83
|
const m002Entry = state.registry.find((m) => m.id === "M002");
|
|
86
|
-
|
|
87
|
-
m002Entry !== undefined,
|
|
88
|
-
"M002 (disk-only milestone) should appear in state.registry (#2416)",
|
|
89
|
-
);
|
|
84
|
+
assertEq(m002Entry, undefined, "M002 disk-only milestone should not appear in DB-backed state");
|
|
90
85
|
|
|
91
86
|
// M001 should still be in the registry
|
|
92
87
|
const m001Entry = state.registry.find((m) => m.id === "M001");
|
|
@@ -95,24 +90,14 @@ async function main(): Promise<void> {
|
|
|
95
90
|
"M001 (DB milestone) should still appear in state.registry",
|
|
96
91
|
);
|
|
97
92
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
state.activeMilestone !== null,
|
|
101
|
-
"There should be an active milestone",
|
|
102
|
-
);
|
|
103
|
-
if (state.activeMilestone) {
|
|
104
|
-
assertEq(
|
|
105
|
-
state.activeMilestone.id,
|
|
106
|
-
"M002",
|
|
107
|
-
"Active milestone should be M002 (disk-only, not complete) (#2416)",
|
|
108
|
-
);
|
|
109
|
-
}
|
|
93
|
+
assertEq(state.activeMilestone, null, "No active milestone should be inferred from disk-only markdown");
|
|
94
|
+
assertEq(state.phase, "complete", "DB-only complete milestone drives complete state");
|
|
110
95
|
} finally {
|
|
111
96
|
closeDatabase();
|
|
112
97
|
cleanup(base);
|
|
113
98
|
}
|
|
114
99
|
|
|
115
|
-
|
|
100
|
+
console.log("\n=== summary-only disk milestones are not imported ===");
|
|
116
101
|
|
|
117
102
|
{
|
|
118
103
|
const summaryOnlyBase = createFixtureBase();
|
|
@@ -132,20 +117,7 @@ async function main(): Promise<void> {
|
|
|
132
117
|
const state = await deriveStateFromDb(summaryOnlyBase);
|
|
133
118
|
const m002Entry = state.registry.find((m) => m.id === "M002");
|
|
134
119
|
|
|
135
|
-
|
|
136
|
-
m002Entry !== undefined,
|
|
137
|
-
"M002 summary-only disk milestone should appear in state.registry (#4974)",
|
|
138
|
-
);
|
|
139
|
-
assertEq(
|
|
140
|
-
m002Entry?.title,
|
|
141
|
-
"Summary-Only Milestone",
|
|
142
|
-
"M002 summary-only disk milestone should use parsed SUMMARY title (#4974)",
|
|
143
|
-
);
|
|
144
|
-
assertEq(
|
|
145
|
-
m002Entry?.status,
|
|
146
|
-
"complete",
|
|
147
|
-
"M002 summary-only disk milestone should reconcile as complete (#4974)",
|
|
148
|
-
);
|
|
120
|
+
assertEq(m002Entry, undefined, "M002 summary-only disk milestone should not appear without explicit import");
|
|
149
121
|
} finally {
|
|
150
122
|
closeDatabase();
|
|
151
123
|
cleanup(summaryOnlyBase);
|
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
insertArtifact,
|
|
12
12
|
isDbAvailable,
|
|
13
13
|
insertMilestone,
|
|
14
|
+
insertRequirement,
|
|
15
|
+
insertAssessment,
|
|
14
16
|
getAllMilestones,
|
|
15
17
|
insertSlice,
|
|
16
18
|
insertTask,
|
|
@@ -47,6 +49,23 @@ function insertArtifactRow(relativePath: string, content: string, opts?: {
|
|
|
47
49
|
});
|
|
48
50
|
}
|
|
49
51
|
|
|
52
|
+
function insertRequirementRow(id: string, status: string): void {
|
|
53
|
+
insertRequirement({
|
|
54
|
+
id,
|
|
55
|
+
class: 'functional',
|
|
56
|
+
status,
|
|
57
|
+
description: `${id} ${status}`,
|
|
58
|
+
why: 'test',
|
|
59
|
+
source: 'test',
|
|
60
|
+
primary_owner: '',
|
|
61
|
+
supporting_slices: '',
|
|
62
|
+
validation: '',
|
|
63
|
+
notes: '',
|
|
64
|
+
full_content: '',
|
|
65
|
+
superseded_by: null,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
50
69
|
function cleanup(base: string): void {
|
|
51
70
|
rmSync(base, { recursive: true, force: true });
|
|
52
71
|
}
|
|
@@ -114,9 +133,9 @@ describe('derive-state-db', async () => {
|
|
|
114
133
|
writeFile(base, 'milestones/M001/slices/S01/tasks/T01-PLAN.md', '# T01 Plan');
|
|
115
134
|
writeFile(base, 'REQUIREMENTS.md', REQUIREMENTS_CONTENT);
|
|
116
135
|
|
|
117
|
-
// Derive state from
|
|
136
|
+
// Derive state from the explicit legacy file-only path (no DB)
|
|
118
137
|
invalidateStateCache();
|
|
119
|
-
const fileState = await
|
|
138
|
+
const fileState = await _deriveStateImpl(base);
|
|
120
139
|
|
|
121
140
|
// Now open DB, insert matching artifacts + milestone hierarchy
|
|
122
141
|
openDatabase(':memory:');
|
|
@@ -128,6 +147,9 @@ describe('derive-state-db', async () => {
|
|
|
128
147
|
insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Second Slice', status: 'pending', risk: 'low', depends: ['S01'] });
|
|
129
148
|
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'First Task', status: 'pending' });
|
|
130
149
|
insertTask({ id: 'T02', sliceId: 'S01', milestoneId: 'M001', title: 'Done Task', status: 'complete' });
|
|
150
|
+
insertRequirementRow('R001', 'active');
|
|
151
|
+
insertRequirementRow('R002', 'active');
|
|
152
|
+
insertRequirementRow('R003', 'validated');
|
|
131
153
|
|
|
132
154
|
insertArtifactRow('milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT, {
|
|
133
155
|
artifact_type: 'roadmap',
|
|
@@ -174,10 +196,12 @@ describe('derive-state-db', async () => {
|
|
|
174
196
|
}
|
|
175
197
|
});
|
|
176
198
|
|
|
177
|
-
// ─── Test 2:
|
|
178
|
-
test('derive-state-db:
|
|
199
|
+
// ─── Test 2: DB-unavailable runtime does not derive from markdown ──────
|
|
200
|
+
test('derive-state-db: DB-unavailable runtime does not derive from markdown by default', async () => {
|
|
179
201
|
const base = createFixtureBase();
|
|
202
|
+
const prev = process.env.GSD_ALLOW_MARKDOWN_DERIVE_FALLBACK;
|
|
180
203
|
try {
|
|
204
|
+
process.env.GSD_ALLOW_MARKDOWN_DERIVE_FALLBACK = '0';
|
|
181
205
|
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
182
206
|
writeFile(base, 'milestones/M001/slices/S01/S01-PLAN.md', PLAN_CONTENT);
|
|
183
207
|
writeFile(base, 'milestones/M001/slices/S01/tasks/.gitkeep', '');
|
|
@@ -188,17 +212,49 @@ describe('derive-state-db', async () => {
|
|
|
188
212
|
invalidateStateCache();
|
|
189
213
|
const state = await deriveState(base);
|
|
190
214
|
|
|
215
|
+
assert.deepStrictEqual(state.phase, 'pre-planning', 'runtime degrade: phase is pre-planning');
|
|
216
|
+
assert.deepStrictEqual(state.activeMilestone, null, 'runtime degrade: markdown milestone is not imported');
|
|
217
|
+
assert.deepStrictEqual(state.activeSlice, null, 'runtime degrade: markdown slice is not imported');
|
|
218
|
+
assert.deepStrictEqual(state.activeTask, null, 'runtime degrade: markdown task is not imported');
|
|
219
|
+
assert.ok(
|
|
220
|
+
state.blockers.some(b => b.includes('DB unavailable')),
|
|
221
|
+
'runtime degrade: blocker explains unavailable DB',
|
|
222
|
+
);
|
|
223
|
+
} finally {
|
|
224
|
+
if (prev === undefined) delete process.env.GSD_ALLOW_MARKDOWN_DERIVE_FALLBACK;
|
|
225
|
+
else process.env.GSD_ALLOW_MARKDOWN_DERIVE_FALLBACK = prev;
|
|
226
|
+
cleanup(base);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test('derive-state-db: explicit legacy markdown fallback remains opt-in', async () => {
|
|
231
|
+
const base = createFixtureBase();
|
|
232
|
+
const prev = process.env.GSD_ALLOW_MARKDOWN_DERIVE_FALLBACK;
|
|
233
|
+
try {
|
|
234
|
+
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
235
|
+
writeFile(base, 'milestones/M001/slices/S01/S01-PLAN.md', PLAN_CONTENT);
|
|
236
|
+
writeFile(base, 'milestones/M001/slices/S01/tasks/.gitkeep', '');
|
|
237
|
+
writeFile(base, 'milestones/M001/slices/S01/tasks/T01-PLAN.md', '# T01 Plan');
|
|
238
|
+
|
|
239
|
+
process.env.GSD_ALLOW_MARKDOWN_DERIVE_FALLBACK = '1';
|
|
240
|
+
|
|
241
|
+
assert.ok(!isDbAvailable(), 'fallback: DB is not available');
|
|
242
|
+
invalidateStateCache();
|
|
243
|
+
const state = await deriveState(base);
|
|
244
|
+
|
|
191
245
|
assert.deepStrictEqual(state.phase, 'executing', 'fallback: phase is executing');
|
|
192
246
|
assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'fallback: activeMilestone is M001');
|
|
193
247
|
assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'fallback: activeSlice is S01');
|
|
194
248
|
assert.deepStrictEqual(state.activeTask?.id, 'T01', 'fallback: activeTask is T01');
|
|
195
249
|
} finally {
|
|
250
|
+
if (prev === undefined) delete process.env.GSD_ALLOW_MARKDOWN_DERIVE_FALLBACK;
|
|
251
|
+
else process.env.GSD_ALLOW_MARKDOWN_DERIVE_FALLBACK = prev;
|
|
196
252
|
cleanup(base);
|
|
197
253
|
}
|
|
198
254
|
});
|
|
199
255
|
|
|
200
|
-
// ─── Test 3: Empty DB
|
|
201
|
-
test('derive-state-db: empty DB
|
|
256
|
+
// ─── Test 3: Empty DB remains authoritative ───────────────────────────
|
|
257
|
+
test('derive-state-db: empty DB does not import markdown milestones', async () => {
|
|
202
258
|
const base = createFixtureBase();
|
|
203
259
|
try {
|
|
204
260
|
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
@@ -206,21 +262,16 @@ describe('derive-state-db', async () => {
|
|
|
206
262
|
writeFile(base, 'milestones/M001/slices/S01/tasks/.gitkeep', '');
|
|
207
263
|
writeFile(base, 'milestones/M001/slices/S01/tasks/T01-PLAN.md', '# T01 Plan');
|
|
208
264
|
|
|
209
|
-
// Open DB but insert nothing — empty
|
|
210
|
-
// With #2631 fix, deriveState will sync disk milestones into DB
|
|
211
|
-
// and then take the DB path. The result should still reflect the
|
|
212
|
-
// disk milestone correctly.
|
|
265
|
+
// Open DB but insert nothing — empty DB is authoritative at runtime.
|
|
213
266
|
openDatabase(':memory:');
|
|
214
267
|
assert.ok(isDbAvailable(), 'empty-db: DB is available');
|
|
215
268
|
|
|
216
269
|
invalidateStateCache();
|
|
217
270
|
const state = await deriveState(base);
|
|
218
271
|
|
|
219
|
-
|
|
220
|
-
assert.deepStrictEqual(state.activeMilestone
|
|
221
|
-
|
|
222
|
-
// phase than the filesystem path, but the milestone must be found.
|
|
223
|
-
assert.ok(state.activeMilestone !== null, 'empty-db: activeMilestone is not null');
|
|
272
|
+
assert.deepStrictEqual(getAllMilestones().length, 0, 'empty-db: markdown milestones are not imported');
|
|
273
|
+
assert.deepStrictEqual(state.activeMilestone, null, 'empty-db: no active milestone from disk');
|
|
274
|
+
assert.deepStrictEqual(state.registry, [], 'empty-db: registry remains empty');
|
|
224
275
|
|
|
225
276
|
closeDatabase();
|
|
226
277
|
} finally {
|
|
@@ -229,8 +280,8 @@ describe('derive-state-db', async () => {
|
|
|
229
280
|
}
|
|
230
281
|
});
|
|
231
282
|
|
|
232
|
-
// ─── Test 4: Partial DB content
|
|
233
|
-
test('derive-state-db: partial DB
|
|
283
|
+
// ─── Test 4: Partial DB content does not fill gaps from disk ──────────
|
|
284
|
+
test('derive-state-db: partial DB does not fill requirements from disk', async () => {
|
|
234
285
|
const base = createFixtureBase();
|
|
235
286
|
try {
|
|
236
287
|
// Write all files to disk
|
|
@@ -254,15 +305,14 @@ describe('derive-state-db', async () => {
|
|
|
254
305
|
invalidateStateCache();
|
|
255
306
|
const state = await deriveState(base);
|
|
256
307
|
|
|
257
|
-
// Should work
|
|
308
|
+
// Should work from DB hierarchy, but requirements are DB-authoritative.
|
|
258
309
|
assert.deepStrictEqual(state.phase, 'executing', 'partial-db: phase is executing');
|
|
259
310
|
assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'partial-db: activeMilestone is M001');
|
|
260
311
|
assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'partial-db: activeSlice is S01');
|
|
261
312
|
assert.deepStrictEqual(state.activeTask?.id, 'T01', 'partial-db: activeTask is T01');
|
|
262
|
-
|
|
263
|
-
assert.deepStrictEqual(state.requirements?.
|
|
264
|
-
assert.deepStrictEqual(state.requirements?.
|
|
265
|
-
assert.deepStrictEqual(state.requirements?.total, 3, 'partial-db: requirements.total from disk');
|
|
313
|
+
assert.deepStrictEqual(state.requirements?.active, 0, 'partial-db: requirements.active not imported from disk');
|
|
314
|
+
assert.deepStrictEqual(state.requirements?.validated, 0, 'partial-db: requirements.validated not imported from disk');
|
|
315
|
+
assert.deepStrictEqual(state.requirements?.total, 0, 'partial-db: requirements.total not imported from disk');
|
|
266
316
|
|
|
267
317
|
closeDatabase();
|
|
268
318
|
} finally {
|
|
@@ -271,7 +321,7 @@ describe('derive-state-db', async () => {
|
|
|
271
321
|
}
|
|
272
322
|
});
|
|
273
323
|
|
|
274
|
-
test('derive-state-db: partial task rows
|
|
324
|
+
test('derive-state-db: partial task rows do not import missing plan tasks', async (t) => {
|
|
275
325
|
const base = createFixtureBase();
|
|
276
326
|
t.after(() => {
|
|
277
327
|
closeDatabase();
|
|
@@ -306,15 +356,39 @@ describe('derive-state-db', async () => {
|
|
|
306
356
|
const state = await deriveState(base);
|
|
307
357
|
|
|
308
358
|
const dbTasks = getSliceTasks('M001', 'S01');
|
|
309
|
-
assert.deepStrictEqual(dbTasks.length,
|
|
359
|
+
assert.deepStrictEqual(dbTasks.length, 1, 'partial-task-db: missing T02 is not imported from plan');
|
|
310
360
|
assert.deepStrictEqual(dbTasks.find(t => t.id === 'T01')?.status, 'complete', 'partial-task-db: existing complete T01 preserved');
|
|
311
|
-
assert.deepStrictEqual(dbTasks.find(t => t.id === 'T02')
|
|
312
|
-
assert.deepStrictEqual(state.phase, '
|
|
313
|
-
assert.deepStrictEqual(state.activeTask
|
|
314
|
-
assert.deepStrictEqual(state.progress?.tasks, { done: 1, total:
|
|
361
|
+
assert.deepStrictEqual(dbTasks.find(t => t.id === 'T02'), undefined, 'partial-task-db: missing T02 absent from DB');
|
|
362
|
+
assert.deepStrictEqual(state.phase, 'summarizing', 'partial-task-db: phase follows DB tasks only');
|
|
363
|
+
assert.deepStrictEqual(state.activeTask, null, 'partial-task-db: no active task from disk-only plan row');
|
|
364
|
+
assert.deepStrictEqual(state.progress?.tasks, { done: 1, total: 1 }, 'partial-task-db: task progress is DB-only');
|
|
315
365
|
});
|
|
316
366
|
|
|
317
|
-
test('derive-state-db:
|
|
367
|
+
test('derive-state-db: disk SUMMARY does not complete a pending DB task', async (t) => {
|
|
368
|
+
const base = createFixtureBase();
|
|
369
|
+
t.after(() => {
|
|
370
|
+
closeDatabase();
|
|
371
|
+
cleanup(base);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
375
|
+
writeFile(base, 'milestones/M001/slices/S01/S01-PLAN.md', PLAN_CONTENT);
|
|
376
|
+
writeFile(base, 'milestones/M001/slices/S01/tasks/T01-SUMMARY.md', '# T01 Summary\n\nManual disk edit.');
|
|
377
|
+
|
|
378
|
+
openDatabase(':memory:');
|
|
379
|
+
insertMilestone({ id: 'M001', title: 'Test Milestone', status: 'active' });
|
|
380
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'First Slice', status: 'active', risk: 'low', depends: [] });
|
|
381
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'First Task', status: 'pending' });
|
|
382
|
+
|
|
383
|
+
invalidateStateCache();
|
|
384
|
+
const state = await deriveStateFromDb(base);
|
|
385
|
+
|
|
386
|
+
assert.deepStrictEqual(getSliceTasks('M001', 'S01').find(t => t.id === 'T01')?.status, 'pending');
|
|
387
|
+
assert.deepStrictEqual(state.phase, 'executing');
|
|
388
|
+
assert.deepStrictEqual(state.activeTask?.id, 'T01');
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
test('derive-state-db: empty DB does not import milestone completion and dependencies', async (t) => {
|
|
318
392
|
const base = createFixtureBase();
|
|
319
393
|
t.after(() => {
|
|
320
394
|
closeDatabase();
|
|
@@ -350,16 +424,13 @@ describe('derive-state-db', async () => {
|
|
|
350
424
|
const state = await deriveState(base);
|
|
351
425
|
|
|
352
426
|
const milestones = getAllMilestones();
|
|
353
|
-
assert.deepStrictEqual(milestones.
|
|
354
|
-
assert.deepStrictEqual(
|
|
355
|
-
assert.deepStrictEqual(
|
|
356
|
-
assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'disk-import: M002 is active because M001 completion satisfies dependency');
|
|
357
|
-
assert.deepStrictEqual(state.registry.find(e => e.id === 'M001')?.status, 'complete', 'disk-import: M001 registry status is complete');
|
|
358
|
-
assert.deepStrictEqual(state.registry.find(e => e.id === 'M003')?.status, 'pending', 'disk-import: M003 stays pending on unmet M002 dependency');
|
|
427
|
+
assert.deepStrictEqual(milestones.length, 0, 'disk-import: markdown milestones are not imported');
|
|
428
|
+
assert.deepStrictEqual(state.activeMilestone, null, 'disk-import: no active milestone from markdown');
|
|
429
|
+
assert.deepStrictEqual(state.registry, [], 'disk-import: registry remains DB-only');
|
|
359
430
|
});
|
|
360
431
|
|
|
361
|
-
// ─── Test 5:
|
|
362
|
-
test('derive-state-db: requirements from disk content', async () => {
|
|
432
|
+
// ─── Test 5: Legacy requirements counting from disk ────────────────────
|
|
433
|
+
test('derive-state-db: explicit legacy derivation counts requirements from disk content', async () => {
|
|
363
434
|
const base = createFixtureBase();
|
|
364
435
|
try {
|
|
365
436
|
// Write minimal milestone dir (needed for milestone discovery)
|
|
@@ -368,9 +439,9 @@ describe('derive-state-db', async () => {
|
|
|
368
439
|
writeFile(base, 'REQUIREMENTS.md', REQUIREMENTS_CONTENT);
|
|
369
440
|
|
|
370
441
|
invalidateStateCache();
|
|
371
|
-
const state = await
|
|
442
|
+
const state = await _deriveStateImpl(base);
|
|
372
443
|
|
|
373
|
-
//
|
|
444
|
+
// Explicit legacy derivation still reads requirements from disk.
|
|
374
445
|
assert.deepStrictEqual(state.requirements?.active, 2, 'req-from-disk: requirements.active = 2');
|
|
375
446
|
assert.deepStrictEqual(state.requirements?.validated, 1, 'req-from-disk: requirements.validated = 1');
|
|
376
447
|
assert.deepStrictEqual(state.requirements?.total, 3, 'req-from-disk: requirements.total = 3');
|
|
@@ -805,6 +876,13 @@ describe('derive-state-db', async () => {
|
|
|
805
876
|
openDatabase(':memory:');
|
|
806
877
|
insertMilestone({ id: 'M001', title: 'Stuck Remediation', status: 'active' });
|
|
807
878
|
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Done Slice', status: 'complete', risk: 'low', depends: [] });
|
|
879
|
+
insertAssessment({
|
|
880
|
+
path: 'milestones/M001/M001-VALIDATION.md',
|
|
881
|
+
milestoneId: 'M001',
|
|
882
|
+
status: 'needs-remediation',
|
|
883
|
+
scope: 'milestone-validation',
|
|
884
|
+
fullContent: 'verdict: needs-remediation',
|
|
885
|
+
});
|
|
808
886
|
|
|
809
887
|
invalidateStateCache();
|
|
810
888
|
const dbState = await deriveStateFromDb(base);
|
|
@@ -846,6 +924,13 @@ describe('derive-state-db', async () => {
|
|
|
846
924
|
openDatabase(':memory:');
|
|
847
925
|
insertMilestone({ id: 'M001', title: 'Complete Test', status: 'active' });
|
|
848
926
|
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Done Slice', status: 'complete', risk: 'low', depends: [] });
|
|
927
|
+
insertAssessment({
|
|
928
|
+
path: 'milestones/M001/M001-VALIDATION.md',
|
|
929
|
+
milestoneId: 'M001',
|
|
930
|
+
status: 'pass',
|
|
931
|
+
scope: 'milestone-validation',
|
|
932
|
+
fullContent: 'verdict: pass',
|
|
933
|
+
});
|
|
849
934
|
|
|
850
935
|
invalidateStateCache();
|
|
851
936
|
const dbState = await deriveStateFromDb(base);
|
|
@@ -1098,8 +1183,8 @@ describe('derive-state-db', async () => {
|
|
|
1098
1183
|
}
|
|
1099
1184
|
});
|
|
1100
1185
|
|
|
1101
|
-
// ─── Test 22: Needs-discussion — CONTEXT-DRAFT
|
|
1102
|
-
test('derive-state-db: needs-discussion via DB', async () => {
|
|
1186
|
+
// ─── Test 22: Needs-discussion — DB status, not CONTEXT-DRAFT ─────────
|
|
1187
|
+
test('derive-state-db: needs-discussion via DB status', async () => {
|
|
1103
1188
|
const base = createFixtureBase();
|
|
1104
1189
|
try {
|
|
1105
1190
|
writeFile(base, 'milestones/M001/M001-CONTEXT-DRAFT.md', '# M001: Draft\n\nDraft content.');
|
|
@@ -1108,7 +1193,7 @@ describe('derive-state-db', async () => {
|
|
|
1108
1193
|
const fileState = await _deriveStateImpl(base);
|
|
1109
1194
|
|
|
1110
1195
|
openDatabase(':memory:');
|
|
1111
|
-
insertMilestone({ id: 'M001', title: 'Draft', status: '
|
|
1196
|
+
insertMilestone({ id: 'M001', title: 'Draft', status: 'needs-discussion' });
|
|
1112
1197
|
|
|
1113
1198
|
invalidateStateCache();
|
|
1114
1199
|
const dbState = await deriveStateFromDb(base);
|
|
@@ -1127,7 +1212,7 @@ describe('derive-state-db', async () => {
|
|
|
1127
1212
|
test('derive-state-db: disk-only milestone auto-synced into DB (#2416)', async () => {
|
|
1128
1213
|
const base = createFixtureBase();
|
|
1129
1214
|
try {
|
|
1130
|
-
// M001 is complete and exists in DB. M002
|
|
1215
|
+
// M001 is complete and exists in DB. M002 is disk-only — no DB row.
|
|
1131
1216
|
writeFile(base, 'milestones/M001/M001-SUMMARY.md', '# M001 Summary\n\nDone.');
|
|
1132
1217
|
writeFile(base, 'milestones/M002/M002-CONTEXT.md', '# M002: Queued\n\nQueued milestone.');
|
|
1133
1218
|
|
|
@@ -1138,16 +1223,12 @@ describe('derive-state-db', async () => {
|
|
|
1138
1223
|
invalidateStateCache();
|
|
1139
1224
|
const state = await deriveStateFromDb(base);
|
|
1140
1225
|
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
// After the fix, deriveStateFromDb reconciles disk dirs and inserts M002.
|
|
1144
|
-
assert.deepStrictEqual(state.phase, 'pre-planning', 'disk-sync-2416: phase is pre-planning, not complete');
|
|
1145
|
-
assert.deepStrictEqual(state.registry.length, 2, 'disk-sync-2416: both milestones visible in registry');
|
|
1226
|
+
assert.deepStrictEqual(state.phase, 'complete', 'disk-sync-2416: disk-only milestone is not imported');
|
|
1227
|
+
assert.deepStrictEqual(state.registry.length, 1, 'disk-sync-2416: only DB milestones visible in registry');
|
|
1146
1228
|
assert.deepStrictEqual(state.registry[0]?.id, 'M001', 'disk-sync-2416: registry[0] is M001');
|
|
1147
1229
|
assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'disk-sync-2416: M001 is complete');
|
|
1148
|
-
assert.deepStrictEqual(state.registry[1]
|
|
1149
|
-
assert.deepStrictEqual(state.
|
|
1150
|
-
assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'disk-sync-2416: activeMilestone is M002');
|
|
1230
|
+
assert.deepStrictEqual(state.registry[1], undefined, 'disk-sync-2416: M002 remains absent without explicit import');
|
|
1231
|
+
assert.deepStrictEqual(state.activeMilestone, null, 'disk-sync-2416: no active milestone from disk-only row');
|
|
1151
1232
|
|
|
1152
1233
|
closeDatabase();
|
|
1153
1234
|
} finally {
|
|
@@ -1208,12 +1289,11 @@ describe('derive-state-db', async () => {
|
|
|
1208
1289
|
invalidateStateCache();
|
|
1209
1290
|
const dbState = await deriveStateFromDb(base);
|
|
1210
1291
|
|
|
1211
|
-
// M002
|
|
1292
|
+
// M002 is legitimate legacy disk state but is not authoritative without a DB row.
|
|
1212
1293
|
const m002Entry = dbState.registry.find(e => e.id === 'M002');
|
|
1213
|
-
assert.
|
|
1214
|
-
assert.deepStrictEqual(dbState.activeMilestone
|
|
1215
|
-
|
|
1216
|
-
assert.notEqual(dbState.phase, 'complete', 'ghost-wt: phase should not be complete');
|
|
1294
|
+
assert.equal(m002Entry, undefined, 'ghost-wt: M002 should not be imported into registry');
|
|
1295
|
+
assert.deepStrictEqual(dbState.activeMilestone, null, 'ghost-wt: no active milestone from disk-only worktree');
|
|
1296
|
+
assert.equal(dbState.phase, 'complete', 'ghost-wt: DB-only M001 completion drives state');
|
|
1217
1297
|
|
|
1218
1298
|
closeDatabase();
|
|
1219
1299
|
} finally {
|