@opengsd/gsd-pi 1.2.0-dev.4c756166 → 1.2.0-dev.5457a158
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/dist/cli-style.d.ts +17 -0
- package/dist/cli-style.js +28 -0
- package/dist/cli.js +1 -1
- package/dist/headless-events.d.ts +4 -2
- package/dist/headless-events.js +7 -29
- package/dist/models-resolver.d.ts +3 -13
- package/dist/models-resolver.js +3 -22
- package/dist/resource-loader.js +2 -14
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/bg-shell/utilities.js +5 -2
- package/dist/resources/extensions/claude-code-cli/models.js +9 -0
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +35 -4
- package/dist/resources/extensions/gsd/auto/orchestrator.js +33 -4
- package/dist/resources/extensions/gsd/auto/phases.js +6 -1
- package/dist/resources/extensions/gsd/auto-post-unit.js +19 -8
- package/dist/resources/extensions/gsd/auto-prompts.js +3 -0
- package/dist/resources/extensions/gsd/auto-start.js +12 -14
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +18 -0
- package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +7 -16
- package/dist/resources/extensions/gsd/auto-worktree-repair.js +10 -2
- package/dist/resources/extensions/gsd/auto-worktree.js +35 -352
- package/dist/resources/extensions/gsd/auto.js +8 -20
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +3 -2
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +9 -6
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +86 -6
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +30 -4
- package/dist/resources/extensions/gsd/branch-patterns.js +2 -0
- package/dist/resources/extensions/gsd/captures.js +5 -15
- package/dist/resources/extensions/gsd/closeout-recovery.js +3 -2
- package/dist/resources/extensions/gsd/commands/catalog.js +6 -62
- package/dist/resources/extensions/gsd/crash-recovery.js +4 -12
- package/dist/resources/extensions/gsd/db/engine.js +755 -0
- package/dist/resources/extensions/gsd/db/queries.js +372 -0
- package/dist/resources/extensions/gsd/db/sql-constants.js +11 -0
- package/dist/resources/extensions/gsd/db/writers/cascades.js +194 -0
- package/dist/resources/extensions/gsd/db/writers/import-restore.js +182 -0
- package/dist/resources/extensions/gsd/db/writers/memory.js +149 -0
- package/dist/resources/extensions/gsd/db/writers/reconcile.js +458 -0
- package/dist/resources/extensions/gsd/db/writers/status.js +70 -0
- package/dist/resources/extensions/gsd/doctor-environment.js +5 -11
- package/dist/resources/extensions/gsd/doctor-format.js +9 -6
- package/dist/resources/extensions/gsd/doctor-git-checks.js +4 -3
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +21 -16
- package/dist/resources/extensions/gsd/error-classifier.js +9 -0
- package/dist/resources/extensions/gsd/git-service.js +1 -0
- package/dist/resources/extensions/gsd/gitignore.js +3 -0
- package/dist/resources/extensions/gsd/gsd-db.js +171 -2048
- package/dist/resources/extensions/gsd/guidance.js +98 -0
- package/dist/resources/extensions/gsd/guided-flow.js +51 -5
- package/dist/resources/extensions/gsd/mcp-tool-name.js +5 -13
- package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +1 -1
- package/dist/resources/extensions/gsd/migrate/safety.js +20 -9
- package/dist/resources/extensions/gsd/migration-auto-check.js +24 -3
- package/dist/resources/extensions/gsd/model-cost-table.js +1 -0
- package/dist/resources/extensions/gsd/model-router.js +3 -0
- package/dist/resources/extensions/gsd/notification-store.js +11 -4
- package/dist/resources/extensions/gsd/parallel-merge.js +14 -11
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +11 -7
- package/dist/resources/extensions/gsd/paths.js +37 -24
- package/dist/resources/extensions/gsd/pre-execution-checks.js +91 -3
- package/dist/resources/extensions/gsd/preferences-models.js +12 -46
- package/dist/resources/extensions/gsd/preferences.js +14 -0
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
- package/dist/resources/extensions/gsd/provider-error-guidance.js +1 -5
- package/dist/resources/extensions/gsd/provider-switch-observer.js +1 -1
- package/dist/resources/extensions/gsd/publication.js +87 -0
- package/dist/resources/extensions/gsd/recovery-classification.js +41 -87
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +37 -4
- package/dist/resources/extensions/gsd/safety/evidence-cross-ref.js +7 -2
- package/dist/resources/extensions/gsd/safety/file-change-validator.js +10 -0
- package/dist/resources/extensions/gsd/state-transition-matrix.js +38 -0
- package/dist/resources/extensions/gsd/state.js +1 -20
- package/dist/resources/extensions/gsd/status-guards.js +56 -8
- package/dist/resources/extensions/gsd/stop-notice.js +57 -0
- package/dist/resources/extensions/gsd/tool-surface-readiness.js +56 -0
- package/dist/resources/extensions/gsd/tools/complete-slice.js +24 -43
- package/dist/resources/extensions/gsd/tools/exec-tool.js +5 -8
- package/dist/resources/extensions/gsd/tools/plan-slice.js +12 -6
- package/dist/resources/extensions/gsd/tools/reopen-milestone.js +11 -29
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +14 -33
- package/dist/resources/extensions/gsd/tools/skip-slice.js +18 -36
- package/dist/resources/extensions/gsd/undo.js +8 -7
- package/dist/resources/extensions/gsd/unit-closeout.js +138 -0
- package/dist/resources/extensions/gsd/unit-context-composer.js +9 -1
- package/dist/resources/extensions/gsd/unit-context-manifest.js +4 -27
- package/dist/resources/extensions/gsd/unit-registry.js +350 -0
- package/dist/resources/extensions/gsd/unit-tool-contracts.js +9 -182
- package/dist/resources/extensions/gsd/workflow-tool-surface.js +1 -1
- package/dist/resources/extensions/gsd/worktree-git-recovery.js +293 -0
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +9 -1
- package/dist/resources/extensions/gsd/worktree-manager.js +45 -28
- package/dist/resources/extensions/gsd/worktree-placement.js +59 -0
- package/dist/resources/extensions/gsd/worktree-reentry.js +12 -8
- package/dist/resources/extensions/gsd/worktree-root.js +28 -6
- package/dist/resources/extensions/gsd/worktree-safety.js +8 -5
- package/dist/resources/extensions/gsd/worktree-session-state.js +12 -11
- package/dist/resources/skills/gsd-browser/SKILL.md +1 -1
- 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 +9 -9
- 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/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/mcp-connections/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/shutdown/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 +9 -9
- package/dist/web/standalone/.next/server/chunks/5124.js +1 -1
- package/dist/web/standalone/.next/server/chunks/{5047.js → 5942.js} +2 -2
- package/dist/web/standalone/.next/server/chunks/8357.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/worktree-cli.js +3 -6
- package/dist/worktree-status-banner.js +7 -11
- package/package.json +1 -1
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/dist/workflow.d.ts +4 -0
- package/packages/contracts/dist/workflow.d.ts.map +1 -1
- package/packages/contracts/dist/workflow.js.map +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/dist/cli.js +6 -3
- package/packages/mcp-server/dist/cli.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +8 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +46 -21
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +294 -239
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +260 -256
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/capability-patches.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/capability-patches.js +3 -1
- package/packages/pi-coding-agent/dist/core/capability-patches.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/extensions/bg-shell/utilities.ts +5 -2
- package/src/resources/extensions/claude-code-cli/models.ts +9 -0
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +37 -2
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +28 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -1
- package/src/resources/extensions/gsd/auto/orchestrator.ts +39 -5
- package/src/resources/extensions/gsd/auto/phases.ts +10 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +25 -7
- package/src/resources/extensions/gsd/auto-prompts.ts +3 -0
- package/src/resources/extensions/gsd/auto-start.ts +12 -15
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +19 -0
- package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +10 -17
- package/src/resources/extensions/gsd/auto-worktree-repair.ts +13 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +41 -364
- package/src/resources/extensions/gsd/auto.ts +20 -24
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +3 -5
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +10 -6
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +87 -6
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +29 -3
- package/src/resources/extensions/gsd/branch-patterns.ts +3 -0
- package/src/resources/extensions/gsd/captures.ts +5 -16
- package/src/resources/extensions/gsd/closeout-recovery.ts +2 -1
- package/src/resources/extensions/gsd/commands/catalog.ts +6 -68
- package/src/resources/extensions/gsd/crash-recovery.ts +3 -9
- package/src/resources/extensions/gsd/db/engine.ts +809 -0
- package/src/resources/extensions/gsd/db/queries.ts +453 -0
- package/src/resources/extensions/gsd/db/sql-constants.ts +12 -0
- package/src/resources/extensions/gsd/db/writers/cascades.ts +237 -0
- package/src/resources/extensions/gsd/db/writers/import-restore.ts +310 -0
- package/src/resources/extensions/gsd/db/writers/memory.ts +220 -0
- package/src/resources/extensions/gsd/db/writers/reconcile.ts +500 -0
- package/src/resources/extensions/gsd/db/writers/status.ts +88 -0
- package/src/resources/extensions/gsd/doctor-environment.ts +5 -13
- package/src/resources/extensions/gsd/doctor-format.ts +12 -7
- package/src/resources/extensions/gsd/doctor-git-checks.ts +3 -3
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +22 -17
- package/src/resources/extensions/gsd/error-classifier.ts +11 -0
- package/src/resources/extensions/gsd/git-service.ts +1 -0
- package/src/resources/extensions/gsd/gitignore.ts +3 -0
- package/src/resources/extensions/gsd/gsd-db.ts +173 -2373
- package/src/resources/extensions/gsd/guidance.ts +139 -0
- package/src/resources/extensions/gsd/guided-flow.ts +50 -5
- package/src/resources/extensions/gsd/mcp-tool-name.ts +6 -11
- package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +1 -1
- package/src/resources/extensions/gsd/migrate/safety.ts +18 -7
- package/src/resources/extensions/gsd/migration-auto-check.ts +28 -3
- package/src/resources/extensions/gsd/model-cost-table.ts +1 -0
- package/src/resources/extensions/gsd/model-router.ts +3 -0
- package/src/resources/extensions/gsd/notification-store.ts +26 -3
- package/src/resources/extensions/gsd/parallel-merge.ts +12 -9
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +10 -7
- package/src/resources/extensions/gsd/paths.ts +42 -22
- package/src/resources/extensions/gsd/pre-execution-checks.ts +109 -3
- package/src/resources/extensions/gsd/preferences-models.ts +10 -46
- package/src/resources/extensions/gsd/preferences.ts +18 -0
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
- package/src/resources/extensions/gsd/provider-error-guidance.ts +4 -9
- package/src/resources/extensions/gsd/provider-switch-observer.ts +1 -1
- package/src/resources/extensions/gsd/publication.ts +122 -0
- package/src/resources/extensions/gsd/recovery-classification.ts +47 -88
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +36 -4
- package/src/resources/extensions/gsd/safety/evidence-cross-ref.ts +7 -2
- package/src/resources/extensions/gsd/safety/file-change-validator.ts +14 -0
- package/src/resources/extensions/gsd/state-transition-matrix.ts +42 -0
- package/src/resources/extensions/gsd/state.ts +4 -21
- package/src/resources/extensions/gsd/status-guards.ts +59 -8
- package/src/resources/extensions/gsd/stop-notice.ts +75 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +123 -0
- package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/auto-post-unit-evidence-crossref-4909.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-worktree-repair.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/checkout-branch-stash-guard.test.ts +66 -1
- package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +8 -7
- package/src/resources/extensions/gsd/tests/evidence-xref-gsd-exec.test.ts +157 -0
- package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +33 -1
- package/src/resources/extensions/gsd/tests/guidance.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +51 -4
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +54 -1
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +85 -1
- package/src/resources/extensions/gsd/tests/notification-store.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +193 -1
- package/src/resources/extensions/gsd/tests/provider-error-guidance.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/publication.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/recovery-classification-illegal-transition.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +248 -1
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/session-switch-clears-pending-autostart.test.ts +108 -0
- package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +43 -6
- package/src/resources/extensions/gsd/tests/state-transition-matrix.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/status-guards.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/stop-notice.test.ts +70 -0
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/tool-surface-readiness.test.ts +155 -0
- package/src/resources/extensions/gsd/tests/unit-closeout.test.ts +209 -0
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +23 -2
- package/src/resources/extensions/gsd/tests/unit-registry.test.ts +163 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +41 -4
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +22 -1
- package/src/resources/extensions/gsd/tests/worktree-placement.test.ts +113 -0
- package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +12 -6
- package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +42 -0
- package/src/resources/extensions/gsd/tool-surface-readiness.ts +76 -0
- package/src/resources/extensions/gsd/tools/complete-slice.ts +23 -58
- package/src/resources/extensions/gsd/tools/exec-tool.ts +5 -8
- package/src/resources/extensions/gsd/tools/plan-slice.ts +12 -6
- package/src/resources/extensions/gsd/tools/reopen-milestone.ts +11 -38
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +14 -42
- package/src/resources/extensions/gsd/tools/skip-slice.ts +18 -44
- package/src/resources/extensions/gsd/undo.ts +9 -8
- package/src/resources/extensions/gsd/unit-closeout.ts +201 -0
- package/src/resources/extensions/gsd/unit-context-composer.ts +12 -1
- package/src/resources/extensions/gsd/unit-context-manifest.ts +4 -28
- package/src/resources/extensions/gsd/unit-registry.ts +425 -0
- package/src/resources/extensions/gsd/unit-tool-contracts.ts +27 -192
- package/src/resources/extensions/gsd/workflow-tool-surface.ts +4 -1
- package/src/resources/extensions/gsd/worktree-git-recovery.ts +314 -0
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +10 -1
- package/src/resources/extensions/gsd/worktree-manager.ts +47 -28
- package/src/resources/extensions/gsd/worktree-placement.ts +63 -0
- package/src/resources/extensions/gsd/worktree-reentry.ts +10 -7
- package/src/resources/extensions/gsd/worktree-root.ts +29 -6
- package/src/resources/extensions/gsd/worktree-safety.ts +8 -5
- package/src/resources/extensions/gsd/worktree-session-state.ts +11 -11
- package/src/resources/skills/gsd-browser/SKILL.md +1 -1
- /package/dist/web/standalone/.next/static/{DUFWcMFRH3iXh7d2fbrOF → 2p9Rv9pQflAxCBbGVI2vb}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{DUFWcMFRH3iXh7d2fbrOF → 2p9Rv9pQflAxCBbGVI2vb}/_ssgManifest.js +0 -0
|
@@ -99,6 +99,44 @@ export const STATE_TRANSITION_MATRIX = [
|
|
|
99
99
|
export function findTransition(from, event) {
|
|
100
100
|
return STATE_TRANSITION_MATRIX.find((entry) => (entry.from === from || entry.from === "*") && entry.event === event);
|
|
101
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Edge-keyed legality check for the Phase Transition Invariant (ADR-030).
|
|
104
|
+
* `advance()` derives the next Phase and asserts the (from → to) edge here.
|
|
105
|
+
*
|
|
106
|
+
* The matrix is an assertion, not a decision-maker — `deriveState` already
|
|
107
|
+
* chose the Phase. A self-edge is trivially legal (no transition to assert). An
|
|
108
|
+
* edge is legal when some matrix entry permits it, honoring the `*` wildcard
|
|
109
|
+
* rows (e.g. any → blocked via manual-block, any → executing via
|
|
110
|
+
* retryable-failure).
|
|
111
|
+
*
|
|
112
|
+
* Note: the matrix is currently a sparse hardening spec, not a complete
|
|
113
|
+
* legal-edge graph, so `advance()` consumes this in advisory mode (telemetry
|
|
114
|
+
* only). It must be expanded to cover every edge `deriveState` emits before
|
|
115
|
+
* enforcement flips on.
|
|
116
|
+
*/
|
|
117
|
+
export function isLegalEdge(from, to) {
|
|
118
|
+
if (from === to)
|
|
119
|
+
return true;
|
|
120
|
+
return STATE_TRANSITION_MATRIX.some((entry) => (entry.from === from || entry.from === "*") && entry.to === to);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Thrown when an illegal derived Phase edge survives reconciliation. Carries
|
|
124
|
+
* both endpoints so Recovery Classification can report them. Recognized by
|
|
125
|
+
* class in `classifyFailure` and mapped to the `illegal-transition` kind.
|
|
126
|
+
*/
|
|
127
|
+
export class IllegalPhaseTransitionError extends Error {
|
|
128
|
+
// Explicit fields, not constructor parameter properties — strip-types
|
|
129
|
+
// consumers (workspace-index subprocess, integration tests) reject the
|
|
130
|
+
// parameter-property syntax with ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX.
|
|
131
|
+
from;
|
|
132
|
+
to;
|
|
133
|
+
constructor(from, to) {
|
|
134
|
+
super(`Illegal phase transition ${from} -> ${to} survived reconciliation`);
|
|
135
|
+
this.name = "IllegalPhaseTransitionError";
|
|
136
|
+
this.from = from;
|
|
137
|
+
this.to = to;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
102
140
|
export function validateTransitionMatrix(requiredEvents) {
|
|
103
141
|
const seen = new Set();
|
|
104
142
|
const duplicateKeys = [];
|
|
@@ -22,26 +22,7 @@ import { isDbAvailable, getAllMilestones, getMilestone, getMilestoneSlices, getS
|
|
|
22
22
|
import { wasWorkflowDatabaseOpenAttempted } from './db-workspace.js';
|
|
23
23
|
import { formatCompletePhaseNextAction, countUnmappedActiveRequirements } from './requirements-backlog.js';
|
|
24
24
|
import { classifyMilestoneReadiness, readinessNeedsDiscussion, } from './milestone-readiness.js';
|
|
25
|
-
|
|
26
|
-
return [
|
|
27
|
-
`Milestone ${milestoneId} is blocked because milestone validation returned needs-attention.`,
|
|
28
|
-
`Fix options:`,
|
|
29
|
-
`1. Review the validation details: \`/gsd status\``,
|
|
30
|
-
`2. If you fixed the missing evidence or issue, re-run milestone validation: \`/gsd validate-milestone\``,
|
|
31
|
-
`3. If the finding is acceptable, override it: \`/gsd verdict pass --rationale "why this is okay"\``,
|
|
32
|
-
`4. If this should wait, defer it explicitly: \`/gsd park ${milestoneId}\``,
|
|
33
|
-
`After validation or override passes, run \`/gsd auto\` to complete and merge the milestone.`,
|
|
34
|
-
].join("\n");
|
|
35
|
-
}
|
|
36
|
-
function formatNeedsRemediationBlocker(milestoneId) {
|
|
37
|
-
return [
|
|
38
|
-
`Milestone ${milestoneId} is blocked because milestone validation returned needs-remediation, but all slices are complete.`,
|
|
39
|
-
`Fix options:`,
|
|
40
|
-
`1. Run \`/gsd dispatch reassess\` to add remediation slices, then run \`/gsd auto\``,
|
|
41
|
-
`2. If the finding is acceptable, override it: \`/gsd verdict pass --rationale "why this is okay"\``,
|
|
42
|
-
`3. If this should wait, defer it explicitly: \`/gsd park ${milestoneId}\``,
|
|
43
|
-
].join("\n");
|
|
44
|
-
}
|
|
25
|
+
import { needsAttentionBlockerGuidance as formatNeedsAttentionBlocker, needsRemediationBlockerGuidance as formatNeedsRemediationBlocker, } from './guidance.js';
|
|
45
26
|
/**
|
|
46
27
|
* A "ghost" milestone directory contains only META.json (and no substantive
|
|
47
28
|
* files like CONTEXT, CONTEXT-DRAFT, ROADMAP, or SUMMARY). These appear when
|
|
@@ -1,16 +1,64 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Status predicates for GSD state-machine
|
|
2
|
+
* Status predicates and the canonical status vocabulary for GSD state-machine
|
|
3
|
+
* guards (ADR-030).
|
|
3
4
|
*
|
|
4
|
-
* The DB
|
|
5
|
-
*
|
|
6
|
-
* "closed" (legacy/imported), and
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* The DB column is free-form `string` so legacy/imported rows still load. Three
|
|
6
|
+
* raw values besides canonical "complete"/"skipped" indicate "closed": "done"
|
|
7
|
+
* (legacy alias), "closed" (legacy/imported), and "skipped" (user-directed skip
|
|
8
|
+
* via rethink or backtrack). `RAW_CLOSED_STATUSES` is the single source for both
|
|
9
|
+
* `isClosedStatus()` and the SQL terminal-status fragment
|
|
10
|
+
* (`db/sql-constants.ts` derives `TERMINAL_STATUS_SQL` from it), replacing the
|
|
11
|
+
* prior independent definitions.
|
|
12
|
+
*
|
|
13
|
+
* `toStatus()` is the single seam where a free-form string becomes the canonical
|
|
14
|
+
* `Status` vocabulary; the Status Transition Core writes canonical, so the store
|
|
15
|
+
* converges over time without a forced migration.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Canonical, normalized entity-status vocabulary across milestones, slices, and
|
|
19
|
+
* tasks — the single source for both the `Status` type and the runtime
|
|
20
|
+
* membership set. The in-memory domain speaks `Status`; the DB column stays
|
|
21
|
+
* free-form.
|
|
22
|
+
*/
|
|
23
|
+
export const CANONICAL_STATUSES = [
|
|
24
|
+
"pending", "queued", "active", "parked", "in_progress", "blocked", "complete", "skipped", "deferred",
|
|
25
|
+
];
|
|
26
|
+
const CANONICAL_STATUS_SET = new Set(CANONICAL_STATUSES);
|
|
27
|
+
/**
|
|
28
|
+
* Raw status values that mean a unit is closed — the single source of truth.
|
|
29
|
+
* Includes legacy/imported aliases ("done", "closed") alongside canonical
|
|
30
|
+
* "complete"/"skipped" because the DB column is free-form and older rows /
|
|
31
|
+
* imports still carry them. Order matters: `TERMINAL_STATUS_SQL` is derived
|
|
32
|
+
* from this array verbatim.
|
|
33
|
+
*/
|
|
34
|
+
export const RAW_CLOSED_STATUSES = ["complete", "done", "skipped", "closed"];
|
|
35
|
+
const RAW_CLOSED_SET = new Set(RAW_CLOSED_STATUSES);
|
|
36
|
+
/** Free-form aliases mapped to their canonical Status on read. */
|
|
37
|
+
const ALIAS_TO_CANONICAL = {
|
|
38
|
+
done: "complete",
|
|
39
|
+
closed: "complete",
|
|
40
|
+
planned: "pending",
|
|
41
|
+
"in-progress": "in_progress",
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Normalize a free-form DB status string into the canonical `Status`
|
|
45
|
+
* vocabulary. Maps known aliases (done/closed → complete, planned → pending,
|
|
46
|
+
* in-progress → in_progress). An unrecognized/legacy value is **quarantined** —
|
|
47
|
+
* preserved verbatim rather than silently remapped to a wrong canonical state —
|
|
48
|
+
* so reads never fail and reconciliation/telemetry can surface it.
|
|
10
49
|
*/
|
|
50
|
+
export function toStatus(raw) {
|
|
51
|
+
const value = raw.trim();
|
|
52
|
+
if (CANONICAL_STATUS_SET.has(value))
|
|
53
|
+
return value;
|
|
54
|
+
const alias = ALIAS_TO_CANONICAL[value];
|
|
55
|
+
if (alias)
|
|
56
|
+
return alias;
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
11
59
|
/** Returns true when a milestone, slice, or task status indicates closure. */
|
|
12
60
|
export function isClosedStatus(status) {
|
|
13
|
-
return status
|
|
61
|
+
return RAW_CLOSED_SET.has(status);
|
|
14
62
|
}
|
|
15
63
|
/** Returns true when a slice status indicates it was deferred by a decision. */
|
|
16
64
|
export function isDeferredStatus(status) {
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Stop Notice module — single owner of the auto/step-mode
|
|
3
|
+
// stop/pause notice vocabulary. Both sides of the wire live here: the
|
|
4
|
+
// formatters that produce the canonical prefixes (used by stopAuto/pauseAuto)
|
|
5
|
+
// and the classifiers that recognize them (used by the headless host to pick
|
|
6
|
+
// exit codes). Wording changes in this file keep emitter and detector in
|
|
7
|
+
// lockstep; round-trip tests enforce it.
|
|
8
|
+
/** A reason string of the form "Blocked: …" marks a blocked stop. */
|
|
9
|
+
export function isBlockedStopReason(reason) {
|
|
10
|
+
return /^Blocked:\s*/i.test(reason ?? "");
|
|
11
|
+
}
|
|
12
|
+
/** Strip the "Blocked: " marker for display. */
|
|
13
|
+
export function stopNoticeDisplayReason(reason) {
|
|
14
|
+
return (reason ?? "").replace(/^Blocked:\s*/i, "").trim();
|
|
15
|
+
}
|
|
16
|
+
export function stopNoticeKind(reason) {
|
|
17
|
+
return isBlockedStopReason(reason) ? "blocked" : "stopped";
|
|
18
|
+
}
|
|
19
|
+
/** Canonical stop-notice prefix: "Auto-mode blocked — reason" / "Auto-mode stopped". */
|
|
20
|
+
export function formatStopNoticePrefix(reason) {
|
|
21
|
+
const displayReason = stopNoticeDisplayReason(reason);
|
|
22
|
+
const prefix = stopNoticeKind(reason) === "blocked" ? "Auto-mode blocked" : "Auto-mode stopped";
|
|
23
|
+
return displayReason ? `${prefix} — ${displayReason}` : prefix;
|
|
24
|
+
}
|
|
25
|
+
// ─── Classification (headless host side) ────────────────────────────────
|
|
26
|
+
// The canonical lowercase prefixes the headless event loop recognizes in
|
|
27
|
+
// notify messages. Emitters above and ad-hoc emitters elsewhere must start
|
|
28
|
+
// their terminal notices with one of these.
|
|
29
|
+
export const PAUSED_NOTICE_PREFIXES = ["auto-mode paused", "step-mode paused"];
|
|
30
|
+
export const TERMINAL_NOTICE_PREFIXES = [
|
|
31
|
+
"auto-mode stopped",
|
|
32
|
+
"step-mode stopped",
|
|
33
|
+
"auto-mode complete",
|
|
34
|
+
"no active milestone",
|
|
35
|
+
"auto-mode idle",
|
|
36
|
+
];
|
|
37
|
+
/** Manual-resolution notices emitted before auto-mode can formally pause/stop. */
|
|
38
|
+
export function isManualResolutionNotice(message) {
|
|
39
|
+
return (message.includes("resolve manually and re-run /gsd auto") ||
|
|
40
|
+
message.includes("resolve conflicts manually and run /gsd auto to resume") ||
|
|
41
|
+
message.includes("resolve and run /gsd auto to resume"));
|
|
42
|
+
}
|
|
43
|
+
export function isPauseNotice(message) {
|
|
44
|
+
return PAUSED_NOTICE_PREFIXES.some((prefix) => message.startsWith(prefix));
|
|
45
|
+
}
|
|
46
|
+
export function isTerminalNotice(message) {
|
|
47
|
+
return TERMINAL_NOTICE_PREFIXES.some((prefix) => message.startsWith(prefix));
|
|
48
|
+
}
|
|
49
|
+
/** Pauses that do not require operator intervention in headless mode. */
|
|
50
|
+
export function isNonBlockingPauseNotice(message) {
|
|
51
|
+
return message.includes("idempotent advance: unit already active");
|
|
52
|
+
}
|
|
53
|
+
export function isBlockedNoticeMessage(message) {
|
|
54
|
+
return (message.includes("blocked:") ||
|
|
55
|
+
(isPauseNotice(message) && !isNonBlockingPauseNotice(message)) ||
|
|
56
|
+
isManualResolutionNotice(message));
|
|
57
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Tool Contract module's runtime face — verify the live SDK tool surface covers a Unit's required workflow tools.
|
|
3
|
+
import { mcpToolMatchesBaseName } from "./mcp-tool-name.js";
|
|
4
|
+
import { getRequiredWorkflowToolsForUnit } from "./unit-tool-contracts.js";
|
|
5
|
+
import { isWorkflowToolSurfaceName } from "./workflow-tool-surface.js";
|
|
6
|
+
/**
|
|
7
|
+
* Stable phrase recognized as transient by auto-tool-tracking's
|
|
8
|
+
* isToolUnavailableError and error-classifier's transient buckets,
|
|
9
|
+
* which build their matchers from this constant.
|
|
10
|
+
*/
|
|
11
|
+
export const TOOL_SURFACE_NOT_READY = "workflow tool surface not ready";
|
|
12
|
+
/** MCP server statuses that will not self-heal within the session. */
|
|
13
|
+
const TERMINAL_MCP_SERVER_STATUSES = new Set(["failed", "needs-auth", "disabled"]);
|
|
14
|
+
/**
|
|
15
|
+
* Verify the live tool surface observed at SDK session init covers the Unit's
|
|
16
|
+
* required workflow tools. Complements the static pre-dispatch gate
|
|
17
|
+
* (getWorkflowTransportSupportError), which only proves the MCP launch config
|
|
18
|
+
* is discoverable — the workflow server connects asynchronously after session
|
|
19
|
+
* start, so the static gate cannot see whether the tools actually registered.
|
|
20
|
+
*
|
|
21
|
+
* Returns a transient, recovery-classifiable error (kind tool-unavailable →
|
|
22
|
+
* retry) when the workflow server failed or has not yet registered a required
|
|
23
|
+
* tool, so dispatch aborts before the first model turn instead of letting the
|
|
24
|
+
* Unit improvise around "No such tool available". Returns null when no
|
|
25
|
+
* workflow server is part of this session (native tool path), when the Unit
|
|
26
|
+
* requires no workflow tools, or when the surface is ready.
|
|
27
|
+
*/
|
|
28
|
+
export function getToolSurfaceReadinessError(input) {
|
|
29
|
+
const { unitType, workflowServerName, observation } = input;
|
|
30
|
+
if (!unitType || !workflowServerName)
|
|
31
|
+
return null;
|
|
32
|
+
const required = getRequiredWorkflowToolsForUnit(unitType).filter(isWorkflowToolSurfaceName);
|
|
33
|
+
if (required.length === 0)
|
|
34
|
+
return null;
|
|
35
|
+
const server = observation.mcpServers.find((entry) => entry.name === workflowServerName);
|
|
36
|
+
if (!server) {
|
|
37
|
+
return `${TOOL_SURFACE_NOT_READY} for ${unitType}: MCP server "${workflowServerName}" is absent from the init surface (not yet connected): ${required.join(", ")}`;
|
|
38
|
+
}
|
|
39
|
+
// The SDK does not wait for MCP servers before init — a still-connecting
|
|
40
|
+
// server reports "pending" there routinely, then registers within seconds,
|
|
41
|
+
// usually well before the Unit's first workflow tool call. Aborting on
|
|
42
|
+
// "pending" would fail the common healthy session, so it passes through;
|
|
43
|
+
// a genuine miss after pass-through still surfaces in-session as
|
|
44
|
+
// "No such tool available" and classifies tool-unavailable → bounded retry.
|
|
45
|
+
// Only statuses that cannot self-heal abort here.
|
|
46
|
+
if (server.status !== "connected" && !TERMINAL_MCP_SERVER_STATUSES.has(server.status)) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const missing = required.filter((tool) => !observation.tools.some((name) => name === tool || mcpToolMatchesBaseName(name, tool)));
|
|
50
|
+
if (missing.length === 0)
|
|
51
|
+
return null;
|
|
52
|
+
const serverDetail = server.status === "connected"
|
|
53
|
+
? `MCP server "${workflowServerName}" is connected but has not registered`
|
|
54
|
+
: `MCP server "${workflowServerName}" status is "${server.status}" and it has not registered`;
|
|
55
|
+
return `${TOOL_SURFACE_NOT_READY} for ${unitType}: ${serverDetail}: ${missing.join(", ")}`;
|
|
56
|
+
}
|
|
@@ -11,8 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { existsSync, readFileSync } from "node:fs";
|
|
13
13
|
import { join } from "node:path";
|
|
14
|
-
import {
|
|
15
|
-
import { transaction, insertMilestone, insertSlice, getSlice, getSliceTasks, getMilestone, updateSliceStatus, setSliceSummaryMd, saveGateResult, getPendingGatesForTurn, getMilestoneSlices, updateMilestoneStatus, } from "../gsd-db.js";
|
|
14
|
+
import { completeSliceCascade, setSliceSummaryMd, saveGateResult, getPendingGatesForTurn, } from "../gsd-db.js";
|
|
16
15
|
import { getGatesForTurn } from "../gate-registry.js";
|
|
17
16
|
import { gsdProjectionRoot, clearPathCache, resolveMilestoneFile } from "../paths.js";
|
|
18
17
|
import { resolveCanonicalMilestoneRoot } from "../worktree-manager.js";
|
|
@@ -295,52 +294,34 @@ export async function handleCompleteSlice(params, basePath) {
|
|
|
295
294
|
error: `UAT requires browser verification (opening a page in a browser, navigating to a page or localhost, screenshots) but declares "UAT mode: artifact-driven", which only runs static/file checks and would defer the browser work to a human. Use a mode that actually verifies the UI: "browser-executable" (interactive browser tools), "runtime-executable" (a browser test command such as playwright), or a browser-inclusive "mixed"/"live-runtime". Re-author the UAT Type section and complete the slice again.`,
|
|
296
295
|
};
|
|
297
296
|
}
|
|
298
|
-
// ──
|
|
297
|
+
// ── Atomic completion cascade (guards + writes in one transaction) ───────
|
|
299
298
|
const completedAt = new Date().toISOString();
|
|
300
299
|
let guardError = null;
|
|
301
300
|
let existingSummaryMd = "";
|
|
302
301
|
let duplicateComplete = false;
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
// Only block if they exist and are closed.
|
|
307
|
-
const milestone = getMilestone(params.milestoneId);
|
|
308
|
-
if (milestone && isClosedStatus(milestone.status)) {
|
|
309
|
-
guardError = `cannot complete slice in a closed milestone: ${params.milestoneId} (status: ${milestone.status})`;
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
const slice = getSlice(params.milestoneId, params.sliceId);
|
|
313
|
-
existingSummaryMd = slice?.full_summary_md?.trim() ?? "";
|
|
314
|
-
if (slice && isClosedStatus(slice.status)) {
|
|
315
|
-
duplicateComplete = true;
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
318
|
-
// Verify all tasks are complete
|
|
319
|
-
const tasks = getSliceTasks(params.milestoneId, params.sliceId);
|
|
320
|
-
if (tasks.length === 0) {
|
|
321
|
-
guardError = `no tasks found for slice ${params.sliceId} in milestone ${params.milestoneId}`;
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
const incompleteTasks = tasks.filter(t => !isClosedStatus(t.status));
|
|
325
|
-
if (incompleteTasks.length > 0) {
|
|
326
|
-
const incompleteIds = incompleteTasks.map(t => `${t.id} (status: ${t.status})`).join(", ");
|
|
327
|
-
guardError = `incomplete tasks: ${incompleteIds}`;
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
// All guards passed — perform writes. Preserve existing planning metadata:
|
|
331
|
-
// completion should not overwrite title/risk/depends/demo/sequence.
|
|
332
|
-
insertMilestone({ id: params.milestoneId, title: params.milestoneId });
|
|
333
|
-
if (!slice) {
|
|
334
|
-
insertSlice({ id: params.sliceId, milestoneId: params.milestoneId, title: params.sliceTitle || params.sliceId });
|
|
335
|
-
}
|
|
336
|
-
updateSliceStatus(params.milestoneId, params.sliceId, "complete", completedAt);
|
|
337
|
-
const updatedSlices = getMilestoneSlices(params.milestoneId);
|
|
338
|
-
if (milestone?.status === "planned" &&
|
|
339
|
-
updatedSlices.length > 0 &&
|
|
340
|
-
updatedSlices.every((s) => isClosedStatus(s.status))) {
|
|
341
|
-
updateMilestoneStatus(params.milestoneId, "active");
|
|
342
|
-
}
|
|
302
|
+
const outcome = completeSliceCascade(params.milestoneId, params.sliceId, {
|
|
303
|
+
sliceTitle: params.sliceTitle,
|
|
304
|
+
completedAt,
|
|
343
305
|
});
|
|
306
|
+
if (outcome.ok) {
|
|
307
|
+
existingSummaryMd = outcome.existingSummaryMd;
|
|
308
|
+
duplicateComplete = outcome.duplicate;
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
switch (outcome.reason) {
|
|
312
|
+
case "milestone-closed":
|
|
313
|
+
guardError = `cannot complete slice in a closed milestone: ${params.milestoneId} (status: ${outcome.status})`;
|
|
314
|
+
break;
|
|
315
|
+
case "no-tasks":
|
|
316
|
+
guardError = `no tasks found for slice ${params.sliceId} in milestone ${params.milestoneId}`;
|
|
317
|
+
break;
|
|
318
|
+
case "incomplete-tasks": {
|
|
319
|
+
const incompleteIds = outcome.incomplete.map((t) => `${t.id} (status: ${t.status})`).join(", ");
|
|
320
|
+
guardError = `incomplete tasks: ${incompleteIds}`;
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
344
325
|
if (duplicateComplete) {
|
|
345
326
|
const staleSummaryPath = sliceSummaryPath(artifactBasePath, params.milestoneId, params.sliceId);
|
|
346
327
|
const duplicateIsStale = isStaleWrite("complete-slice");
|
|
@@ -4,6 +4,7 @@ import { EXEC_DEFAULTS, runExecSandbox, } from "../exec-sandbox.js";
|
|
|
4
4
|
import { realpathSync } from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { isContextModeEnabled } from "../preferences-types.js";
|
|
7
|
+
import { projectRootFromWorktreePath } from "../worktree-root.js";
|
|
7
8
|
import { contextModeDisabledResult } from "./context-mode-tool-result.js";
|
|
8
9
|
const UAT_EXEC_INTENTS = [
|
|
9
10
|
"uat-artifact-check",
|
|
@@ -141,14 +142,10 @@ function normalizeScanPath(value) {
|
|
|
141
142
|
}
|
|
142
143
|
function parseWorktreeBase(baseDir) {
|
|
143
144
|
const normalizedBase = normalizeScanPath(baseDir);
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
if (markerIndex <= 0)
|
|
145
|
+
const originalRoot = projectRootFromWorktreePath(normalizedBase);
|
|
146
|
+
if (!originalRoot)
|
|
147
147
|
return null;
|
|
148
|
-
return {
|
|
149
|
-
originalRoot: normalizedBase.slice(0, markerIndex),
|
|
150
|
-
worktreeRoot: normalizedBase,
|
|
151
|
-
};
|
|
148
|
+
return { originalRoot, worktreeRoot: normalizedBase };
|
|
152
149
|
}
|
|
153
150
|
function pathInside(parent, target) {
|
|
154
151
|
const parentWithSep = parent.endsWith("/") ? parent : `${parent}/`;
|
|
@@ -234,7 +231,7 @@ function scriptReferencesOriginalRootFromWorktree(script, baseDir) {
|
|
|
234
231
|
return false;
|
|
235
232
|
const normalizedScript = script.replace(/\\/g, "/");
|
|
236
233
|
return comparablePathVariants(parsed.originalRoot).some((originalRoot) => {
|
|
237
|
-
const originalRootPattern = new RegExp(`${escapeRegExp(originalRoot)}(?=$|[\\s'"\\\`;)&|<>]|/(?!\\.gsd
|
|
234
|
+
const originalRootPattern = new RegExp(`${escapeRegExp(originalRoot)}(?=$|[\\s'"\\\`;)&|<>]|/(?!\\.gsd(?:-worktrees|/worktrees)(?:/|$)))`);
|
|
238
235
|
return originalRootPattern.test(normalizedScript);
|
|
239
236
|
});
|
|
240
237
|
}
|
|
@@ -12,7 +12,8 @@ import { writeManifest } from "../workflow-manifest.js";
|
|
|
12
12
|
import { appendEvent } from "../workflow-events.js";
|
|
13
13
|
import { logWarning } from "../workflow-logger.js";
|
|
14
14
|
import { validatePathOnlyPlanningFields, validatePlanningPathScope } from "../planning-path-scope.js";
|
|
15
|
-
import {
|
|
15
|
+
import { runTaskPathChecks } from "../pre-execution-checks.js";
|
|
16
|
+
import { resolveWorktreeProjectRoot } from "../worktree-root.js";
|
|
16
17
|
import { buildTaskFileName, gsdProjectionRoot } from "../paths.js";
|
|
17
18
|
import { loadEffectiveGSDPreferences } from "../preferences.js";
|
|
18
19
|
import { createRepositoryRegistryFromPreferences, defaultRepositoryTargets } from "../repository-registry.js";
|
|
@@ -193,11 +194,16 @@ function validateTaskPathsBeforePersist(params, basePath, defaultTargets, allowe
|
|
|
193
194
|
const additionalRoots = allowedRoots
|
|
194
195
|
.map((root) => resolve(root))
|
|
195
196
|
.filter((root) => root !== baseRoot);
|
|
196
|
-
const
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
197
|
+
const resolvedCanonicalRoot = resolve(resolveWorktreeProjectRoot(basePath));
|
|
198
|
+
const canonicalProjectRoot = resolvedCanonicalRoot !== baseRoot ? resolvedCanonicalRoot : undefined;
|
|
199
|
+
const hasContext = additionalRoots.length > 0 || canonicalProjectRoot !== undefined;
|
|
200
|
+
const context = hasContext
|
|
201
|
+
? {
|
|
202
|
+
...(additionalRoots.length > 0 ? { additionalRoots } : {}),
|
|
203
|
+
...(canonicalProjectRoot !== undefined ? { canonicalProjectRoot } : {}),
|
|
204
|
+
}
|
|
205
|
+
: undefined;
|
|
206
|
+
const checks = runTaskPathChecks(taskRows, basePath, context);
|
|
201
207
|
const blocking = checks.filter((check) => !check.passed && check.blocking);
|
|
202
208
|
if (blocking.length === 0)
|
|
203
209
|
return null;
|
|
@@ -7,9 +7,8 @@
|
|
|
7
7
|
* artifacts so the DB-filesystem reconciler does not auto-correct
|
|
8
8
|
* entities back to "complete".
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
10
|
+
import { getMilestoneSlices, getSliceTasks, reopenMilestoneCascade, } from "../gsd-db.js";
|
|
11
11
|
import { invalidateStateCache } from "../state.js";
|
|
12
|
-
import { isClosedStatus } from "../status-guards.js";
|
|
13
12
|
import { renderAllProjections } from "../workflow-projections.js";
|
|
14
13
|
import { writeManifest } from "../workflow-manifest.js";
|
|
15
14
|
import { appendEvent } from "../workflow-events.js";
|
|
@@ -23,35 +22,18 @@ export async function handleReopenMilestone(params, basePath) {
|
|
|
23
22
|
if (!params.milestoneId || typeof params.milestoneId !== "string" || params.milestoneId.trim() === "") {
|
|
24
23
|
return { error: "milestoneId is required and must be a non-empty string" };
|
|
25
24
|
}
|
|
26
|
-
// ──
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return;
|
|
25
|
+
// ── Atomic reopen cascade (guards + writes in one transaction) ───────────
|
|
26
|
+
const outcome = reopenMilestoneCascade(params.milestoneId);
|
|
27
|
+
if (!outcome.ok) {
|
|
28
|
+
switch (outcome.reason) {
|
|
29
|
+
case "milestone-not-found":
|
|
30
|
+
return { error: `milestone not found: ${params.milestoneId}` };
|
|
31
|
+
case "milestone-not-closed":
|
|
32
|
+
return { error: `milestone ${params.milestoneId} is not closed (status: ${outcome.status}) — nothing to reopen` };
|
|
35
33
|
}
|
|
36
|
-
if (!isClosedStatus(milestone.status)) {
|
|
37
|
-
guardError = `milestone ${params.milestoneId} is not closed (status: ${milestone.status}) — nothing to reopen`;
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
reopenMilestoneStatus(params.milestoneId);
|
|
41
|
-
const slices = getMilestoneSlices(params.milestoneId);
|
|
42
|
-
slicesResetCount = slices.length;
|
|
43
|
-
for (const slice of slices) {
|
|
44
|
-
updateSliceStatus(params.milestoneId, slice.id, "in_progress");
|
|
45
|
-
const tasks = getSliceTasks(params.milestoneId, slice.id);
|
|
46
|
-
tasksResetCount += tasks.length;
|
|
47
|
-
for (const task of tasks) {
|
|
48
|
-
updateTaskStatus(params.milestoneId, slice.id, task.id, "pending");
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
if (guardError) {
|
|
53
|
-
return { error: guardError };
|
|
54
34
|
}
|
|
35
|
+
const slicesResetCount = outcome.slicesReset;
|
|
36
|
+
const tasksResetCount = outcome.tasksReset;
|
|
55
37
|
// ── Invalidate caches ────────────────────────────────────────────────────
|
|
56
38
|
invalidateStateCache();
|
|
57
39
|
// ── Clean up stale filesystem artifacts (M12 fix) ────────────────────────
|
|
@@ -9,9 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
// GSD — reopen-slice tool handler
|
|
11
11
|
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
12
|
-
import {
|
|
12
|
+
import { getSliceTasks, reopenSliceCascade, } from "../gsd-db.js";
|
|
13
13
|
import { invalidateStateCache } from "../state.js";
|
|
14
|
-
import { isClosedStatus } from "../status-guards.js";
|
|
15
14
|
import { renderAllProjections } from "../workflow-projections.js";
|
|
16
15
|
import { writeManifest } from "../workflow-manifest.js";
|
|
17
16
|
import { appendEvent } from "../workflow-events.js";
|
|
@@ -27,39 +26,21 @@ export async function handleReopenSlice(params, basePath) {
|
|
|
27
26
|
if (!params.milestoneId || typeof params.milestoneId !== "string" || params.milestoneId.trim() === "") {
|
|
28
27
|
return { error: "milestoneId is required and must be a non-empty string" };
|
|
29
28
|
}
|
|
30
|
-
// ──
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
// ── Atomic reopen cascade (guards + writes in one transaction) ───────────
|
|
30
|
+
const outcome = reopenSliceCascade(params.milestoneId, params.sliceId);
|
|
31
|
+
if (!outcome.ok) {
|
|
32
|
+
switch (outcome.reason) {
|
|
33
|
+
case "milestone-not-found":
|
|
34
|
+
return { error: `milestone not found: ${params.milestoneId}` };
|
|
35
|
+
case "milestone-closed":
|
|
36
|
+
return { error: `cannot reopen slice in a closed milestone: ${params.milestoneId} (status: ${outcome.status})` };
|
|
37
|
+
case "slice-not-found":
|
|
38
|
+
return { error: `slice not found: ${params.milestoneId}/${params.sliceId}` };
|
|
39
|
+
case "slice-not-complete":
|
|
40
|
+
return { error: `slice ${params.sliceId} is not complete (status: ${outcome.status}) — nothing to reopen` };
|
|
38
41
|
}
|
|
39
|
-
if (isClosedStatus(milestone.status)) {
|
|
40
|
-
guardError = `cannot reopen slice in a closed milestone: ${params.milestoneId} (status: ${milestone.status})`;
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
const slice = getSlice(params.milestoneId, params.sliceId);
|
|
44
|
-
if (!slice) {
|
|
45
|
-
guardError = `slice not found: ${params.milestoneId}/${params.sliceId}`;
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
if (!isClosedStatus(slice.status)) {
|
|
49
|
-
guardError = `slice ${params.sliceId} is not complete (status: ${slice.status}) — nothing to reopen`;
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
// Fetch tasks inside txn so the list is consistent with the slice status check
|
|
53
|
-
const tasks = getSliceTasks(params.milestoneId, params.sliceId);
|
|
54
|
-
tasksResetCount = tasks.length;
|
|
55
|
-
updateSliceStatus(params.milestoneId, params.sliceId, "in_progress");
|
|
56
|
-
for (const task of tasks) {
|
|
57
|
-
updateTaskStatus(params.milestoneId, params.sliceId, task.id, "pending");
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
if (guardError) {
|
|
61
|
-
return { error: guardError };
|
|
62
42
|
}
|
|
43
|
+
const tasksResetCount = outcome.tasksReset;
|
|
63
44
|
// ── Invalidate caches ────────────────────────────────────────────────────
|
|
64
45
|
invalidateStateCache();
|
|
65
46
|
// ── Clean up stale filesystem artifacts (M12 fix) ────────────────────────
|
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
* This function performs DB writes only. The MCP wrapper in
|
|
10
10
|
* bootstrap/db-tools.ts handles state-cache invalidation and STATE.md rebuild.
|
|
11
11
|
*/
|
|
12
|
-
import {
|
|
13
|
-
import { isClosedStatus } from "../status-guards.js";
|
|
12
|
+
import { isDbAvailable, skipSliceCascade, } from "../gsd-db.js";
|
|
14
13
|
/**
|
|
15
14
|
* Mark a slice as "skipped" and cascade the skip to every non-closed task in
|
|
16
15
|
* that slice. Runs as a single transaction so slice status and task statuses
|
|
@@ -39,40 +38,23 @@ export function handleSkipSlice(params) {
|
|
|
39
38
|
if (!isDbAvailable()) {
|
|
40
39
|
throw new Error("handleSkipSlice: GSD database is not available");
|
|
41
40
|
}
|
|
42
|
-
// ──
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
41
|
+
// ── Atomic skip cascade (guards + writes in one transaction) ────────────
|
|
42
|
+
const outcome = skipSliceCascade(params.milestoneId, params.sliceId);
|
|
43
|
+
if (!outcome.ok) {
|
|
44
|
+
switch (outcome.reason) {
|
|
45
|
+
case "slice-not-found":
|
|
46
|
+
return {
|
|
47
|
+
...base,
|
|
48
|
+
error: `Slice ${params.sliceId} not found in milestone ${params.milestoneId}`,
|
|
49
|
+
errorCode: "slice_not_found",
|
|
50
|
+
};
|
|
51
|
+
case "slice-already-complete":
|
|
52
|
+
return {
|
|
53
|
+
...base,
|
|
54
|
+
error: `Slice ${params.sliceId} is already complete — cannot skip.`,
|
|
55
|
+
errorCode: "already_complete",
|
|
56
|
+
};
|
|
53
57
|
}
|
|
54
|
-
if (slice.status === "complete" || slice.status === "done") {
|
|
55
|
-
guardError = `Slice ${params.sliceId} is already complete — cannot skip.`;
|
|
56
|
-
guardCode = "already_complete";
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
wasAlreadySkipped = slice.status === "skipped";
|
|
60
|
-
if (!wasAlreadySkipped) {
|
|
61
|
-
updateSliceStatus(params.milestoneId, params.sliceId, "skipped");
|
|
62
|
-
}
|
|
63
|
-
// Cascade: mark every non-closed task as skipped so milestone completion
|
|
64
|
-
// doesn't trip the deep-task guard (#4375). Closed tasks (complete/done/
|
|
65
|
-
// skipped) are left untouched — we never downgrade.
|
|
66
|
-
const tasks = getSliceTasks(params.milestoneId, params.sliceId);
|
|
67
|
-
for (const task of tasks) {
|
|
68
|
-
if (!isClosedStatus(task.status)) {
|
|
69
|
-
updateTaskStatus(params.milestoneId, params.sliceId, task.id, "skipped");
|
|
70
|
-
tasksSkipped++;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
if (guardError) {
|
|
75
|
-
return { ...base, error: guardError, errorCode: guardCode ?? undefined };
|
|
76
58
|
}
|
|
77
|
-
return { ...base, tasksSkipped, wasAlreadySkipped };
|
|
59
|
+
return { ...base, tasksSkipped: outcome.tasksSkipped, wasAlreadySkipped: outcome.wasAlreadySkipped };
|
|
78
60
|
}
|
|
@@ -11,7 +11,7 @@ import { deriveState } from "./state.js";
|
|
|
11
11
|
import { invalidateAllCaches } from "./cache.js";
|
|
12
12
|
import { gsdRoot, resolveTasksDir, resolveSlicePath, resolveTaskFile, buildTaskFileName, buildSliceFileName } from "./paths.js";
|
|
13
13
|
import { sendDesktopNotification } from "./notifications.js";
|
|
14
|
-
import { getTask, getSlice, getSliceTasks, updateTaskStatus,
|
|
14
|
+
import { getTask, getSlice, getSliceTasks, updateTaskStatus, resetSliceCascade } from "./gsd-db.js";
|
|
15
15
|
import { renderPlanCheckboxes, renderRoadmapCheckboxes } from "./markdown-renderer.js";
|
|
16
16
|
/**
|
|
17
17
|
* Undo the last completed unit: revert git commits,
|
|
@@ -275,20 +275,21 @@ export async function handleResetSlice(args, ctx, _pi, basePath) {
|
|
|
275
275
|
`Run /gsd reset-slice ${rawId} --force to confirm.`, "warning");
|
|
276
276
|
return;
|
|
277
277
|
}
|
|
278
|
-
// Reset all
|
|
279
|
-
|
|
278
|
+
// Reset all task statuses to "pending" and the slice to "active" in one
|
|
279
|
+
// atomic commit (DB is source of truth). Previously a per-task updateTaskStatus
|
|
280
|
+
// loop + a separate updateSliceStatus, which could leave a partial reset if
|
|
281
|
+
// interrupted mid-loop.
|
|
282
|
+
resetSliceCascade(mid, sid);
|
|
283
|
+
// Delete task summary files — projection cleanup, separate from the DB reset.
|
|
284
|
+
const tasksReset = tasks.length;
|
|
280
285
|
let summariesDeleted = 0;
|
|
281
286
|
for (const t of tasks) {
|
|
282
|
-
updateTaskStatus(mid, sid, t.id, "pending");
|
|
283
|
-
tasksReset++;
|
|
284
287
|
const summaryPath = resolveTaskFile(basePath, mid, sid, t.id, "SUMMARY");
|
|
285
288
|
if (summaryPath && existsSync(summaryPath)) {
|
|
286
289
|
unlinkSync(summaryPath);
|
|
287
290
|
summariesDeleted++;
|
|
288
291
|
}
|
|
289
292
|
}
|
|
290
|
-
// Reset slice status
|
|
291
|
-
updateSliceStatus(mid, sid, "active");
|
|
292
293
|
// Delete slice summary and UAT files
|
|
293
294
|
let sliceFilesDeleted = 0;
|
|
294
295
|
const slicePath = resolveSlicePath(basePath, mid, sid);
|