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
|
@@ -175,6 +175,7 @@ import { getErrorMessage } from "./error-utils.js";
|
|
|
175
175
|
import { recoverFailedMigration } from "./migrate-external.js";
|
|
176
176
|
import { initRegistry, convertDispatchRules } from "./rule-registry.js";
|
|
177
177
|
import { emitJournalEvent as _emitJournalEvent, type JournalEntry } from "./journal.js";
|
|
178
|
+
import { isClosedStatus } from "./status-guards.js";
|
|
178
179
|
import {
|
|
179
180
|
type AutoDashboardData,
|
|
180
181
|
updateProgressWidget as _updateProgressWidget,
|
|
@@ -194,6 +195,7 @@ import {
|
|
|
194
195
|
import { isDbAvailable, getMilestone } from "./gsd-db.js";
|
|
195
196
|
import { countPendingCaptures } from "./captures.js";
|
|
196
197
|
import { CMUX_CHANNELS, type CmuxLogLevel } from "../shared/cmux-events.js";
|
|
198
|
+
import { ensureDbOpen } from "./bootstrap/dynamic-tools.js";
|
|
197
199
|
|
|
198
200
|
function makeCmuxEmitters(pi: ExtensionAPI) {
|
|
199
201
|
return {
|
|
@@ -254,6 +256,7 @@ export type {
|
|
|
254
256
|
} from "./auto/session.js";
|
|
255
257
|
import { autoSession as s } from "./auto-runtime-state.js";
|
|
256
258
|
import { gsdHome } from "./gsd-home.js";
|
|
259
|
+
import { createWorkspace, scopeMilestone } from "./workspace.js";
|
|
257
260
|
|
|
258
261
|
// ── ENCAPSULATION INVARIANT ─────────────────────────────────────────────────
|
|
259
262
|
// ALL mutable auto-mode state lives in the AutoSession class (auto/session.ts).
|
|
@@ -322,6 +325,32 @@ function restoreMilestoneLockEnv(): void {
|
|
|
322
325
|
s.milestoneLockEnvCaptured = false;
|
|
323
326
|
}
|
|
324
327
|
|
|
328
|
+
/**
|
|
329
|
+
* Rebuild s.scope from the current s.basePath / s.originalBasePath / s.currentMilestoneId.
|
|
330
|
+
*
|
|
331
|
+
* Pass the worktree path as rawPath when entering a worktree so createWorkspace
|
|
332
|
+
* can detect the worktree layout and set mode="worktree". When no worktree is
|
|
333
|
+
* active, rawPath should equal the project root.
|
|
334
|
+
*
|
|
335
|
+
* Clears s.scope when milestoneId is absent — scope is only meaningful when a
|
|
336
|
+
* milestone is active.
|
|
337
|
+
*
|
|
338
|
+
* TODO(C8): remove basePath/originalBasePath once all readers use s.scope.
|
|
339
|
+
*/
|
|
340
|
+
function rebuildScope(rawPath: string, milestoneId: string | null): void {
|
|
341
|
+
if (!milestoneId) {
|
|
342
|
+
s.scope = null;
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
try {
|
|
346
|
+
const workspace = createWorkspace(rawPath);
|
|
347
|
+
s.scope = scopeMilestone(workspace, milestoneId);
|
|
348
|
+
} catch {
|
|
349
|
+
// Non-fatal — scope is additive. Existing readers still use basePath.
|
|
350
|
+
s.scope = null;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
325
354
|
function normalizeSessionFilePath(raw: unknown): string | null {
|
|
326
355
|
if (typeof raw !== "string") return null;
|
|
327
356
|
const trimmed = raw.trim();
|
|
@@ -507,6 +536,26 @@ export function _setAutoActiveForTest(active: boolean): void {
|
|
|
507
536
|
s.active = active;
|
|
508
537
|
}
|
|
509
538
|
|
|
539
|
+
/**
|
|
540
|
+
* Test-only seam: emit the missing-worktree warning exactly as the resume path
|
|
541
|
+
* does. Allows unit tests to verify the warning is produced without
|
|
542
|
+
* bootstrapping the full auto-mode entry point. Do not use in production code.
|
|
543
|
+
*/
|
|
544
|
+
export function _warnIfWorktreeMissingForTest(
|
|
545
|
+
worktreePath: string | null | undefined,
|
|
546
|
+
milestoneId: string,
|
|
547
|
+
): boolean {
|
|
548
|
+
if (worktreePath && !existsSync(worktreePath)) {
|
|
549
|
+
logWarning(
|
|
550
|
+
"session",
|
|
551
|
+
`Worktree was expected at ${worktreePath} but is missing. Continuing in project-root mode. To restart with a fresh worktree, run /gsd-debug or recreate the milestone.`,
|
|
552
|
+
{ file: "auto.ts", milestoneId },
|
|
553
|
+
);
|
|
554
|
+
return true;
|
|
555
|
+
}
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
558
|
+
|
|
510
559
|
export function isAutoPaused(): boolean {
|
|
511
560
|
return s.paused;
|
|
512
561
|
}
|
|
@@ -1506,14 +1555,29 @@ export async function startAuto(
|
|
|
1506
1555
|
);
|
|
1507
1556
|
if (shouldResumePausedSession) {
|
|
1508
1557
|
// Validate the milestone still exists and isn't already complete (#1664).
|
|
1558
|
+
// DB status is authoritative when available; SUMMARY.md is a legacy
|
|
1559
|
+
// fallback only for unmigrated/offline projects.
|
|
1509
1560
|
const mDir = resolveMilestonePath(base, meta.milestoneId);
|
|
1510
|
-
const summaryFile = resolveMilestoneFile(base, meta.milestoneId, "SUMMARY");
|
|
1511
1561
|
let summaryIsTerminal = false;
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1562
|
+
let dbAvailable = isDbAvailable();
|
|
1563
|
+
let milestoneRow = dbAvailable ? getMilestone(meta.milestoneId) : null;
|
|
1564
|
+
if (!milestoneRow) {
|
|
1565
|
+
const opened = await ensureDbOpen(base);
|
|
1566
|
+
dbAvailable = opened || isDbAvailable();
|
|
1567
|
+
if (dbAvailable) {
|
|
1568
|
+
milestoneRow = getMilestone(meta.milestoneId);
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
if (dbAvailable) {
|
|
1572
|
+
summaryIsTerminal = !!milestoneRow && isClosedStatus(milestoneRow.status);
|
|
1573
|
+
} else {
|
|
1574
|
+
const summaryFile = resolveMilestoneFile(base, meta.milestoneId, "SUMMARY");
|
|
1575
|
+
if (summaryFile) {
|
|
1576
|
+
try {
|
|
1577
|
+
summaryIsTerminal = classifyMilestoneSummaryContent(readFileSync(summaryFile, "utf-8")) !== "failure";
|
|
1578
|
+
} catch {
|
|
1579
|
+
summaryIsTerminal = false;
|
|
1580
|
+
}
|
|
1517
1581
|
}
|
|
1518
1582
|
}
|
|
1519
1583
|
if (!mDir || summaryIsTerminal) {
|
|
@@ -1536,6 +1600,22 @@ export async function startAuto(
|
|
|
1536
1600
|
s.autoStartTime = meta.autoStartTime || Date.now();
|
|
1537
1601
|
s.sessionMilestoneLock = meta.milestoneLock ?? null;
|
|
1538
1602
|
s.paused = true;
|
|
1603
|
+
// Build scope from persisted state. Use worktreePath when present and
|
|
1604
|
+
// still on disk so mode is detected correctly; fall back to project root.
|
|
1605
|
+
{
|
|
1606
|
+
const persistedWorktreePath = meta.worktreePath ?? null;
|
|
1607
|
+
if (persistedWorktreePath && !existsSync(persistedWorktreePath)) {
|
|
1608
|
+
logWarning(
|
|
1609
|
+
"session",
|
|
1610
|
+
`Worktree was expected at ${persistedWorktreePath} but is missing. Continuing in project-root mode. To restart with a fresh worktree, run /gsd-debug or recreate the milestone.`,
|
|
1611
|
+
{ file: "auto.ts", milestoneId: meta.milestoneId ?? "" },
|
|
1612
|
+
);
|
|
1613
|
+
}
|
|
1614
|
+
const rawForScope = (persistedWorktreePath && existsSync(persistedWorktreePath))
|
|
1615
|
+
? persistedWorktreePath
|
|
1616
|
+
: (s.originalBasePath || base);
|
|
1617
|
+
rebuildScope(rawForScope, s.currentMilestoneId);
|
|
1618
|
+
}
|
|
1539
1619
|
try { unlinkSync(pausedPath); } catch (e) {
|
|
1540
1620
|
if ((e as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
1541
1621
|
logWarning("session", `pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" });
|
|
@@ -1620,10 +1700,19 @@ export async function startAuto(
|
|
|
1620
1700
|
// session (e.g. isolation mode changed, detectWorktreeName differs across
|
|
1621
1701
|
// process restarts). We guard with existsSync so a stale or deleted
|
|
1622
1702
|
// worktree directory safely falls back to the project root.
|
|
1623
|
-
const resumeWorktreePath = freshStartAssessment.pausedSession?.worktreePath;
|
|
1703
|
+
const resumeWorktreePath = freshStartAssessment.pausedSession?.worktreePath ?? null;
|
|
1704
|
+
if (resumeWorktreePath && !existsSync(resumeWorktreePath)) {
|
|
1705
|
+
logWarning(
|
|
1706
|
+
"session",
|
|
1707
|
+
`Worktree was expected at ${resumeWorktreePath} but is missing. Continuing in project-root mode. To restart with a fresh worktree, run /gsd-debug or recreate the milestone.`,
|
|
1708
|
+
{ file: "auto.ts", milestoneId: s.currentMilestoneId ?? "" },
|
|
1709
|
+
);
|
|
1710
|
+
}
|
|
1624
1711
|
if (resumeWorktreePath && existsSync(resumeWorktreePath)) {
|
|
1625
1712
|
s.basePath = resumeWorktreePath;
|
|
1626
1713
|
}
|
|
1714
|
+
// Rebuild scope now that s.basePath reflects the actual worktree (or project root).
|
|
1715
|
+
rebuildScope(s.basePath, s.currentMilestoneId);
|
|
1627
1716
|
// Ensure the workflow-logger audit log is pinned to the project root
|
|
1628
1717
|
// even when auto-mode is entered via a path that bypasses the
|
|
1629
1718
|
// bootstrap/dynamic-tools ensureDbOpen() → setLogBasePath() chain
|
|
@@ -1652,6 +1741,8 @@ export async function startAuto(
|
|
|
1652
1741
|
buildResolver().enterMilestone(s.currentMilestoneId, {
|
|
1653
1742
|
notify: ctx.ui.notify.bind(ctx.ui),
|
|
1654
1743
|
});
|
|
1744
|
+
// s.basePath may have been updated to a worktree path by enterMilestone.
|
|
1745
|
+
rebuildScope(s.basePath, s.currentMilestoneId);
|
|
1655
1746
|
}
|
|
1656
1747
|
|
|
1657
1748
|
registerSigtermHandler(lockBase());
|
|
@@ -1766,6 +1857,10 @@ export async function startAuto(
|
|
|
1766
1857
|
);
|
|
1767
1858
|
if (!ready) return;
|
|
1768
1859
|
|
|
1860
|
+
// Build scope after bootstrap has populated s.basePath / s.originalBasePath /
|
|
1861
|
+
// s.currentMilestoneId (including worktree setup inside bootstrapAutoSession).
|
|
1862
|
+
rebuildScope(s.basePath, s.currentMilestoneId);
|
|
1863
|
+
|
|
1769
1864
|
captureProjectRootEnv(s.originalBasePath || s.basePath);
|
|
1770
1865
|
try {
|
|
1771
1866
|
pi.events.emit(CMUX_CHANNELS.SIDEBAR, { action: "sync" as const, preferences: loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, state: await deriveState(s.basePath) });
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import {
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
3
|
|
|
4
4
|
import type { ExtensionAPI } from "@gsd/pi-coding-agent";
|
|
5
5
|
import { createBashTool, createEditTool, createReadTool, createWriteTool } from "@gsd/pi-coding-agent";
|
|
6
6
|
|
|
7
7
|
import { DEFAULT_BASH_TIMEOUT_SECS } from "../constants.js";
|
|
8
8
|
import { setLogBasePath, logWarning } from "../workflow-logger.js";
|
|
9
|
+
import { resolveGsdPathContract } from "../paths.js";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Resolve the correct DB path for the current working directory.
|
|
@@ -14,75 +15,16 @@ import { setLogBasePath, logWarning } from "../workflow-logger.js";
|
|
|
14
15
|
* returns `<basePath>/.gsd/gsd.db`.
|
|
15
16
|
*/
|
|
16
17
|
export function resolveProjectRootDbPath(basePath: string): string {
|
|
17
|
-
|
|
18
|
-
// A worktree path looks like: /project/root/.gsd/worktrees/M001/...
|
|
19
|
-
// We need to resolve back to /project/root/.gsd/gsd.db
|
|
20
|
-
const marker = `${sep}.gsd${sep}worktrees${sep}`;
|
|
21
|
-
const idx = basePath.indexOf(marker);
|
|
22
|
-
if (idx !== -1) {
|
|
23
|
-
const projectRoot = basePath.slice(0, idx);
|
|
24
|
-
return join(projectRoot, ".gsd", "gsd.db");
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Also handle forward-slash paths on all platforms
|
|
28
|
-
const fwdMarker = "/.gsd/worktrees/";
|
|
29
|
-
const fwdIdx = basePath.indexOf(fwdMarker);
|
|
30
|
-
if (fwdIdx !== -1) {
|
|
31
|
-
const projectRoot = basePath.slice(0, fwdIdx);
|
|
32
|
-
return join(projectRoot, ".gsd", "gsd.db");
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// External-state layout: ~/.gsd/projects/<hash>/worktrees/<MID>/...
|
|
36
|
-
// Resolve to ~/.gsd/projects/<hash>/gsd.db (the canonical project DB) (#2952).
|
|
37
|
-
// Must be checked before the generic symlink-resolved handler: both match
|
|
38
|
-
// /.gsd/projects/<hash>/worktrees/ but require different resolution targets.
|
|
39
|
-
const extRe = /[/\\]\.gsd[/\\]projects[/\\][a-f0-9]+[/\\]worktrees(?:[/\\]|$)/;
|
|
40
|
-
const extMatch = extRe.exec(basePath);
|
|
41
|
-
if (extMatch) {
|
|
42
|
-
const matchStr = extMatch[0];
|
|
43
|
-
// Find the "/worktrees" portion within the match and slice up to it
|
|
44
|
-
const wtIdx = matchStr.search(/[/\\]worktrees(?:[/\\]|$)/);
|
|
45
|
-
const projectStateRoot = basePath.slice(0, extMatch.index + wtIdx);
|
|
46
|
-
return join(projectStateRoot, "gsd.db");
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Symlink-resolved layout: /.gsd/projects/<hash>/worktrees/M001/...
|
|
50
|
-
// The project root is everything before /.gsd/projects/ (#2517)
|
|
51
|
-
const symlinkMarker = `${sep}.gsd${sep}projects${sep}`;
|
|
52
|
-
const symlinkIdx = basePath.indexOf(symlinkMarker);
|
|
53
|
-
if (symlinkIdx !== -1) {
|
|
54
|
-
const afterProjects = basePath.slice(symlinkIdx + symlinkMarker.length);
|
|
55
|
-
// Expect: <hash>/worktrees/...
|
|
56
|
-
const worktreeSeg = `${sep}worktrees${sep}`;
|
|
57
|
-
if (afterProjects.includes(worktreeSeg)) {
|
|
58
|
-
const projectRoot = basePath.slice(0, symlinkIdx);
|
|
59
|
-
return join(projectRoot, ".gsd", "gsd.db");
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Forward-slash variant for symlink-resolved layout
|
|
64
|
-
const fwdSymlinkMarker = "/.gsd/projects/";
|
|
65
|
-
const fwdSymlinkIdx = basePath.indexOf(fwdSymlinkMarker);
|
|
66
|
-
if (fwdSymlinkIdx !== -1) {
|
|
67
|
-
const afterProjects = basePath.slice(fwdSymlinkIdx + fwdSymlinkMarker.length);
|
|
68
|
-
if (afterProjects.includes("/worktrees/")) {
|
|
69
|
-
const projectRoot = basePath.slice(0, fwdSymlinkIdx);
|
|
70
|
-
return join(projectRoot, ".gsd", "gsd.db");
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return join(basePath, ".gsd", "gsd.db");
|
|
18
|
+
return resolveGsdPathContract(basePath).projectDb;
|
|
76
19
|
}
|
|
77
20
|
|
|
78
21
|
export async function ensureDbOpen(basePath: string = process.cwd()): Promise<boolean> {
|
|
79
22
|
try {
|
|
80
23
|
const db = await import("../gsd-db.js");
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const projectRoot = join(dbPath, "..", "..");
|
|
24
|
+
const contract = resolveGsdPathContract(basePath);
|
|
25
|
+
const dbPath = contract.projectDb;
|
|
26
|
+
const gsdDir = contract.projectGsd;
|
|
27
|
+
const projectRoot = dirname(dirname(dbPath));
|
|
86
28
|
|
|
87
29
|
// Open existing DB file (may be at project root for worktrees)
|
|
88
30
|
if (existsSync(dbPath)) {
|
|
@@ -91,26 +33,9 @@ export async function ensureDbOpen(basePath: string = process.cwd()): Promise<bo
|
|
|
91
33
|
return opened;
|
|
92
34
|
}
|
|
93
35
|
|
|
94
|
-
// No DB file — create
|
|
36
|
+
// No DB file — create an empty authoritative DB. Markdown migration is
|
|
37
|
+
// explicit-only; runtime startup must not import projections into state.
|
|
95
38
|
if (existsSync(gsdDir)) {
|
|
96
|
-
const hasDecisions = existsSync(join(gsdDir, "DECISIONS.md"));
|
|
97
|
-
const hasRequirements = existsSync(join(gsdDir, "REQUIREMENTS.md"));
|
|
98
|
-
const hasMilestones = existsSync(join(gsdDir, "milestones"));
|
|
99
|
-
if (hasDecisions || hasRequirements || hasMilestones) {
|
|
100
|
-
const opened = db.openDatabase(dbPath);
|
|
101
|
-
if (opened) {
|
|
102
|
-
setLogBasePath(projectRoot);
|
|
103
|
-
try {
|
|
104
|
-
const { migrateFromMarkdown } = await import("../md-importer.js");
|
|
105
|
-
migrateFromMarkdown(basePath);
|
|
106
|
-
} catch (err) {
|
|
107
|
-
logWarning("bootstrap", `ensureDbOpen auto-migration failed: ${(err as Error).message}`);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return opened;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// .gsd/ exists but has no Markdown content (fresh project) — create empty DB
|
|
114
39
|
const opened = db.openDatabase(dbPath);
|
|
115
40
|
if (opened) setLogBasePath(projectRoot);
|
|
116
41
|
return opened;
|
|
@@ -76,7 +76,7 @@ export function registerHooks(
|
|
|
76
76
|
const { initHealthWidget } = await import("../health-widget.js");
|
|
77
77
|
initHealthWidget(ctx);
|
|
78
78
|
}
|
|
79
|
-
resetWriteGateState();
|
|
79
|
+
resetWriteGateState(process.cwd());
|
|
80
80
|
resetToolCallLoopGuard();
|
|
81
81
|
approvalQuestionAbortInFlight = false;
|
|
82
82
|
await resetAskUserQuestionsTurnCache();
|
|
@@ -126,10 +126,10 @@ export function registerHooks(
|
|
|
126
126
|
pi.on("session_switch", async (_event, ctx) => {
|
|
127
127
|
initNotificationStore(process.cwd());
|
|
128
128
|
installNotifyInterceptor(ctx);
|
|
129
|
-
resetWriteGateState();
|
|
129
|
+
resetWriteGateState(process.cwd());
|
|
130
130
|
resetToolCallLoopGuard();
|
|
131
131
|
await resetAskUserQuestionsTurnCache();
|
|
132
|
-
clearDiscussionFlowState();
|
|
132
|
+
clearDiscussionFlowState(process.cwd());
|
|
133
133
|
await syncServiceTierStatus(ctx);
|
|
134
134
|
await applyDisabledModelProviderPolicy(ctx);
|
|
135
135
|
// Skip MCP auto-prep when running inside an auto-worktree. The worktree
|
|
@@ -155,12 +155,13 @@ export function registerHooks(
|
|
|
155
155
|
const { getEcosystemReadyPromise } = await import("../ecosystem/loader.js");
|
|
156
156
|
await getEcosystemReadyPromise();
|
|
157
157
|
|
|
158
|
+
const beforeAgentBasePath = process.cwd();
|
|
158
159
|
const pendingApprovalGate = getPendingGate();
|
|
159
160
|
if (pendingApprovalGate && isExplicitApprovalResponse(event.prompt, pendingApprovalGate)) {
|
|
160
|
-
markApprovalGateVerified(pendingApprovalGate);
|
|
161
|
+
markApprovalGateVerified(pendingApprovalGate, beforeAgentBasePath);
|
|
161
162
|
const milestoneId = extractDepthVerificationMilestoneId(pendingApprovalGate);
|
|
162
|
-
if (milestoneId) markDepthVerified(milestoneId);
|
|
163
|
-
clearPendingGate();
|
|
163
|
+
if (milestoneId) markDepthVerified(milestoneId, beforeAgentBasePath);
|
|
164
|
+
clearPendingGate(beforeAgentBasePath);
|
|
164
165
|
}
|
|
165
166
|
|
|
166
167
|
// GSD's own context injection (existing behavior — unchanged).
|
|
@@ -346,7 +347,7 @@ export function registerHooks(
|
|
|
346
347
|
if (!shouldPauseForUserApprovalQuestion(unitType, [event.message])) return;
|
|
347
348
|
|
|
348
349
|
const gateId = approvalGateIdForUnit(unitType, unitId);
|
|
349
|
-
if (gateId) setPendingGate(gateId);
|
|
350
|
+
if (gateId) setPendingGate(gateId, process.cwd());
|
|
350
351
|
|
|
351
352
|
approvalQuestionAbortInFlight = true;
|
|
352
353
|
ctx.ui.notify(
|
|
@@ -393,7 +394,7 @@ export function registerHooks(
|
|
|
393
394
|
const questions: any[] = (event.input as any)?.questions ?? [];
|
|
394
395
|
const questionId = questions.find((question) => typeof question?.id === "string" && isGateQuestionId(question.id))?.id;
|
|
395
396
|
if (typeof questionId === "string") {
|
|
396
|
-
setPendingGate(questionId);
|
|
397
|
+
setPendingGate(questionId, discussionBasePath);
|
|
397
398
|
}
|
|
398
399
|
}
|
|
399
400
|
|
|
@@ -555,7 +556,8 @@ export function registerHooks(
|
|
|
555
556
|
}
|
|
556
557
|
const toolName = canonicalToolName(event.toolName);
|
|
557
558
|
if (toolName !== "ask_user_questions") return;
|
|
558
|
-
const
|
|
559
|
+
const basePath = process.cwd();
|
|
560
|
+
const milestoneId = await getDiscussionMilestoneIdFor(basePath);
|
|
559
561
|
const queueActive = isQueuePhaseActive();
|
|
560
562
|
|
|
561
563
|
const details = event.details as any;
|
|
@@ -588,10 +590,10 @@ export function registerHooks(
|
|
|
588
590
|
if (pendingQuestion) {
|
|
589
591
|
const answer = details.response?.answers?.[currentPendingGate];
|
|
590
592
|
if (isDepthConfirmationAnswer(answer?.selected, pendingQuestion.options)) {
|
|
591
|
-
markApprovalGateVerified(currentPendingGate);
|
|
593
|
+
markApprovalGateVerified(currentPendingGate, basePath);
|
|
592
594
|
const milestoneIdFromGate = extractDepthVerificationMilestoneId(currentPendingGate);
|
|
593
|
-
if (milestoneIdFromGate) markDepthVerified(milestoneIdFromGate);
|
|
594
|
-
clearPendingGate();
|
|
595
|
+
if (milestoneIdFromGate) markDepthVerified(milestoneIdFromGate, basePath);
|
|
596
|
+
clearPendingGate(basePath);
|
|
595
597
|
}
|
|
596
598
|
}
|
|
597
599
|
}
|
|
@@ -607,9 +609,9 @@ export function registerHooks(
|
|
|
607
609
|
const inferredMilestoneId = extractDepthVerificationMilestoneId(question.id) ?? milestoneId;
|
|
608
610
|
if (isDepthConfirmationAnswer(answer?.selected, question.options)) {
|
|
609
611
|
if (currentPendingGate && question.id !== currentPendingGate) break;
|
|
610
|
-
markApprovalGateVerified(question.id);
|
|
611
|
-
markDepthVerified(inferredMilestoneId);
|
|
612
|
-
clearPendingGate();
|
|
612
|
+
markApprovalGateVerified(question.id, basePath);
|
|
613
|
+
markDepthVerified(inferredMilestoneId, basePath);
|
|
614
|
+
clearPendingGate(basePath);
|
|
613
615
|
}
|
|
614
616
|
break;
|
|
615
617
|
}
|
|
@@ -617,8 +619,6 @@ export function registerHooks(
|
|
|
617
619
|
|
|
618
620
|
if (!milestoneId && !queueActive) return;
|
|
619
621
|
if (!milestoneId) return;
|
|
620
|
-
|
|
621
|
-
const basePath = process.cwd();
|
|
622
622
|
const milestoneDir = resolveMilestonePath(basePath, milestoneId);
|
|
623
623
|
if (!milestoneDir) return;
|
|
624
624
|
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// GSD-2 write-gate bootstrap — regression test for required basePath (commit A3)
|
|
2
|
+
//
|
|
3
|
+
// Verifies that persistWriteGateSnapshot / loadWriteGateSnapshot are pinned to
|
|
4
|
+
// the basePath argument and do not silently fall back to process.cwd(). The
|
|
5
|
+
// underlying bug: both functions defaulted `basePath = process.cwd()`, so a
|
|
6
|
+
// persist in cwd-A followed by a chdir to cwd-B and a load (which also
|
|
7
|
+
// defaulted to process.cwd(), now cwd-B) missed the persisted file entirely —
|
|
8
|
+
// the depth-verification state became invisible across cwd boundaries.
|
|
9
|
+
|
|
10
|
+
import { test, describe, before, after } from "node:test";
|
|
11
|
+
import assert from "node:assert/strict";
|
|
12
|
+
import { mkdtempSync, rmSync, existsSync } from "node:fs";
|
|
13
|
+
import { tmpdir } from "node:os";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
markDepthVerified,
|
|
18
|
+
loadWriteGateSnapshot,
|
|
19
|
+
clearDiscussionFlowState,
|
|
20
|
+
} from "../write-gate.js";
|
|
21
|
+
|
|
22
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
function makeTempDir(): string {
|
|
25
|
+
return mkdtempSync(join(tmpdir(), "wg-basepath-test-"));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Save and restore process.cwd() across tests to avoid cross-test pollution.
|
|
29
|
+
let originalCwd: string;
|
|
30
|
+
before(() => {
|
|
31
|
+
originalCwd = process.cwd();
|
|
32
|
+
});
|
|
33
|
+
after(() => {
|
|
34
|
+
if (process.cwd() !== originalCwd) {
|
|
35
|
+
process.chdir(originalCwd);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// ─── Scenario: persist with basePath=A, chdir, load with basePath=A ─────────
|
|
40
|
+
//
|
|
41
|
+
// This is the exact failure mode from the bug: persist used process.cwd() and
|
|
42
|
+
// load used process.cwd(), and they resolved to different directories after a
|
|
43
|
+
// chdir. With the fix, both calls receive an explicit basePath so cwd changes
|
|
44
|
+
// have no effect.
|
|
45
|
+
|
|
46
|
+
describe("write-gate basePath regression", () => {
|
|
47
|
+
let baseDirA: string;
|
|
48
|
+
let baseDirB: string;
|
|
49
|
+
|
|
50
|
+
before(() => {
|
|
51
|
+
baseDirA = makeTempDir();
|
|
52
|
+
baseDirB = makeTempDir();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
after(() => {
|
|
56
|
+
// Restore cwd before cleanup to avoid issues on Windows.
|
|
57
|
+
process.chdir(originalCwd);
|
|
58
|
+
rmSync(baseDirA, { recursive: true, force: true });
|
|
59
|
+
rmSync(baseDirB, { recursive: true, force: true });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("snapshot persisted to basePath=A is readable after chdir to basePath=B", (t) => {
|
|
63
|
+
// Arrange: enable persistence (the default when env var is not set to "0"/"false").
|
|
64
|
+
const prev = process.env.GSD_PERSIST_WRITE_GATE_STATE;
|
|
65
|
+
t.after(() => {
|
|
66
|
+
if (prev === undefined) {
|
|
67
|
+
delete process.env.GSD_PERSIST_WRITE_GATE_STATE;
|
|
68
|
+
} else {
|
|
69
|
+
process.env.GSD_PERSIST_WRITE_GATE_STATE = prev;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
process.env.GSD_PERSIST_WRITE_GATE_STATE = "1";
|
|
73
|
+
|
|
74
|
+
// Reset state and clear any stale snapshot files from both dirs.
|
|
75
|
+
clearDiscussionFlowState(baseDirA);
|
|
76
|
+
clearDiscussionFlowState(baseDirB);
|
|
77
|
+
|
|
78
|
+
// Act: persist a milestone as depth-verified into baseDirA.
|
|
79
|
+
markDepthVerified("M001", baseDirA);
|
|
80
|
+
|
|
81
|
+
// Confirm the snapshot file was written under baseDirA.
|
|
82
|
+
const snapshotPath = join(baseDirA, ".gsd", "runtime", "write-gate-state.json");
|
|
83
|
+
assert.ok(existsSync(snapshotPath), "snapshot file should exist under baseDirA");
|
|
84
|
+
|
|
85
|
+
// Simulate what happens when cwd changes to a different project root.
|
|
86
|
+
process.chdir(baseDirB);
|
|
87
|
+
assert.notEqual(process.cwd(), baseDirA, "cwd should differ from baseDirA after chdir");
|
|
88
|
+
|
|
89
|
+
// Load snapshot using the explicit baseDirA — must see the persisted state.
|
|
90
|
+
const snapshot = loadWriteGateSnapshot(baseDirA);
|
|
91
|
+
assert.ok(
|
|
92
|
+
snapshot.verifiedDepthMilestones.includes("M001"),
|
|
93
|
+
"loadWriteGateSnapshot(baseDirA) must return the persisted milestone despite cwd being baseDirB",
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// Loading with baseDirB must NOT see the state from baseDirA.
|
|
97
|
+
const snapshotB = loadWriteGateSnapshot(baseDirB);
|
|
98
|
+
assert.ok(
|
|
99
|
+
!snapshotB.verifiedDepthMilestones.includes("M001"),
|
|
100
|
+
"loadWriteGateSnapshot(baseDirB) must not bleed state from baseDirA",
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
});
|