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
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
// GSD Extension — Tests for
|
|
1
|
+
// GSD Extension — Tests for DB-authoritative deriveStateFromDb behavior
|
|
2
2
|
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// detectBlockers, checkReplanTrigger, checkInterruptedWork
|
|
8
|
-
//
|
|
9
|
-
// Helpers are private — exercised through deriveStateFromDb integration.
|
|
4
|
+
// Private helper behavior is exercised through deriveStateFromDb integration.
|
|
5
|
+
// Markdown files in these tests are projections unless the DB row explicitly
|
|
6
|
+
// makes them authoritative.
|
|
10
7
|
|
|
11
8
|
import { describe, test, beforeEach, afterEach } from 'node:test';
|
|
12
9
|
import assert from 'node:assert/strict';
|
|
@@ -14,13 +11,16 @@ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
|
14
11
|
import { join } from 'node:path';
|
|
15
12
|
import { tmpdir } from 'node:os';
|
|
16
13
|
|
|
17
|
-
import { invalidateStateCache, deriveStateFromDb } from '../state.ts';
|
|
14
|
+
import { invalidateStateCache, deriveStateFromDb, getActiveMilestoneId } from '../state.ts';
|
|
18
15
|
import {
|
|
19
16
|
openDatabase,
|
|
20
17
|
closeDatabase,
|
|
18
|
+
insertAssessment,
|
|
21
19
|
insertMilestone,
|
|
20
|
+
insertRequirement,
|
|
22
21
|
insertSlice,
|
|
23
22
|
insertTask,
|
|
23
|
+
setMilestoneQueueOrder,
|
|
24
24
|
updateTaskStatus,
|
|
25
25
|
} from '../gsd-db.ts';
|
|
26
26
|
|
|
@@ -112,6 +112,20 @@ describe('derive-state-helpers', () => {
|
|
|
112
112
|
|
|
113
113
|
openDatabase(':memory:');
|
|
114
114
|
insertMilestone({ id: 'M001', title: 'First', status: 'complete' });
|
|
115
|
+
insertRequirement({
|
|
116
|
+
id: 'R001',
|
|
117
|
+
class: 'functional',
|
|
118
|
+
status: 'active',
|
|
119
|
+
description: 'Unmapped',
|
|
120
|
+
why: 'test',
|
|
121
|
+
source: 'test',
|
|
122
|
+
primary_owner: '',
|
|
123
|
+
supporting_slices: '',
|
|
124
|
+
validation: '',
|
|
125
|
+
notes: '',
|
|
126
|
+
full_content: '',
|
|
127
|
+
superseded_by: null,
|
|
128
|
+
});
|
|
115
129
|
|
|
116
130
|
invalidateStateCache();
|
|
117
131
|
const state = await deriveStateFromDb(base);
|
|
@@ -126,10 +140,11 @@ describe('derive-state-helpers', () => {
|
|
|
126
140
|
});
|
|
127
141
|
|
|
128
142
|
// ─── resolveSliceDependencies: GSD_SLICE_LOCK with missing slice ────
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
143
|
+
test('resolveSliceDependencies: GSD_SLICE_LOCK pointing to non-existent slice returns blocked', async () => {
|
|
144
|
+
const base = createFixtureBase();
|
|
145
|
+
const origLock = process.env.GSD_SLICE_LOCK;
|
|
146
|
+
const origWorker = process.env.GSD_PARALLEL_WORKER;
|
|
147
|
+
try {
|
|
133
148
|
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
134
149
|
writeFile(base, 'milestones/M001/slices/S01/S01-PLAN.md', PLAN_CONTENT);
|
|
135
150
|
writeFile(base, 'milestones/M001/slices/S01/tasks/.gitkeep', '');
|
|
@@ -140,26 +155,30 @@ describe('derive-state-helpers', () => {
|
|
|
140
155
|
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'First', status: 'active', risk: 'low', depends: [] });
|
|
141
156
|
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'First Task', status: 'pending' });
|
|
142
157
|
|
|
143
|
-
|
|
158
|
+
process.env.GSD_SLICE_LOCK = 'S99';
|
|
159
|
+
process.env.GSD_PARALLEL_WORKER = '1';
|
|
144
160
|
|
|
145
161
|
invalidateStateCache();
|
|
146
162
|
const state = await deriveStateFromDb(base);
|
|
147
163
|
|
|
148
164
|
assert.equal(state.phase, 'blocked', 'slice-lock-miss: phase is blocked');
|
|
149
165
|
assert.ok(state.blockers.some(b => b.includes('GSD_SLICE_LOCK=S99')), 'slice-lock-miss: blocker mentions lock');
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
166
|
+
} finally {
|
|
167
|
+
if (origLock !== undefined) process.env.GSD_SLICE_LOCK = origLock;
|
|
168
|
+
else delete process.env.GSD_SLICE_LOCK;
|
|
169
|
+
if (origWorker !== undefined) process.env.GSD_PARALLEL_WORKER = origWorker;
|
|
170
|
+
else delete process.env.GSD_PARALLEL_WORKER;
|
|
171
|
+
closeDatabase();
|
|
154
172
|
cleanup(base);
|
|
155
173
|
}
|
|
156
174
|
});
|
|
157
175
|
|
|
158
176
|
// ─── resolveSliceDependencies: GSD_SLICE_LOCK with valid slice ──────
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
177
|
+
test('resolveSliceDependencies: GSD_SLICE_LOCK targeting valid slice bypasses deps', async () => {
|
|
178
|
+
const base = createFixtureBase();
|
|
179
|
+
const origLock = process.env.GSD_SLICE_LOCK;
|
|
180
|
+
const origWorker = process.env.GSD_PARALLEL_WORKER;
|
|
181
|
+
try {
|
|
163
182
|
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
164
183
|
// S02 depends on S01 but we lock to S02 directly
|
|
165
184
|
writeFile(base, 'milestones/M001/slices/S02/S02-PLAN.md', `# S02\n\n**Goal:** Test.\n**Demo:** Pass.\n\n## Tasks\n\n- [ ] **T01: Task** \`est:5m\`\n Do thing.\n`);
|
|
@@ -172,23 +191,26 @@ describe('derive-state-helpers', () => {
|
|
|
172
191
|
insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Second', status: 'pending', risk: 'low', depends: ['S01'] });
|
|
173
192
|
insertTask({ id: 'T01', sliceId: 'S02', milestoneId: 'M001', title: 'Task', status: 'pending' });
|
|
174
193
|
|
|
175
|
-
|
|
194
|
+
process.env.GSD_SLICE_LOCK = 'S02';
|
|
195
|
+
process.env.GSD_PARALLEL_WORKER = '1';
|
|
176
196
|
|
|
177
197
|
invalidateStateCache();
|
|
178
198
|
const state = await deriveStateFromDb(base);
|
|
179
199
|
|
|
180
200
|
assert.equal(state.activeSlice?.id, 'S02', 'slice-lock-valid: activeSlice is S02 (locked)');
|
|
181
201
|
assert.equal(state.phase, 'executing', 'slice-lock-valid: phase is executing');
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
202
|
+
} finally {
|
|
203
|
+
if (origLock !== undefined) process.env.GSD_SLICE_LOCK = origLock;
|
|
204
|
+
else delete process.env.GSD_SLICE_LOCK;
|
|
205
|
+
if (origWorker !== undefined) process.env.GSD_PARALLEL_WORKER = origWorker;
|
|
206
|
+
else delete process.env.GSD_PARALLEL_WORKER;
|
|
207
|
+
closeDatabase();
|
|
186
208
|
cleanup(base);
|
|
187
209
|
}
|
|
188
210
|
});
|
|
189
211
|
|
|
190
|
-
// ───
|
|
191
|
-
test('
|
|
212
|
+
// ─── DB-authoritative tasks: plan projection does not import tasks ──────
|
|
213
|
+
test('deriveStateFromDb: DB-empty task list does not import PLAN tasks', async () => {
|
|
192
214
|
const base = createFixtureBase();
|
|
193
215
|
try {
|
|
194
216
|
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
@@ -200,24 +222,23 @@ describe('derive-state-helpers', () => {
|
|
|
200
222
|
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
201
223
|
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'First', status: 'active', risk: 'low', depends: [] });
|
|
202
224
|
insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Second', status: 'pending', risk: 'low', depends: ['S01'] });
|
|
203
|
-
// No tasks inserted —
|
|
225
|
+
// No tasks inserted — PLAN.md is a projection and must not be imported.
|
|
204
226
|
|
|
205
227
|
invalidateStateCache();
|
|
206
228
|
const state = await deriveStateFromDb(base);
|
|
207
229
|
|
|
208
|
-
|
|
209
|
-
assert.equal(state.
|
|
210
|
-
assert.equal(state.
|
|
211
|
-
assert.equal(state.progress?.tasks?.
|
|
212
|
-
assert.equal(state.progress?.tasks?.done, 1, 'task-reconcile: done tasks = 1 (T02 was [x])');
|
|
230
|
+
assert.equal(state.phase, 'planning', 'db-empty-tasks: phase is planning');
|
|
231
|
+
assert.equal(state.activeTask, null, 'db-empty-tasks: no active task');
|
|
232
|
+
assert.equal(state.progress?.tasks?.total, 0, 'db-empty-tasks: no tasks imported');
|
|
233
|
+
assert.equal(state.progress?.tasks?.done, 0, 'db-empty-tasks: no completed tasks imported');
|
|
213
234
|
} finally {
|
|
214
235
|
closeDatabase();
|
|
215
236
|
cleanup(base);
|
|
216
237
|
}
|
|
217
238
|
});
|
|
218
239
|
|
|
219
|
-
// ───
|
|
220
|
-
test('
|
|
240
|
+
// ─── DB-authoritative tasks: SUMMARY projection does not complete task ────
|
|
241
|
+
test('deriveStateFromDb: disk SUMMARY does not reconcile pending task', async () => {
|
|
221
242
|
const base = createFixtureBase();
|
|
222
243
|
try {
|
|
223
244
|
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
@@ -237,11 +258,9 @@ describe('derive-state-helpers', () => {
|
|
|
237
258
|
invalidateStateCache();
|
|
238
259
|
const state = await deriveStateFromDb(base);
|
|
239
260
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
assert.equal(state.
|
|
243
|
-
assert.equal(state.activeTask, null, 'stale-task: no active task (all done)');
|
|
244
|
-
assert.equal(state.progress?.tasks?.done, 2, 'stale-task: tasks.done = 2');
|
|
261
|
+
assert.equal(state.phase, 'executing', 'disk-summary-ignored: phase is executing');
|
|
262
|
+
assert.equal(state.activeTask?.id, 'T01', 'disk-summary-ignored: T01 remains active');
|
|
263
|
+
assert.equal(state.progress?.tasks?.done, 1, 'disk-summary-ignored: only DB-complete task is done');
|
|
245
264
|
} finally {
|
|
246
265
|
closeDatabase();
|
|
247
266
|
cleanup(base);
|
|
@@ -256,7 +275,8 @@ describe('derive-state-helpers', () => {
|
|
|
256
275
|
writeFile(base, 'milestones/M001/slices/S01/S01-PLAN.md', PLAN_CONTENT);
|
|
257
276
|
writeFile(base, 'milestones/M001/slices/S01/tasks/.gitkeep', '');
|
|
258
277
|
writeFile(base, 'milestones/M001/slices/S01/tasks/T01-PLAN.md', '# T01 Plan');
|
|
259
|
-
// T02 completed with blocker discovered
|
|
278
|
+
// T02 completed with blocker discovered. The disk summary is a projection;
|
|
279
|
+
// only the DB blocker flag is authoritative for deriveStateFromDb().
|
|
260
280
|
writeFile(base, 'milestones/M001/slices/S01/tasks/T02-SUMMARY.md',
|
|
261
281
|
'---\nblocker_discovered: true\n---\n\n# T02 Summary\n\nFound a blocker.');
|
|
262
282
|
|
|
@@ -265,7 +285,7 @@ describe('derive-state-helpers', () => {
|
|
|
265
285
|
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'First', status: 'active', risk: 'low', depends: [] });
|
|
266
286
|
insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Second', status: 'pending', risk: 'low', depends: ['S01'] });
|
|
267
287
|
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'First Task', status: 'pending' });
|
|
268
|
-
insertTask({ id: 'T02', sliceId: 'S01', milestoneId: 'M001', title: 'Done Task', status: 'complete' });
|
|
288
|
+
insertTask({ id: 'T02', sliceId: 'S01', milestoneId: 'M001', title: 'Done Task', status: 'complete', blockerDiscovered: true });
|
|
269
289
|
|
|
270
290
|
invalidateStateCache();
|
|
271
291
|
const state = await deriveStateFromDb(base);
|
|
@@ -278,8 +298,8 @@ describe('derive-state-helpers', () => {
|
|
|
278
298
|
}
|
|
279
299
|
});
|
|
280
300
|
|
|
281
|
-
// ───
|
|
282
|
-
test('
|
|
301
|
+
// ─── CONTINUE.md projection is ignored by DB derive ─────────────────
|
|
302
|
+
test('deriveStateFromDb: continue.md projection does not trigger resume nextAction', async () => {
|
|
283
303
|
const base = createFixtureBase();
|
|
284
304
|
try {
|
|
285
305
|
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
@@ -299,8 +319,8 @@ describe('derive-state-helpers', () => {
|
|
|
299
319
|
const state = await deriveStateFromDb(base);
|
|
300
320
|
|
|
301
321
|
assert.equal(state.phase, 'executing', 'continue: phase is still executing');
|
|
302
|
-
assert.ok(state.nextAction.includes('Resume interrupted work'), 'continue: nextAction
|
|
303
|
-
assert.ok(state.nextAction.includes('continue.md'), 'continue: nextAction
|
|
322
|
+
assert.ok(!state.nextAction.includes('Resume interrupted work'), 'continue: nextAction does not mention resume');
|
|
323
|
+
assert.ok(!state.nextAction.includes('continue.md'), 'continue: nextAction does not mention continue.md');
|
|
304
324
|
} finally {
|
|
305
325
|
closeDatabase();
|
|
306
326
|
cleanup(base);
|
|
@@ -380,6 +400,13 @@ describe('derive-state-helpers', () => {
|
|
|
380
400
|
insertMilestone({ id: 'M001', title: 'First', status: 'active' });
|
|
381
401
|
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'First', status: 'complete', risk: 'low', depends: [] });
|
|
382
402
|
insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Second', status: 'complete', risk: 'low', depends: ['S01'] });
|
|
403
|
+
insertAssessment({
|
|
404
|
+
path: 'milestones/M001/M001-VALIDATION.md',
|
|
405
|
+
milestoneId: 'M001',
|
|
406
|
+
status: 'pass',
|
|
407
|
+
scope: 'milestone-validation',
|
|
408
|
+
fullContent: 'verdict: passed',
|
|
409
|
+
});
|
|
383
410
|
|
|
384
411
|
invalidateStateCache();
|
|
385
412
|
const state = await deriveStateFromDb(base);
|
|
@@ -394,34 +421,34 @@ describe('derive-state-helpers', () => {
|
|
|
394
421
|
}
|
|
395
422
|
});
|
|
396
423
|
|
|
397
|
-
// ───
|
|
398
|
-
test('
|
|
424
|
+
// ─── DB-authoritative slices: roadmap projection does not insert slices ───
|
|
425
|
+
test('deriveStateFromDb: ROADMAP slices missing from DB are not auto-inserted', async () => {
|
|
399
426
|
const base = createFixtureBase();
|
|
400
427
|
try {
|
|
401
428
|
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
402
429
|
|
|
403
430
|
openDatabase(':memory:');
|
|
404
431
|
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
405
|
-
// No slices inserted —
|
|
432
|
+
// No slices inserted — ROADMAP.md is a projection and must not be imported.
|
|
406
433
|
|
|
407
434
|
invalidateStateCache();
|
|
408
435
|
const state = await deriveStateFromDb(base);
|
|
409
436
|
|
|
410
|
-
|
|
411
|
-
assert.equal(state.
|
|
412
|
-
assert.equal(state.
|
|
413
|
-
assert.
|
|
437
|
+
assert.equal(state.activeMilestone?.id, 'M001', 'roadmap-projection: M001 is active');
|
|
438
|
+
assert.equal(state.activeSlice, null, 'roadmap-projection: no active slice imported');
|
|
439
|
+
assert.equal(state.phase, 'pre-planning', 'roadmap-projection: no DB slices routes to pre-planning');
|
|
440
|
+
assert.equal(state.progress?.slices, undefined, 'roadmap-projection: no slice progress from projection');
|
|
414
441
|
} finally {
|
|
415
442
|
closeDatabase();
|
|
416
443
|
cleanup(base);
|
|
417
444
|
}
|
|
418
445
|
});
|
|
419
446
|
|
|
420
|
-
// ─── Queue order:
|
|
421
|
-
test('deriveStateFromDb
|
|
447
|
+
// ─── Queue order: DB sequence is authoritative ─────────────────────
|
|
448
|
+
test('deriveStateFromDb ignores QUEUE-ORDER.json and uses DB sequence', async () => {
|
|
422
449
|
const base = createFixtureBase();
|
|
423
450
|
try {
|
|
424
|
-
//
|
|
451
|
+
// QUEUE-ORDER.json is a projection and should not drive DB derivation.
|
|
425
452
|
const queueOrder = JSON.stringify({ order: ['M003', 'M001', 'M002'], updatedAt: new Date().toISOString() });
|
|
426
453
|
writeFileSync(join(base, '.gsd', 'QUEUE-ORDER.json'), queueOrder);
|
|
427
454
|
writeFile(base, 'milestones/M001/M001-CONTEXT.md', '# M001\n\nContext.');
|
|
@@ -429,23 +456,47 @@ describe('derive-state-helpers', () => {
|
|
|
429
456
|
writeFile(base, 'milestones/M003/M003-CONTEXT.md', '# M003\n\nContext.');
|
|
430
457
|
|
|
431
458
|
openDatabase(':memory:');
|
|
432
|
-
// Insert in natural order
|
|
459
|
+
// Insert in natural order, then store the authoritative DB sequence.
|
|
433
460
|
insertMilestone({ id: 'M001', title: 'First', status: 'active' });
|
|
434
461
|
insertMilestone({ id: 'M002', title: 'Second', status: 'active' });
|
|
435
462
|
insertMilestone({ id: 'M003', title: 'Third', status: 'active' });
|
|
463
|
+
setMilestoneQueueOrder(['M002', 'M001', 'M003']);
|
|
436
464
|
|
|
437
465
|
invalidateStateCache();
|
|
438
466
|
const state = await deriveStateFromDb(base);
|
|
439
467
|
|
|
440
|
-
|
|
441
|
-
assert.equal(state.
|
|
442
|
-
assert.equal(state.registry[0]?.id, 'M003', 'queue-order: registry[0] is M003');
|
|
468
|
+
assert.equal(state.activeMilestone?.id, 'M002', 'queue-order: DB sequence chooses M002');
|
|
469
|
+
assert.equal(state.registry[0]?.id, 'M002', 'queue-order: registry[0] follows DB sequence');
|
|
443
470
|
} finally {
|
|
444
471
|
closeDatabase();
|
|
445
472
|
cleanup(base);
|
|
446
473
|
}
|
|
447
474
|
});
|
|
448
475
|
|
|
476
|
+
test('getActiveMilestoneId: DB lock path ignores PARKED flag projection', async () => {
|
|
477
|
+
const base = createFixtureBase();
|
|
478
|
+
const previousLock = process.env.GSD_MILESTONE_LOCK;
|
|
479
|
+
const previousWorker = process.env.GSD_PARALLEL_WORKER;
|
|
480
|
+
try {
|
|
481
|
+
process.env.GSD_MILESTONE_LOCK = 'M001';
|
|
482
|
+
process.env.GSD_PARALLEL_WORKER = '1';
|
|
483
|
+
writeFile(base, 'milestones/M001/M001-PARKED.md', '# Parked on disk');
|
|
484
|
+
|
|
485
|
+
openDatabase(':memory:');
|
|
486
|
+
insertMilestone({ id: 'M001', title: 'Active in DB', status: 'active' });
|
|
487
|
+
|
|
488
|
+
const id = await getActiveMilestoneId(base);
|
|
489
|
+
assert.equal(id, 'M001', 'DB status remains authoritative despite PARKED projection');
|
|
490
|
+
} finally {
|
|
491
|
+
if (previousLock === undefined) delete process.env.GSD_MILESTONE_LOCK;
|
|
492
|
+
else process.env.GSD_MILESTONE_LOCK = previousLock;
|
|
493
|
+
if (previousWorker === undefined) delete process.env.GSD_PARALLEL_WORKER;
|
|
494
|
+
else process.env.GSD_PARALLEL_WORKER = previousWorker;
|
|
495
|
+
closeDatabase();
|
|
496
|
+
cleanup(base);
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
|
|
449
500
|
// ─── handleAllSlicesDone: needs-remediation + all slices done → blocked (#4506) ──
|
|
450
501
|
test('handleAllSlicesDone: needs-remediation with all slices done returns blocked', async () => {
|
|
451
502
|
const base = createFixtureBase();
|
|
@@ -458,6 +509,13 @@ describe('derive-state-helpers', () => {
|
|
|
458
509
|
openDatabase(':memory:');
|
|
459
510
|
insertMilestone({ id: 'M001', title: 'Remediation Test', status: 'active' });
|
|
460
511
|
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Done', status: 'complete', risk: 'low', depends: [] });
|
|
512
|
+
insertAssessment({
|
|
513
|
+
path: 'milestones/M001/M001-VALIDATION.md',
|
|
514
|
+
milestoneId: 'M001',
|
|
515
|
+
status: 'needs-remediation',
|
|
516
|
+
scope: 'milestone-validation',
|
|
517
|
+
fullContent: 'verdict: needs-remediation',
|
|
518
|
+
});
|
|
461
519
|
|
|
462
520
|
invalidateStateCache();
|
|
463
521
|
const state = await deriveStateFromDb(base);
|
|
@@ -5,6 +5,10 @@ import { join } from 'node:path';
|
|
|
5
5
|
import { tmpdir } from 'node:os';
|
|
6
6
|
|
|
7
7
|
import { deriveState, isSliceComplete, isMilestoneComplete, isGhostMilestone } from '../state.ts';
|
|
8
|
+
|
|
9
|
+
// This suite exercises the explicit legacy markdown derivation path.
|
|
10
|
+
process.env.GSD_ALLOW_MARKDOWN_DERIVE_FALLBACK = '1';
|
|
11
|
+
|
|
8
12
|
// ─── Fixture Helpers ───────────────────────────────────────────────────────
|
|
9
13
|
|
|
10
14
|
function createFixtureBase(): string {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import {
|
|
4
|
+
annotateBackgroundable,
|
|
5
|
+
type AnnotatableDispatchAction,
|
|
6
|
+
} from "../delegation-policy.js";
|
|
7
|
+
|
|
8
|
+
function dispatchAction(unitType: string): AnnotatableDispatchAction {
|
|
9
|
+
return {
|
|
10
|
+
action: "dispatch",
|
|
11
|
+
unitType,
|
|
12
|
+
unitId: `M001/${unitType}`,
|
|
13
|
+
prompt: "(test prompt)",
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
test("annotateBackgroundable marks plan-slice as backgroundable", () => {
|
|
18
|
+
const annotated = annotateBackgroundable(dispatchAction("plan-slice"));
|
|
19
|
+
assert.equal(annotated.action, "dispatch");
|
|
20
|
+
if (annotated.action !== "dispatch") return;
|
|
21
|
+
assert.equal(annotated.backgroundable, true);
|
|
22
|
+
assert.equal(annotated.unitType, "plan-slice");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("annotateBackgroundable marks validate-milestone and reassess-roadmap as backgroundable", () => {
|
|
26
|
+
for (const unitType of ["validate-milestone", "reassess-roadmap"]) {
|
|
27
|
+
const annotated = annotateBackgroundable(dispatchAction(unitType));
|
|
28
|
+
assert.equal(annotated.action, "dispatch");
|
|
29
|
+
if (annotated.action !== "dispatch") continue;
|
|
30
|
+
assert.equal(annotated.backgroundable, true, `${unitType} should be backgroundable`);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("annotateBackgroundable marks plan-milestone and replan-slice as NOT backgroundable", () => {
|
|
35
|
+
for (const unitType of ["plan-milestone", "replan-slice"]) {
|
|
36
|
+
const annotated = annotateBackgroundable(dispatchAction(unitType));
|
|
37
|
+
assert.equal(annotated.action, "dispatch");
|
|
38
|
+
if (annotated.action !== "dispatch") continue;
|
|
39
|
+
assert.equal(annotated.backgroundable, false, `${unitType} should not be backgroundable`);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("annotateBackgroundable defaults unknown unit types to false (default-deny)", () => {
|
|
44
|
+
const annotated = annotateBackgroundable(dispatchAction("execute-task"));
|
|
45
|
+
assert.equal(annotated.action, "dispatch");
|
|
46
|
+
if (annotated.action !== "dispatch") return;
|
|
47
|
+
assert.equal(annotated.backgroundable, false);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("annotateBackgroundable leaves stop and skip actions untouched", () => {
|
|
51
|
+
const stop: AnnotatableDispatchAction = { action: "stop", reason: "test", level: "info" };
|
|
52
|
+
const skip: AnnotatableDispatchAction = { action: "skip" };
|
|
53
|
+
assert.deepEqual(annotateBackgroundable(stop), stop);
|
|
54
|
+
assert.deepEqual(annotateBackgroundable(skip), skip);
|
|
55
|
+
});
|
|
@@ -47,26 +47,12 @@ describe("completing-milestone dispatch guard (#4324)", () => {
|
|
|
47
47
|
assert.ok(dispatchIdx > -1, "complete-milestone dispatch should exist after the skip guard");
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
test("
|
|
50
|
+
test("does not reconcile DB from SUMMARY.md projection (#4658 superseded)", () => {
|
|
51
51
|
const phaseCheck = source.indexOf('phase !== "completing-milestone"');
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
assert.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
assert.ok(classifyCall > -1, "SUMMARY mismatch handling should classify summary content");
|
|
58
|
-
const dbGateBeforeClassify = source.indexOf("existingSummary && isDbAvailable()", summaryGuard);
|
|
59
|
-
assert.ok(
|
|
60
|
-
dbGateBeforeClassify === -1 || dbGateBeforeClassify > classifyCall,
|
|
61
|
-
"SUMMARY classification must not be gated on DB availability",
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
const reconcileCall = source.indexOf('updateMilestoneStatus(mid, "complete"', summaryGuard);
|
|
65
|
-
assert.ok(reconcileCall > -1, "successful SUMMARY should reconcile DB to complete");
|
|
66
|
-
|
|
67
|
-
const stopAction = source.indexOf('action: "stop"', summaryGuard);
|
|
68
|
-
assert.ok(stopAction > -1, "SUMMARY mismatch should return stop action");
|
|
69
|
-
const warningLevel = source.indexOf('level: "warning"', summaryGuard);
|
|
70
|
-
assert.ok(warningLevel > -1, "SUMMARY mismatch should be warning-level stop (pauses auto-mode)");
|
|
52
|
+
assert.ok(phaseCheck > -1, "completing-milestone phase check should exist");
|
|
53
|
+
const ruleTail = source.slice(phaseCheck, source.indexOf('name:', phaseCheck + 1));
|
|
54
|
+
assert.doesNotMatch(ruleTail, /resolveMilestoneFile\(basePath,\s*mid,\s*"SUMMARY"\)/);
|
|
55
|
+
assert.doesNotMatch(ruleTail, /classifyMilestoneSummaryContent/);
|
|
56
|
+
assert.doesNotMatch(ruleTail, /updateMilestoneStatus\(mid,\s*"complete"/);
|
|
71
57
|
});
|
|
72
58
|
});
|
|
@@ -225,14 +225,14 @@ test("dispatch guard allows slice with all declared dependencies complete", (t)
|
|
|
225
225
|
);
|
|
226
226
|
});
|
|
227
227
|
|
|
228
|
-
test("dispatch guard
|
|
228
|
+
test("dispatch guard does not skip prior milestone from SUMMARY projection when DB is not closed", (t) => {
|
|
229
229
|
const repo = setupRepo();
|
|
230
230
|
t.after(() => teardownRepo(repo));
|
|
231
231
|
|
|
232
232
|
mkdirSync(join(repo, ".gsd", "milestones", "M001"), { recursive: true });
|
|
233
233
|
mkdirSync(join(repo, ".gsd", "milestones", "M002"), { recursive: true });
|
|
234
234
|
|
|
235
|
-
// M001
|
|
235
|
+
// M001 has a successful SUMMARY projection but is not closed in the DB.
|
|
236
236
|
insertMilestone({ id: "M001", title: "Previous" });
|
|
237
237
|
insertSlice({ id: "S01", milestoneId: "M001", title: "Core", status: "complete", depends: [], sequence: 1 });
|
|
238
238
|
insertSlice({ id: "S02", milestoneId: "M001", title: "Tests", status: "complete", depends: ["S01"], sequence: 2 });
|
|
@@ -242,16 +242,15 @@ test("dispatch guard skips completed milestone with SUMMARY even if it has unche
|
|
|
242
242
|
insertMilestone({ id: "M002", title: "Current" });
|
|
243
243
|
insertSlice({ id: "S01", milestoneId: "M002", title: "Start", status: "pending", depends: [], sequence: 1 });
|
|
244
244
|
|
|
245
|
-
// M001 SUMMARY on disk
|
|
245
|
+
// M001 SUMMARY on disk must not trigger skip while DB remains open/active.
|
|
246
246
|
writeFileSync(join(repo, ".gsd", "milestones", "M001", "M001-ROADMAP.md"), "# M001\n");
|
|
247
247
|
writeFileSync(join(repo, ".gsd", "milestones", "M001", "M001-SUMMARY.md"),
|
|
248
248
|
"---\nstatus: complete\n---\n# M001 Summary\nDone.\n");
|
|
249
249
|
writeFileSync(join(repo, ".gsd", "milestones", "M002", "M002-ROADMAP.md"), "# M002\n");
|
|
250
250
|
|
|
251
|
-
// M001 has SUMMARY — should be skipped, not block M002/S01
|
|
252
251
|
assert.equal(
|
|
253
252
|
getPriorSliceCompletionBlocker(repo, "main", "plan-slice", "M002/S01"),
|
|
254
|
-
|
|
253
|
+
"Cannot dispatch plan-slice M002/S01: earlier slice M001/S03-R is not complete.",
|
|
255
254
|
);
|
|
256
255
|
});
|
|
257
256
|
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* dispatcher-stuck-planning.test.ts
|
|
2
|
+
* dispatcher-stuck-planning.test.ts
|
|
3
3
|
*
|
|
4
|
-
* Verify that state.ts
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* tool, leaving the DB with zero or partial task rows.
|
|
4
|
+
* Verify that state.ts no longer imports disk PLAN.md tasks into the runtime
|
|
5
|
+
* DB. PLAN.md is a projection; task rows must be created through DB-backed
|
|
6
|
+
* planning/import APIs.
|
|
8
7
|
*/
|
|
9
8
|
|
|
10
9
|
import { describe, test } from "node:test";
|
|
@@ -16,23 +15,23 @@ 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("dispatcher
|
|
18
|
+
describe("dispatcher DB-authoritative planning boundary", () => {
|
|
20
19
|
const source = readFileSync(sourceFile, "utf-8");
|
|
21
20
|
|
|
22
|
-
test("
|
|
23
|
-
assert.
|
|
21
|
+
test("does not import insertTask into state derivation", () => {
|
|
22
|
+
assert.doesNotMatch(source, /import\s*\{[^}]*insertTask[^}]*\}\s*from/);
|
|
24
23
|
});
|
|
25
24
|
|
|
26
|
-
test("
|
|
27
|
-
assert.
|
|
28
|
-
assert.match(source, /
|
|
25
|
+
test("does not contain plan-file task reconciliation block", () => {
|
|
26
|
+
assert.doesNotMatch(source, /dbTaskIds\.has\(t\.id\)/);
|
|
27
|
+
assert.match(source, /Slice \$\{activeSlice\.id\} has no DB tasks/);
|
|
29
28
|
});
|
|
30
29
|
|
|
31
|
-
test("
|
|
32
|
-
assert.
|
|
30
|
+
test("does not call insertTask from state derivation", () => {
|
|
31
|
+
assert.doesNotMatch(source, /insertTask\(\{/);
|
|
33
32
|
});
|
|
34
33
|
|
|
35
|
-
test("
|
|
36
|
-
assert.match(source,
|
|
34
|
+
test("documents markdown projections as non-authoritative", () => {
|
|
35
|
+
assert.match(source, /Markdown files are projections only/);
|
|
37
36
|
});
|
|
38
37
|
});
|
|
@@ -133,29 +133,9 @@ assert(
|
|
|
133
133
|
"stale CONTEXT-DRAFT.md should be deleted in both-files case",
|
|
134
134
|
);
|
|
135
135
|
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const { readFileSync } = await import("node:fs");
|
|
141
|
-
const guidedFlowSource = readFileSync(
|
|
142
|
-
join(import.meta.dirname, "..", "guided-flow.ts"),
|
|
143
|
-
"utf-8",
|
|
144
|
-
);
|
|
145
|
-
|
|
146
|
-
const checkFnIdx = guidedFlowSource.indexOf("checkAutoStartAfterDiscuss");
|
|
147
|
-
const checkFnEnd = guidedFlowSource.indexOf("\nexport ", checkFnIdx + 1);
|
|
148
|
-
const checkFnChunk = guidedFlowSource.slice(checkFnIdx, checkFnEnd > checkFnIdx ? checkFnEnd : checkFnIdx + 5000);
|
|
149
|
-
|
|
150
|
-
assert(
|
|
151
|
-
checkFnChunk.includes("CONTEXT-DRAFT"),
|
|
152
|
-
"checkAutoStartAfterDiscuss should reference CONTEXT-DRAFT for cleanup",
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
assert(
|
|
156
|
-
checkFnChunk.includes("unlinkSync"),
|
|
157
|
-
"checkAutoStartAfterDiscuss should use unlinkSync to delete the draft",
|
|
158
|
-
);
|
|
136
|
+
// Note: source-grep assertions removed per CONTRIBUTING.md (no asserting against
|
|
137
|
+
// readFileSync of source). The behavioral scenarios above already exercise the
|
|
138
|
+
// CONTEXT-DRAFT cleanup path end-to-end via the actual filesystem state.
|
|
159
139
|
|
|
160
140
|
// ─── Cleanup ──────────────────────────────────────────────────────────
|
|
161
141
|
|