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,7 +1,7 @@
|
|
|
1
1
|
import { describe, test } from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
|
-
// ensureDbOpen — Tests that the lazy DB opener creates
|
|
4
|
-
//
|
|
3
|
+
// ensureDbOpen — Tests that the lazy DB opener creates/opens the authoritative
|
|
4
|
+
// database without implicitly importing markdown projections.
|
|
5
5
|
//
|
|
6
6
|
// This covers the bug where interactive (non-auto) sessions got
|
|
7
7
|
// "GSD database is not available" because ensureDbOpen only opened
|
|
@@ -11,7 +11,7 @@ import * as path from 'node:path';
|
|
|
11
11
|
import * as os from 'node:os';
|
|
12
12
|
import * as fs from 'node:fs';
|
|
13
13
|
import { createRequire } from 'node:module';
|
|
14
|
-
import { closeDatabase, isDbAvailable, getDecisionById, _getAdapter } from '../gsd-db.ts';
|
|
14
|
+
import { closeDatabase, isDbAvailable, getDecisionById, SCHEMA_VERSION, _getAdapter } from '../gsd-db.ts';
|
|
15
15
|
|
|
16
16
|
const _require = createRequire(import.meta.url);
|
|
17
17
|
|
|
@@ -284,11 +284,11 @@ function createLegacyV15Db(dbPath: string): void {
|
|
|
284
284
|
}
|
|
285
285
|
|
|
286
286
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
287
|
-
// ensureDbOpen creates DB
|
|
287
|
+
// ensureDbOpen creates DB without implicit Markdown migration
|
|
288
288
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
289
289
|
|
|
290
290
|
describe('ensure-db-open', () => {
|
|
291
|
-
test('ensureDbOpen: creates DB
|
|
291
|
+
test('ensureDbOpen: creates empty DB without importing Markdown', async () => {
|
|
292
292
|
const tmpDir = makeTmpDir();
|
|
293
293
|
const gsdDir = path.join(tmpDir, '.gsd');
|
|
294
294
|
fs.mkdirSync(gsdDir, { recursive: true });
|
|
@@ -319,17 +319,12 @@ describe('ensure-db-open', () => {
|
|
|
319
319
|
|
|
320
320
|
const result = await ensureDbOpen();
|
|
321
321
|
|
|
322
|
-
assert.ok(result === true, 'ensureDbOpen should return true when .gsd/
|
|
322
|
+
assert.ok(result === true, 'ensureDbOpen should return true when .gsd/ exists');
|
|
323
323
|
assert.ok(fs.existsSync(dbPath), 'DB file should be created after ensureDbOpen');
|
|
324
324
|
assert.ok(isDbAvailable(), 'DB should be available after ensureDbOpen');
|
|
325
325
|
|
|
326
|
-
// Verify that Markdown migration actually ran
|
|
327
326
|
const decision = getDecisionById('D001');
|
|
328
|
-
assert.
|
|
329
|
-
if (decision) {
|
|
330
|
-
assert.deepStrictEqual(decision.scope, 'architecture', 'Migrated decision scope should match');
|
|
331
|
-
assert.deepStrictEqual(decision.choice, 'SQLite', 'Migrated decision choice should match');
|
|
332
|
-
}
|
|
327
|
+
assert.equal(decision, null, 'D001 should not be imported from DECISIONS.md without explicit migration');
|
|
333
328
|
} finally {
|
|
334
329
|
process.cwd = origCwd;
|
|
335
330
|
closeDatabase();
|
|
@@ -360,7 +355,7 @@ describe('ensure-db-open', () => {
|
|
|
360
355
|
assert.ok(result === true, 'ensureDbOpen should honor explicit basePath');
|
|
361
356
|
assert.equal(process.cwd(), originalCwd, 'ensureDbOpen should not mutate process.cwd');
|
|
362
357
|
assert.ok(isDbAvailable(), 'DB should be available after explicit open');
|
|
363
|
-
assert.
|
|
358
|
+
assert.equal(getDecisionById('D777'), null, 'explicit basePath should not import DECISIONS.md');
|
|
364
359
|
} finally {
|
|
365
360
|
closeDatabase();
|
|
366
361
|
cleanupDir(tmpDir);
|
|
@@ -389,7 +384,7 @@ describe('ensure-db-open', () => {
|
|
|
389
384
|
assert.ok(db, 'adapter should be available after ensureDbOpen');
|
|
390
385
|
assert.equal(
|
|
391
386
|
db.prepare('SELECT MAX(version) as version FROM schema_version').get()?.version,
|
|
392
|
-
|
|
387
|
+
SCHEMA_VERSION,
|
|
393
388
|
'legacy DB should migrate to current schema version',
|
|
394
389
|
);
|
|
395
390
|
|
|
@@ -519,9 +514,9 @@ describe('ensure-db-open', () => {
|
|
|
519
514
|
try {
|
|
520
515
|
const { ensureDbOpen } = await import('../bootstrap/dynamic-tools.ts');
|
|
521
516
|
assert.equal(await ensureDbOpen(firstDir), true);
|
|
522
|
-
assert.
|
|
517
|
+
assert.equal(getDecisionById('D101'), null, 'first DB should not import DECISIONS.md');
|
|
523
518
|
assert.equal(await ensureDbOpen(secondDir), true);
|
|
524
|
-
assert.
|
|
519
|
+
assert.equal(getDecisionById('D202'), null, 'second DB should not import DECISIONS.md');
|
|
525
520
|
assert.equal(getDecisionById('D101'), null, 'first DB should no longer be active after switch');
|
|
526
521
|
} finally {
|
|
527
522
|
closeDatabase();
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
claimEscalationOverride,
|
|
20
20
|
findUnappliedEscalationOverride,
|
|
21
21
|
listEscalationArtifacts,
|
|
22
|
+
SCHEMA_VERSION,
|
|
22
23
|
_getAdapter,
|
|
23
24
|
} from "../gsd-db.ts";
|
|
24
25
|
import {
|
|
@@ -348,7 +349,7 @@ test("ADR-011 P2: schema v20 fresh DB has all escalation columns on tasks + sour
|
|
|
348
349
|
assert.ok(decCols.includes("source"), "decisions table must have source column");
|
|
349
350
|
|
|
350
351
|
const version = adapter.prepare("SELECT MAX(version) as v FROM schema_version").get();
|
|
351
|
-
assert.equal(version?.["v"],
|
|
352
|
+
assert.equal(version?.["v"], SCHEMA_VERSION);
|
|
352
353
|
});
|
|
353
354
|
|
|
354
355
|
test("ADR-011 P2: findUnappliedEscalationOverride returns null when escalation_pending=1 (still pending)", (t) => {
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GSD-2 / guided-flow — regression tests for Gate 1b orphan discrimination
|
|
3
|
+
*
|
|
4
|
+
* Gate 1b in checkAutoStartAfterDiscuss discriminates between two "queued" states:
|
|
5
|
+
* (a) plan-blocked: discuss completed (CONTEXT.md on disk), but gsd_plan_milestone
|
|
6
|
+
* was hard-blocked by the depth-verification gate. DB row stuck at "queued".
|
|
7
|
+
* → emit recovery hint directing the LLM to retry gsd_plan_milestone.
|
|
8
|
+
* (b) discuss-incomplete: discuss did not finish, no CONTEXT.md, DB row "queued".
|
|
9
|
+
* → silent block (no recovery hint).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
13
|
+
import assert from "node:assert/strict";
|
|
14
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import { tmpdir } from "node:os";
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
checkAutoStartAfterDiscuss,
|
|
20
|
+
setPendingAutoStart,
|
|
21
|
+
clearPendingAutoStart,
|
|
22
|
+
} from "../guided-flow.ts";
|
|
23
|
+
import { drainLogs } from "../workflow-logger.ts";
|
|
24
|
+
import {
|
|
25
|
+
openDatabase,
|
|
26
|
+
closeDatabase,
|
|
27
|
+
insertMilestone,
|
|
28
|
+
} from "../gsd-db.ts";
|
|
29
|
+
|
|
30
|
+
// ─── Harness ───────────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
interface MockCapture {
|
|
33
|
+
notifies: Array<{ msg: string; level: string }>;
|
|
34
|
+
messages: Array<{ payload: any; options: any }>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function mkCapture(): MockCapture {
|
|
38
|
+
return { notifies: [], messages: [] };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function mkCtx(cap: MockCapture): any {
|
|
42
|
+
return {
|
|
43
|
+
ui: {
|
|
44
|
+
notify: (msg: string, level: string) => {
|
|
45
|
+
cap.notifies.push({ msg, level });
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function mkPi(cap: MockCapture): any {
|
|
52
|
+
return {
|
|
53
|
+
sendMessage: (payload: any, options: any) => {
|
|
54
|
+
cap.messages.push({ payload, options });
|
|
55
|
+
},
|
|
56
|
+
setActiveTools: () => undefined,
|
|
57
|
+
getActiveTools: () => [],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Create a minimal temp tree with a .gsd/milestones/M001 directory.
|
|
63
|
+
*/
|
|
64
|
+
function mkBase(): string {
|
|
65
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-gate1b-"));
|
|
66
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
|
|
67
|
+
return base;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ─── Tests ─────────────────────────────────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
describe("Gate 1b orphan discrimination in checkAutoStartAfterDiscuss", () => {
|
|
73
|
+
let base: string;
|
|
74
|
+
let cap: MockCapture;
|
|
75
|
+
|
|
76
|
+
beforeEach(() => {
|
|
77
|
+
clearPendingAutoStart();
|
|
78
|
+
drainLogs(); // discard noise from prior tests
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
afterEach(() => {
|
|
82
|
+
closeDatabase();
|
|
83
|
+
clearPendingAutoStart();
|
|
84
|
+
if (base) {
|
|
85
|
+
rmSync(base, { recursive: true, force: true });
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("plan-blocked: CONTEXT.md present + DB row queued → returns false + recovery hint emitted", () => {
|
|
90
|
+
base = mkBase();
|
|
91
|
+
openDatabase(":memory:");
|
|
92
|
+
|
|
93
|
+
// DB row exists with status "queued" (plan_milestone was blocked)
|
|
94
|
+
insertMilestone({ id: "M001", title: "Test Milestone", status: "queued" });
|
|
95
|
+
|
|
96
|
+
// CONTEXT.md on disk (discuss phase completed)
|
|
97
|
+
writeFileSync(
|
|
98
|
+
join(base, ".gsd", "milestones", "M001", "M001-CONTEXT.md"),
|
|
99
|
+
"# M001: Test Milestone\n\nContext written by discuss phase.\n",
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
cap = mkCapture();
|
|
103
|
+
setPendingAutoStart(base, {
|
|
104
|
+
basePath: base,
|
|
105
|
+
milestoneId: "M001",
|
|
106
|
+
ctx: mkCtx(cap),
|
|
107
|
+
pi: mkPi(cap),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const result = checkAutoStartAfterDiscuss();
|
|
111
|
+
|
|
112
|
+
// Must return false — auto-start should not proceed
|
|
113
|
+
assert.equal(result, false, "checkAutoStartAfterDiscuss must return false (plan still blocked)");
|
|
114
|
+
|
|
115
|
+
// Recovery hint must be sent to the LLM
|
|
116
|
+
assert.equal(
|
|
117
|
+
cap.messages.length,
|
|
118
|
+
1,
|
|
119
|
+
"exactly one sendMessage call expected for the recovery hint",
|
|
120
|
+
);
|
|
121
|
+
assert.equal(
|
|
122
|
+
cap.messages[0].payload.customType,
|
|
123
|
+
"gsd-plan-milestone-blocked-recovery",
|
|
124
|
+
"recovery message must have customType gsd-plan-milestone-blocked-recovery",
|
|
125
|
+
);
|
|
126
|
+
assert.equal(
|
|
127
|
+
cap.messages[0].options.triggerTurn,
|
|
128
|
+
true,
|
|
129
|
+
"recovery message must set triggerTurn: true",
|
|
130
|
+
);
|
|
131
|
+
assert.match(
|
|
132
|
+
cap.messages[0].payload.content,
|
|
133
|
+
/gsd_plan_milestone/,
|
|
134
|
+
"recovery message content must mention gsd_plan_milestone",
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
// User must be notified via ctx.ui.notify
|
|
138
|
+
assert.ok(
|
|
139
|
+
cap.notifies.some((n) => n.level === "warning" && /queued/.test(n.msg)),
|
|
140
|
+
"user must be notified with a warning about the queued state",
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// logWarning must have recorded the Gate 1b event
|
|
144
|
+
const logs = drainLogs();
|
|
145
|
+
const gate1bLog = logs.find(
|
|
146
|
+
(e) => e.component === "guided" && /Gate 1b/.test(e.message),
|
|
147
|
+
);
|
|
148
|
+
assert.ok(gate1bLog, "Gate 1b warning must be logged via logWarning");
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("discuss-incomplete: no CONTEXT.md + DB row queued → returns false silently (no recovery hint)", () => {
|
|
152
|
+
base = mkBase();
|
|
153
|
+
openDatabase(":memory:");
|
|
154
|
+
|
|
155
|
+
// DB row exists with status "queued", but NO CONTEXT.md on disk
|
|
156
|
+
insertMilestone({ id: "M001", title: "Test Milestone", status: "queued" });
|
|
157
|
+
|
|
158
|
+
// No CONTEXT.md written — discuss phase is incomplete
|
|
159
|
+
cap = mkCapture();
|
|
160
|
+
setPendingAutoStart(base, {
|
|
161
|
+
basePath: base,
|
|
162
|
+
milestoneId: "M001",
|
|
163
|
+
ctx: mkCtx(cap),
|
|
164
|
+
pi: mkPi(cap),
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
drainLogs(); // clear any noise before the call
|
|
168
|
+
|
|
169
|
+
const result = checkAutoStartAfterDiscuss();
|
|
170
|
+
|
|
171
|
+
// Must return false — silent block
|
|
172
|
+
assert.equal(result, false, "checkAutoStartAfterDiscuss must return false when discuss is incomplete");
|
|
173
|
+
|
|
174
|
+
// No recovery hint — Gate 1 blocks before Gate 1b is reached
|
|
175
|
+
assert.equal(
|
|
176
|
+
cap.messages.length,
|
|
177
|
+
0,
|
|
178
|
+
"no sendMessage calls expected when CONTEXT.md is absent",
|
|
179
|
+
);
|
|
180
|
+
assert.equal(
|
|
181
|
+
cap.notifies.length,
|
|
182
|
+
0,
|
|
183
|
+
"no user notifications expected for discuss-incomplete case",
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
// No Gate 1b log entry
|
|
187
|
+
const logs = drainLogs();
|
|
188
|
+
const gate1bLog = logs.find(
|
|
189
|
+
(e) => e.component === "guided" && /Gate 1b/.test(e.message),
|
|
190
|
+
);
|
|
191
|
+
assert.equal(gate1bLog, undefined, "Gate 1b must not log when CONTEXT.md is absent");
|
|
192
|
+
});
|
|
193
|
+
});
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
// GSD-2 + Gate 1b recovery bound corrections — regression tests for the two bugs
|
|
2
|
+
// found in peer review of the H1 fix (commit f0e1d42a2):
|
|
3
|
+
// 1. Escalation message must describe /gsd (counter reset) AND /gsd-debug (diagnose).
|
|
4
|
+
// 2. planBlockedRecoveryCount must NOT increment when pi.sendMessage throws.
|
|
5
|
+
|
|
6
|
+
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
checkAutoStartAfterDiscuss,
|
|
14
|
+
setPendingAutoStart,
|
|
15
|
+
clearPendingAutoStart,
|
|
16
|
+
_getPendingAutoStart,
|
|
17
|
+
} from "../guided-flow.ts";
|
|
18
|
+
import { drainLogs } from "../workflow-logger.ts";
|
|
19
|
+
import {
|
|
20
|
+
openDatabase,
|
|
21
|
+
closeDatabase,
|
|
22
|
+
insertMilestone,
|
|
23
|
+
} from "../gsd-db.ts";
|
|
24
|
+
|
|
25
|
+
// ─── Harness ───────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
interface MockCapture {
|
|
28
|
+
notifies: Array<{ msg: string; level: string }>;
|
|
29
|
+
messages: Array<{ payload: any; options: any }>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function mkCapture(): MockCapture {
|
|
33
|
+
return { notifies: [], messages: [] };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function mkCtx(cap: MockCapture): any {
|
|
37
|
+
return {
|
|
38
|
+
ui: {
|
|
39
|
+
notify: (msg: string, level: string) => {
|
|
40
|
+
cap.notifies.push({ msg, level });
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Returns a pi stub whose sendMessage throws on the first call, succeeds after. */
|
|
47
|
+
function mkPiThrowOnce(cap: MockCapture): any {
|
|
48
|
+
let callCount = 0;
|
|
49
|
+
return {
|
|
50
|
+
sendMessage: (payload: any, options: any) => {
|
|
51
|
+
callCount += 1;
|
|
52
|
+
if (callCount === 1) {
|
|
53
|
+
throw new Error("transient network error");
|
|
54
|
+
}
|
|
55
|
+
cap.messages.push({ payload, options });
|
|
56
|
+
},
|
|
57
|
+
setActiveTools: () => undefined,
|
|
58
|
+
getActiveTools: () => [],
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function mkPi(cap: MockCapture): any {
|
|
63
|
+
return {
|
|
64
|
+
sendMessage: (payload: any, options: any) => {
|
|
65
|
+
cap.messages.push({ payload, options });
|
|
66
|
+
},
|
|
67
|
+
setActiveTools: () => undefined,
|
|
68
|
+
getActiveTools: () => [],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function mkBase(): string {
|
|
73
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-gate1b-corrections-"));
|
|
74
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
|
|
75
|
+
writeFileSync(
|
|
76
|
+
join(base, ".gsd", "milestones", "M001", "M001-CONTEXT.md"),
|
|
77
|
+
"# M001: Corrections Test\n\nContext written by discuss phase.\n",
|
|
78
|
+
);
|
|
79
|
+
return base;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ─── Tests ─────────────────────────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
describe("Gate 1b recovery bound corrections", () => {
|
|
85
|
+
let base: string;
|
|
86
|
+
let cap: MockCapture;
|
|
87
|
+
|
|
88
|
+
beforeEach(() => {
|
|
89
|
+
clearPendingAutoStart();
|
|
90
|
+
drainLogs();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
afterEach(() => {
|
|
94
|
+
closeDatabase();
|
|
95
|
+
clearPendingAutoStart();
|
|
96
|
+
if (base) {
|
|
97
|
+
rmSync(base, { recursive: true, force: true });
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// ── Fix 1: escalation message ──────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
test("escalation message describes /gsd for reset AND /gsd-debug for diagnosis", () => {
|
|
104
|
+
base = mkBase();
|
|
105
|
+
openDatabase(":memory:");
|
|
106
|
+
insertMilestone({ id: "M001", title: "Corrections Test", status: "queued" });
|
|
107
|
+
|
|
108
|
+
cap = mkCapture();
|
|
109
|
+
setPendingAutoStart(base, {
|
|
110
|
+
basePath: base,
|
|
111
|
+
milestoneId: "M001",
|
|
112
|
+
ctx: mkCtx(cap),
|
|
113
|
+
pi: mkPi(cap),
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Exhaust the recovery budget (MAX = 3)
|
|
117
|
+
checkAutoStartAfterDiscuss(); // count → 1
|
|
118
|
+
checkAutoStartAfterDiscuss(); // count → 2
|
|
119
|
+
checkAutoStartAfterDiscuss(); // count → 3
|
|
120
|
+
|
|
121
|
+
cap.notifies = [];
|
|
122
|
+
drainLogs();
|
|
123
|
+
|
|
124
|
+
// This call hits the cap and must escalate
|
|
125
|
+
const result = checkAutoStartAfterDiscuss();
|
|
126
|
+
assert.equal(result, false, "escalation call must return false");
|
|
127
|
+
|
|
128
|
+
const errorNotify = cap.notifies.find((n) => n.level === "error");
|
|
129
|
+
assert.ok(errorNotify, "escalation must emit a notify with level 'error'");
|
|
130
|
+
|
|
131
|
+
// Must mention /gsd with reset semantics
|
|
132
|
+
assert.match(
|
|
133
|
+
errorNotify.msg,
|
|
134
|
+
/\/gsd\b/,
|
|
135
|
+
"escalation message must reference /gsd (the command that resets the counter)",
|
|
136
|
+
);
|
|
137
|
+
assert.match(
|
|
138
|
+
errorNotify.msg,
|
|
139
|
+
/reset/i,
|
|
140
|
+
"escalation message must use the word 'reset' so users know /gsd resets the counter",
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// Must also mention /gsd-debug
|
|
144
|
+
assert.match(
|
|
145
|
+
errorNotify.msg,
|
|
146
|
+
/\/gsd-debug/i,
|
|
147
|
+
"escalation message must also reference /gsd-debug for diagnosis",
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
// Must NOT suggest /gsd-debug alone as the sole remediation
|
|
151
|
+
assert.doesNotMatch(
|
|
152
|
+
errorNotify.msg,
|
|
153
|
+
/^[^/]*\/gsd-debug[^/]*$/,
|
|
154
|
+
"escalation message must not mention /gsd-debug as the only option",
|
|
155
|
+
);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// ── Fix 2: counter ordering ────────────────────────────────────────────
|
|
159
|
+
|
|
160
|
+
test("counter stays at 0 when sendMessage throws on the first call", () => {
|
|
161
|
+
base = mkBase();
|
|
162
|
+
openDatabase(":memory:");
|
|
163
|
+
insertMilestone({ id: "M001", title: "Corrections Test", status: "queued" });
|
|
164
|
+
|
|
165
|
+
cap = mkCapture();
|
|
166
|
+
setPendingAutoStart(base, {
|
|
167
|
+
basePath: base,
|
|
168
|
+
milestoneId: "M001",
|
|
169
|
+
ctx: mkCtx(cap),
|
|
170
|
+
pi: mkPiThrowOnce(cap),
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// First call: sendMessage throws — counter must NOT increment
|
|
174
|
+
const result = checkAutoStartAfterDiscuss();
|
|
175
|
+
assert.equal(result, false, "must return false even when sendMessage throws");
|
|
176
|
+
|
|
177
|
+
const entry = _getPendingAutoStart(base);
|
|
178
|
+
assert.ok(entry, "entry must still exist after a failed sendMessage");
|
|
179
|
+
assert.equal(
|
|
180
|
+
entry.planBlockedRecoveryCount,
|
|
181
|
+
0,
|
|
182
|
+
"counter must remain 0 when sendMessage throws — no budget burned by transient failure",
|
|
183
|
+
);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("counter increments to 1 on the second call when first sendMessage threw", () => {
|
|
187
|
+
base = mkBase();
|
|
188
|
+
openDatabase(":memory:");
|
|
189
|
+
insertMilestone({ id: "M001", title: "Corrections Test", status: "queued" });
|
|
190
|
+
|
|
191
|
+
cap = mkCapture();
|
|
192
|
+
setPendingAutoStart(base, {
|
|
193
|
+
basePath: base,
|
|
194
|
+
milestoneId: "M001",
|
|
195
|
+
ctx: mkCtx(cap),
|
|
196
|
+
pi: mkPiThrowOnce(cap),
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
checkAutoStartAfterDiscuss(); // sendMessage throws → count stays 0
|
|
200
|
+
|
|
201
|
+
const entryAfterThrow = _getPendingAutoStart(base);
|
|
202
|
+
assert.equal(entryAfterThrow!.planBlockedRecoveryCount, 0, "count is 0 after throw");
|
|
203
|
+
|
|
204
|
+
checkAutoStartAfterDiscuss(); // sendMessage succeeds → count becomes 1
|
|
205
|
+
assert.equal(cap.messages.length, 1, "second call must produce one successful sendMessage");
|
|
206
|
+
|
|
207
|
+
const entryAfterSuccess = _getPendingAutoStart(base);
|
|
208
|
+
assert.equal(
|
|
209
|
+
entryAfterSuccess!.planBlockedRecoveryCount,
|
|
210
|
+
1,
|
|
211
|
+
"counter must be 1 after first successful dispatch",
|
|
212
|
+
);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("3 successful sendMessage calls exhaust the budget; 4th emits escalation notify", () => {
|
|
216
|
+
base = mkBase();
|
|
217
|
+
openDatabase(":memory:");
|
|
218
|
+
insertMilestone({ id: "M001", title: "Corrections Test", status: "queued" });
|
|
219
|
+
|
|
220
|
+
cap = mkCapture();
|
|
221
|
+
setPendingAutoStart(base, {
|
|
222
|
+
basePath: base,
|
|
223
|
+
milestoneId: "M001",
|
|
224
|
+
ctx: mkCtx(cap),
|
|
225
|
+
pi: mkPi(cap),
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// Three successful recoveries
|
|
229
|
+
checkAutoStartAfterDiscuss(); // count → 1
|
|
230
|
+
checkAutoStartAfterDiscuss(); // count → 2
|
|
231
|
+
checkAutoStartAfterDiscuss(); // count → 3
|
|
232
|
+
|
|
233
|
+
const entry = _getPendingAutoStart(base);
|
|
234
|
+
assert.equal(entry!.planBlockedRecoveryCount, 3, "counter must be 3 after three successes");
|
|
235
|
+
assert.equal(cap.messages.length, 3, "three sendMessage calls must have occurred");
|
|
236
|
+
|
|
237
|
+
// Fourth call hits the cap
|
|
238
|
+
cap.notifies = [];
|
|
239
|
+
cap.messages = [];
|
|
240
|
+
const resultAtCap = checkAutoStartAfterDiscuss();
|
|
241
|
+
assert.equal(resultAtCap, false, "4th call must return false");
|
|
242
|
+
assert.equal(cap.messages.length, 0, "4th call must NOT call sendMessage");
|
|
243
|
+
const errorNotify = cap.notifies.find((n) => n.level === "error");
|
|
244
|
+
assert.ok(errorNotify, "4th call must emit escalation notify with level 'error'");
|
|
245
|
+
});
|
|
246
|
+
});
|