@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
|
@@ -20,6 +20,7 @@ import { spawn } from "node:child_process";
|
|
|
20
20
|
import { homedir } from "node:os";
|
|
21
21
|
import { isAbsolute, relative, resolve } from "node:path";
|
|
22
22
|
import { validateVerificationCommand } from "./verification-gate.js";
|
|
23
|
+
import { FRAMEWORK_METADATA_DIRS, PLANNING_ARTIFACT_NAME_RE } from "./paths.js";
|
|
23
24
|
const NPM_COMMAND = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
24
25
|
function inputExistsOnDisk(normalizedFile, basePath, context) {
|
|
25
26
|
if (existsSync(resolve(basePath, normalizedFile)))
|
|
@@ -510,6 +511,10 @@ export function checkFilePathConsistency(tasks, basePath, context) {
|
|
|
510
511
|
continue;
|
|
511
512
|
if (!shouldValidateInputAsPath(file))
|
|
512
513
|
continue;
|
|
514
|
+
// Planning-artifact references are owned by checkPlanningArtifactReferences,
|
|
515
|
+
// which reports a precise removal message instead of "doesn't exist".
|
|
516
|
+
if (isPlanningArtifactReference(file, basePath, context))
|
|
517
|
+
continue;
|
|
513
518
|
// Normalize path for consistent comparison
|
|
514
519
|
const normalizedFile = toComparisonPath(file, basePath);
|
|
515
520
|
if (containsGlobPattern(normalizedFile))
|
|
@@ -546,6 +551,71 @@ export function checkFilePathConsistency(tasks, basePath, context) {
|
|
|
546
551
|
}
|
|
547
552
|
return results;
|
|
548
553
|
}
|
|
554
|
+
// ─── Planning Artifact Reference Check ───────────────────────────────────────
|
|
555
|
+
function isFrameworkMetadataPath(normalizedFile) {
|
|
556
|
+
return normalizedFile.split("/").some((segment) => FRAMEWORK_METADATA_DIRS.includes(segment.toLowerCase()));
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* True when a task IO entry references a GSD planning artifact.
|
|
560
|
+
*
|
|
561
|
+
* Task inputs are source files only — planning artifacts are projections of DB
|
|
562
|
+
* state that the framework preloads as composed context (see CONTEXT.md "Task
|
|
563
|
+
* Input"). Two triggers, deliberately different in strictness:
|
|
564
|
+
*
|
|
565
|
+
* - Any path entering framework-owned metadata (.gsd/, .planning/, .audits/)
|
|
566
|
+
* is always a violation, even when the file exists.
|
|
567
|
+
* - A bare artifact-style basename (M001-CONTEXT.md, S01-PLAN.md, …) is a
|
|
568
|
+
* violation only when it does NOT resolve to a real file. When such a file
|
|
569
|
+
* genuinely exists in the source tree (e.g. a fixture), it is a source file
|
|
570
|
+
* and stays legal; a path with directory components is likewise left to the
|
|
571
|
+
* ordinary existence checks.
|
|
572
|
+
*/
|
|
573
|
+
function isPlanningArtifactReference(rawEntry, basePath, context) {
|
|
574
|
+
if (!shouldValidateInputAsPath(rawEntry))
|
|
575
|
+
return false;
|
|
576
|
+
const normalized = toComparisonPath(rawEntry, basePath);
|
|
577
|
+
if (isFrameworkMetadataPath(normalized))
|
|
578
|
+
return true;
|
|
579
|
+
return (!normalized.includes("/") &&
|
|
580
|
+
PLANNING_ARTIFACT_NAME_RE.test(normalized) &&
|
|
581
|
+
!inputExistsOnDisk(normalized, basePath, context));
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Block GSD planning artifacts in task IO fields (inputs, files, expectedOutput).
|
|
585
|
+
*
|
|
586
|
+
* For inputs/files the violation smuggles a projection into the executor's
|
|
587
|
+
* preloaded source context; for expectedOutput it promises a write the runtime
|
|
588
|
+
* write-gate would reject mid-execution. Both get a precise message so the
|
|
589
|
+
* planning retry steers toward *removing* the reference, not creating the file.
|
|
590
|
+
* checkFilePathConsistency and checkTaskOrdering skip entries this check owns.
|
|
591
|
+
*/
|
|
592
|
+
export function checkPlanningArtifactReferences(tasks, basePath, context) {
|
|
593
|
+
const results = [];
|
|
594
|
+
for (const task of tasks) {
|
|
595
|
+
const fields = [
|
|
596
|
+
{ label: "inputs", entries: task.inputs },
|
|
597
|
+
{ label: "files", entries: task.files },
|
|
598
|
+
{ label: "expectedOutput", entries: task.expected_output },
|
|
599
|
+
];
|
|
600
|
+
for (const { label, entries } of fields) {
|
|
601
|
+
for (const file of entries) {
|
|
602
|
+
if (!isPlanningArtifactReference(file, basePath, context))
|
|
603
|
+
continue;
|
|
604
|
+
const message = label === "expectedOutput"
|
|
605
|
+
? `Task ${task.id} lists '${file}' in expectedOutput — GSD planning artifacts are written by workflow tools (e.g. gsd_summary_save), never by tasks; remove it`
|
|
606
|
+
: `Task ${task.id} lists '${file}' in ${label} — GSD planning artifacts are projections preloaded as context, never task ${label}; remove it`;
|
|
607
|
+
results.push({
|
|
608
|
+
category: "file",
|
|
609
|
+
target: file,
|
|
610
|
+
passed: false,
|
|
611
|
+
message,
|
|
612
|
+
blocking: true,
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
return results;
|
|
618
|
+
}
|
|
549
619
|
// ─── Task Ordering Check ─────────────────────────────────────────────────────
|
|
550
620
|
/**
|
|
551
621
|
* Detect impossible task ordering: task N reads a file that task N+M creates.
|
|
@@ -583,6 +653,10 @@ export function checkTaskOrdering(tasks, basePath, context) {
|
|
|
583
653
|
continue;
|
|
584
654
|
if (!shouldValidateInputAsPath(file))
|
|
585
655
|
continue;
|
|
656
|
+
// Owned by checkPlanningArtifactReferences — avoid a duplicate sequence
|
|
657
|
+
// finding on top of the removal message.
|
|
658
|
+
if (isPlanningArtifactReference(file, basePath, context))
|
|
659
|
+
continue;
|
|
586
660
|
const normalizedFile = toComparisonPath(file, basePath);
|
|
587
661
|
if (containsGlobPattern(normalizedFile))
|
|
588
662
|
continue;
|
|
@@ -611,6 +685,21 @@ export function checkTaskOrdering(tasks, basePath, context) {
|
|
|
611
685
|
}
|
|
612
686
|
return results;
|
|
613
687
|
}
|
|
688
|
+
// ─── Shared Path-Check Composition ───────────────────────────────────────────
|
|
689
|
+
/**
|
|
690
|
+
* The synchronous path-shaped checks, in order. One list shared by the
|
|
691
|
+
* plan-persist gate (gsd_plan_slice) and the dispatch gate
|
|
692
|
+
* (runPreExecutionChecks) so the two cannot drift — the checks coordinate
|
|
693
|
+
* ownership of planning-artifact entries via isPlanningArtifactReference
|
|
694
|
+
* skips, which only works when they run as a set.
|
|
695
|
+
*/
|
|
696
|
+
export function runTaskPathChecks(tasks, basePath, context) {
|
|
697
|
+
return [
|
|
698
|
+
...checkPlanningArtifactReferences(tasks, basePath, context),
|
|
699
|
+
...checkFilePathConsistency(tasks, basePath, context),
|
|
700
|
+
...checkTaskOrdering(tasks, basePath, context),
|
|
701
|
+
];
|
|
702
|
+
}
|
|
614
703
|
/**
|
|
615
704
|
* Extract function signatures from code blocks in task description.
|
|
616
705
|
* Uses heuristic regex — not an AST parser.
|
|
@@ -741,11 +830,10 @@ export async function runPreExecutionChecks(tasks, basePath, context) {
|
|
|
741
830
|
const startTime = Date.now();
|
|
742
831
|
const allChecks = [];
|
|
743
832
|
// Run sync checks first
|
|
744
|
-
const
|
|
745
|
-
const orderingChecks = checkTaskOrdering(tasks, basePath, context);
|
|
833
|
+
const pathChecks = runTaskPathChecks(tasks, basePath, context);
|
|
746
834
|
const contractChecks = checkInterfaceContracts(tasks, basePath);
|
|
747
835
|
const verificationChecks = checkVerificationCommands(tasks);
|
|
748
|
-
allChecks.push(...
|
|
836
|
+
allChecks.push(...pathChecks, ...contractChecks, ...verificationChecks);
|
|
749
837
|
// Run async package checks
|
|
750
838
|
const packageChecks = await checkPackageExistence(tasks, basePath);
|
|
751
839
|
allChecks.push(...packageChecks);
|
|
@@ -11,6 +11,7 @@ import { join } from "node:path";
|
|
|
11
11
|
import { gsdHome } from "./gsd-home.js";
|
|
12
12
|
import { canonicalModelForTier, defaultRoutingConfig, resolveModelForTier } from "./model-router.js";
|
|
13
13
|
import { loadEffectiveGSDPreferences, getGlobalGSDPreferencesPath } from "./preferences.js";
|
|
14
|
+
import { getUnitPhaseChain } from "./unit-registry.js";
|
|
14
15
|
/**
|
|
15
16
|
* Resolve which model ID to use for a given auto-mode unit type.
|
|
16
17
|
* Returns undefined if no model preference is set for this unit type.
|
|
@@ -29,53 +30,18 @@ export function resolveModelForUnit(unitType) {
|
|
|
29
30
|
* (`resolveThinkingLevelForUnit`) so the two never drift (ADR-026).
|
|
30
31
|
*/
|
|
31
32
|
export function phaseChainForUnit(unitType) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
case "replan-slice":
|
|
43
|
-
return ["planning"];
|
|
44
|
-
// Deep-mode project-level discussion units route to the same model bucket
|
|
45
|
-
// as milestone-level discussion (interactive interview style). Workflow
|
|
46
|
-
// preferences and research-decision are tiny ask_user_questions style units
|
|
47
|
-
// that share the discuss bucket because they are conversational. All fall
|
|
48
|
-
// back to planning when no `discuss` bucket is set.
|
|
49
|
-
case "discuss-milestone":
|
|
50
|
-
case "discuss-slice":
|
|
51
|
-
case "discuss-project":
|
|
52
|
-
case "discuss-requirements":
|
|
53
|
-
case "workflow-preferences":
|
|
54
|
-
case "research-decision":
|
|
55
|
-
return ["discuss", "planning"];
|
|
56
|
-
case "execute-task":
|
|
57
|
-
case "reactive-execute":
|
|
58
|
-
return ["execution"];
|
|
59
|
-
case "execute-task-simple":
|
|
60
|
-
return ["execution_simple", "execution"];
|
|
61
|
-
case "complete-slice":
|
|
62
|
-
case "complete-milestone":
|
|
63
|
-
case "worktree-merge":
|
|
64
|
-
return ["completion"];
|
|
65
|
-
case "run-uat":
|
|
66
|
-
return ["uat", "completion"];
|
|
67
|
-
case "reassess-roadmap":
|
|
68
|
-
case "rewrite-docs":
|
|
69
|
-
case "gate-evaluate":
|
|
70
|
-
case "validate-milestone":
|
|
71
|
-
return ["validation", "planning"];
|
|
72
|
-
default:
|
|
73
|
-
// Subagent unit types (e.g., "subagent", "subagent/scout")
|
|
74
|
-
if (unitType === "subagent" || unitType.startsWith("subagent/")) {
|
|
75
|
-
return ["subagent"];
|
|
76
|
-
}
|
|
77
|
-
return undefined;
|
|
33
|
+
// Unit types declare their chain on their Unit Descriptor (ADR-033).
|
|
34
|
+
const declared = getUnitPhaseChain(unitType);
|
|
35
|
+
if (declared)
|
|
36
|
+
return [...declared];
|
|
37
|
+
// Dispatch types without a Unit Descriptor.
|
|
38
|
+
if (unitType === "worktree-merge")
|
|
39
|
+
return ["completion"];
|
|
40
|
+
// Subagent unit types (e.g., "subagent", "subagent/scout")
|
|
41
|
+
if (unitType === "subagent" || unitType.startsWith("subagent/")) {
|
|
42
|
+
return ["subagent"];
|
|
78
43
|
}
|
|
44
|
+
return undefined;
|
|
79
45
|
}
|
|
80
46
|
/**
|
|
81
47
|
* Find the phase bucket whose `models` entry wins the chain for a unit, plus
|
|
@@ -699,6 +699,20 @@ export function getIsolationMode(basePath) {
|
|
|
699
699
|
return "branch";
|
|
700
700
|
return "none"; // default — no isolation, work on current branch
|
|
701
701
|
}
|
|
702
|
+
/**
|
|
703
|
+
* Resolve the isolation mode a unit actually runs under. A session whose
|
|
704
|
+
* worktree isolation has degraded (worktree creation failed) falls back to
|
|
705
|
+
* the milestone branch in the project root, so configured "worktree" becomes
|
|
706
|
+
* effective "branch". A stranded-work recovery session likewise runs under
|
|
707
|
+
* the adopted mode (`strandedRecoveryIsolationMode`) rather than the
|
|
708
|
+
* configured one until the recovered milestone merges — adopting the
|
|
709
|
+
* milestone branch in the project root is intentional, not degraded.
|
|
710
|
+
*/
|
|
711
|
+
export function resolveEffectiveUnitIsolationMode(configuredMode, isolationDegraded, strandedRecoveryIsolationMode = null) {
|
|
712
|
+
if (configuredMode === "worktree" && isolationDegraded)
|
|
713
|
+
return "branch";
|
|
714
|
+
return strandedRecoveryIsolationMode ?? configuredMode;
|
|
715
|
+
}
|
|
702
716
|
export function resolveParallelConfig(prefs) {
|
|
703
717
|
return {
|
|
704
718
|
enabled: prefs?.parallel?.enabled ?? false,
|
|
@@ -45,7 +45,7 @@ If slice research is inlined, trust its architectural findings, but verify every
|
|
|
45
45
|
6. Include Threat Surface (Q3), Requirement Impact (Q4), proof level, observability, integration closure, Failure Modes (Q5), Load Profile (Q6), and Negative Tests (Q7) only where applicable.
|
|
46
46
|
7. Right-size tasks. Simple slices can be one task; split only when context, ownership, or verification boundaries justify it.
|
|
47
47
|
8. Task `verify` commands must be safe, simple commands. Do not use shell pipes, redirects, semicolons, backticks, command substitution, output trimming, or grep regex alternation with `|`. If multiple checks are needed, create a small test file and run it with `node --test` or a package test script, or use separate simple commands joined only with `&&`. For absence checks, verify a pattern does not exist with `! grep -q 'pattern' file` or `! rg -q 'pattern' file`; do not use `grep -c` or `rg -c` to assert zero matches because count commands exit 1 when they find zero matches, and the verification gate treats that as failure.
|
|
48
|
-
9. Each task needs the exact `gsd_plan_slice.tasks[]` shape: `taskId`, `title`, `description`, `estimate`, `files`, `verify`, `inputs`, `expectedOutput`, and optional `observabilityImpact`. `description` should contain the Why / Do / Done-when narrative. `files`, `inputs`, and `expectedOutput` must be JSON arrays of strings, even when there is only one path (for example, `"inputs": ["src/index.ts"]`, never `"inputs": "src/index.ts"`). Use paths relative to `{{workingDirectory}}`; do not put absolute paths to the original checkout or any directory outside `{{workingDirectory}}` in `files`, `inputs`, `expectedOutput`, or verification commands. **`expectedOutput` must only list files the task actually creates or overwrites on disk.** Do NOT include files the task merely reads, verifies, tests, or describes — those belong in `inputs`, `verify`, `description`, or slice success criteria. If a task is a pure verification or test task that produces no new files, `expectedOutput` must be `[]`; if it writes a test-result log or assertion output file, list only that concrete file path. A file that does not yet exist on disk and is needed as an `input` must be produced by an earlier task's `expectedOutput` — if no prior task creates it, add a task before this one that does.
|
|
48
|
+
9. Each task needs the exact `gsd_plan_slice.tasks[]` shape: `taskId`, `title`, `description`, `estimate`, `files`, `verify`, `inputs`, `expectedOutput`, and optional `observabilityImpact`. `description` should contain the Why / Do / Done-when narrative. `files`, `inputs`, and `expectedOutput` must be JSON arrays of strings, even when there is only one path (for example, `"inputs": ["src/index.ts"]`, never `"inputs": "src/index.ts"`). Use paths relative to `{{workingDirectory}}`; do not put absolute paths to the original checkout or any directory outside `{{workingDirectory}}` in `files`, `inputs`, `expectedOutput`, or verification commands. **`expectedOutput` must only list files the task actually creates or overwrites on disk.** Do NOT include files the task merely reads, verifies, tests, or describes — those belong in `inputs`, `verify`, `description`, or slice success criteria. If a task is a pure verification or test task that produces no new files, `expectedOutput` must be `[]`; if it writes a test-result log or assertion output file, list only that concrete file path. A file that does not yet exist on disk and is needed as an `input` must be produced by an earlier task's `expectedOutput` — if no prior task creates it, add a task before this one that does. Never list GSD planning artifacts — anything under `.gsd/`, `.planning/`, or `.audits/`, or artifact names like `M001-CONTEXT.md` / `S01-PLAN.md` — in `inputs`, `files`, or `expectedOutput`: their content is preloaded as context, and they are written by workflow tools, not by tasks.
|
|
49
49
|
10. Persist with `gsd_plan_slice` using `milestoneId`, `sliceId`, `goal`, optional `successCriteria`/`proofLevel`/`integrationClosure`/`observabilityImpact`, and `tasks`. `gsd_plan_slice` handles task persistence transactionally and renders `{{outputPath}}` plus task plans; do not call `gsd_plan_task`. The DB-backed tool is the canonical write path. Do **not** rely on direct `PLAN.md` writes as the source of truth.
|
|
50
50
|
11. Self-audit before finishing: goal/demo closure, requirement coverage, deliverable coverage audit (cross-check every file listed in CONTEXT.md `## Scope` / `### In Scope` against task `files` or `expectedOutput`), locked decisions, concrete paths, dependency order, wiring, scope size, proof truthfulness, feature completeness, and quality gates. Quality gates: non-trivial slices/tasks include specific Q3-Q7 coverage where applicable.
|
|
51
51
|
12. If planning creates structural decisions, call `gsd_decision_save` for each; the tool persists the decision and regenerates `.gsd/DECISIONS.md`.
|
|
@@ -65,7 +65,7 @@ Then:
|
|
|
65
65
|
3. Define slice-level verification: the objective stopping condition. Plan real test files with real assertions; for simple slices, executable commands are fine.
|
|
66
66
|
**Web apps:** when inlined Web App UAT guidance is present, follow it for Playwright scaffolding and browser-capable verification commands.
|
|
67
67
|
4. For non-trivial slices, plan observability / proof level / integration closure, threat surface, and requirement impact. Omit entirely for simple slices.
|
|
68
|
-
5. Decompose the slice into tasks that fit one context window each. Every task passed to `gsd_plan_slice` must use the exact keys `taskId`, `title`, `description`, `estimate`, `files`, `verify`, `inputs`, `expectedOutput`, and optional `observabilityImpact`. Put Why / Do / Done-when detail in `description`. `files`, `inputs`, and `expectedOutput` must be JSON arrays of strings, even for one path (for example, `"expectedOutput": ["src/index.ts"]`, never `"expectedOutput": "src/index.ts"`). `expectedOutput` is path-only: list only files the task creates or overwrites, and use `[]` for pure verification tasks.
|
|
68
|
+
5. Decompose the slice into tasks that fit one context window each. Every task passed to `gsd_plan_slice` must use the exact keys `taskId`, `title`, `description`, `estimate`, `files`, `verify`, `inputs`, `expectedOutput`, and optional `observabilityImpact`. Put Why / Do / Done-when detail in `description`. `files`, `inputs`, and `expectedOutput` must be JSON arrays of strings, even for one path (for example, `"expectedOutput": ["src/index.ts"]`, never `"expectedOutput": "src/index.ts"`). `expectedOutput` is path-only: list only files the task creates or overwrites, and use `[]` for pure verification tasks. Never list GSD planning artifacts — anything under `.gsd/`, `.planning/`, or `.audits/`, or artifact names like `M001-CONTEXT.md` / `S01-PLAN.md` — in `inputs`, `files`, or `expectedOutput`: their content is preloaded as context, and they are written by workflow tools, not by tasks.
|
|
69
69
|
6. **Persist planning state through `gsd_plan_slice`.** Call it with the full payload. The tool writes to the DB and renders `{{outputPath}}` and `{{slicePath}}/tasks/T##-PLAN.md` automatically. Do NOT rely on direct `PLAN.md` writes.
|
|
70
70
|
7. **Self-audit the plan.** If every task were completed exactly as written, the slice goal/demo should be true. Every must-have maps to a task. Inputs and Expected Output are backtick-wrapped file paths.
|
|
71
71
|
8. If refinement produced structural decisions that diverge from the sketch, call `gsd_decision_save` for each; the tool persists the decision and regenerates `.gsd/DECISIONS.md`.
|
|
@@ -34,7 +34,7 @@ Consider these captures when rewriting the remaining tasks — they represent th
|
|
|
34
34
|
|
|
35
35
|
1. Use the inlined blocker summary excerpt first. Read the full blocker task summary only if the excerpt is absent, marked truncated, or lacks the specific blocker evidence needed to replan.
|
|
36
36
|
2. Analyze the remaining `[ ]` tasks in the slice plan. Determine which are still valid, which need modification, and which should be replaced.
|
|
37
|
-
3. **Persist replan state through `gsd_replan_slice`.** Call it with: `milestoneId`, `sliceId`, `blockerTaskId`, `blockerDescription`, `whatChanged`, `updatedTasks` (array of task objects with taskId, title, description, estimate, files, verify, inputs, expectedOutput), `removedTaskIds` (array of task ID strings). The tool structurally enforces preservation of completed tasks, writes replan history to the DB, re-renders `{{planPath}}`, and renders `{{replanPath}}`. Preserve or update the Threat Surface and Requirement Impact sections if the replan changes the slice's security posture or requirement coverage.
|
|
37
|
+
3. **Persist replan state through `gsd_replan_slice`.** Call it with: `milestoneId`, `sliceId`, `blockerTaskId`, `blockerDescription`, `whatChanged`, `updatedTasks` (array of task objects with taskId, title, description, estimate, files, verify, inputs, expectedOutput), `removedTaskIds` (array of task ID strings). Never list GSD planning artifacts — anything under `.gsd/`, `.planning/`, or `.audits/`, or artifact names like `M001-CONTEXT.md` / `S01-PLAN.md` — in `inputs`, `files`, or `expectedOutput`: their content is preloaded as context, and they are written by workflow tools, not by tasks. The tool structurally enforces preservation of completed tasks, writes replan history to the DB, re-renders `{{planPath}}`, and renders `{{replanPath}}`. Preserve or update the Threat Surface and Requirement Impact sections if the replan changes the slice's security posture or requirement coverage.
|
|
38
38
|
4. If any incomplete task had a `T0x-PLAN.md`, remove or rewrite it to match the new task description.
|
|
39
39
|
5. Do not commit manually — the system auto-commits your changes after this unit completes.
|
|
40
40
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Actionable remediation hints when auto-mode pauses on provider/model errors.
|
|
3
|
+
* Render with formatGuidance from the Guidance module.
|
|
3
4
|
*/
|
|
4
5
|
/** Map auto unit types to the `models:` key in PREFERENCES.md. */
|
|
5
6
|
export function unitTypeToPrefsPhaseKey(unitType) {
|
|
@@ -91,8 +92,3 @@ export function resolveProviderErrorGuidance(input) {
|
|
|
91
92
|
steps.push("Run /gsd next to resume the paused unit.");
|
|
92
93
|
return { summary, steps };
|
|
93
94
|
}
|
|
94
|
-
/** Flatten guidance into a pause banner / notification string. */
|
|
95
|
-
export function formatProviderErrorGuidance(guidance) {
|
|
96
|
-
const numbered = guidance.steps.map((step, index) => `${index + 1}. ${step}`).join("\n");
|
|
97
|
-
return `${guidance.summary}\n\n${numbered}`;
|
|
98
|
-
}
|
|
@@ -92,7 +92,7 @@ function emitAudit(report) {
|
|
|
92
92
|
}
|
|
93
93
|
function emitNotification(report) {
|
|
94
94
|
try {
|
|
95
|
-
appendNotification(summarize(report), "warning", "workflow-logger");
|
|
95
|
+
appendNotification(summarize(report), "warning", "workflow-logger", { kind: "provider-switch" });
|
|
96
96
|
}
|
|
97
97
|
catch {
|
|
98
98
|
// Notification persistence is best-effort.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Publication module — push and draft-PR creation for a merged milestone (ADR-034).
|
|
3
|
+
//
|
|
4
|
+
// Publication is distinct from the merge verb: merging a milestone branch is a
|
|
5
|
+
// Worktree Lifecycle concern (worktree, branch, lease, stash discipline);
|
|
6
|
+
// publication needs only the resulting commit, a remote, and preferences.
|
|
7
|
+
// Extracted from the tail of `mergeMilestoneToMain` (auto-worktree.ts) so it is
|
|
8
|
+
// testable without a merge fixture and reachable from any closeout path.
|
|
9
|
+
//
|
|
10
|
+
// Publication failure is non-fatal to a completed local merge: every failure is
|
|
11
|
+
// logged and reported in the result, never thrown.
|
|
12
|
+
import { execFileSync } from "node:child_process";
|
|
13
|
+
import { buildPullRequestEvidence, createDraftPullRequestFromEvidence, } from "./pull-request-process.js";
|
|
14
|
+
import { logWarning } from "./workflow-logger.js";
|
|
15
|
+
export function gitRemoteExists(basePath, remote) {
|
|
16
|
+
try {
|
|
17
|
+
execFileSync("git", ["remote", "get-url", remote], {
|
|
18
|
+
cwd: basePath,
|
|
19
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
20
|
+
encoding: "utf-8",
|
|
21
|
+
});
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Publish a merged milestone per preferences: push the integration branch
|
|
30
|
+
* (auto-push, suppressed when auto-PR owns the remote interaction) and/or
|
|
31
|
+
* push the milestone branch and open a draft PR (auto-PR).
|
|
32
|
+
*/
|
|
33
|
+
export function publishMilestone(request) {
|
|
34
|
+
const result = { pushed: false, prCreated: false };
|
|
35
|
+
if (request.nothingToCommit)
|
|
36
|
+
return result;
|
|
37
|
+
const { basePath, prefs } = request;
|
|
38
|
+
const remote = prefs.remote ?? "origin";
|
|
39
|
+
if (prefs.autoPush && !prefs.autoPr && gitRemoteExists(basePath, remote)) {
|
|
40
|
+
try {
|
|
41
|
+
execFileSync("git", ["push", remote, request.integrationBranch], {
|
|
42
|
+
cwd: basePath,
|
|
43
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
44
|
+
encoding: "utf-8",
|
|
45
|
+
});
|
|
46
|
+
result.pushed = true;
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
// Push failure is non-fatal
|
|
50
|
+
logWarning("worktree", `git push failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// #2302: PR creation is not gated on pushed/auto-push.
|
|
54
|
+
if (prefs.autoPr && gitRemoteExists(basePath, remote)) {
|
|
55
|
+
try {
|
|
56
|
+
// Push the milestone branch to remote first
|
|
57
|
+
execFileSync("git", ["push", remote, request.milestoneBranch], {
|
|
58
|
+
cwd: basePath,
|
|
59
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
60
|
+
encoding: "utf-8",
|
|
61
|
+
});
|
|
62
|
+
const prEvidence = buildPullRequestEvidence({
|
|
63
|
+
milestoneId: request.milestoneId,
|
|
64
|
+
milestoneTitle: request.milestoneTitle,
|
|
65
|
+
changeType: "feat",
|
|
66
|
+
summaries: [...request.sliceSummaries],
|
|
67
|
+
testsRun: ["Auto-created after milestone merge. Run `npm run verify:merge` before marking this draft ready."],
|
|
68
|
+
rollbackNotes: ["Close the draft PR or revert the merge commit if review finds a behavior regression."],
|
|
69
|
+
how: "Generated by git.auto_pr after the milestone branch was pushed and merged locally.",
|
|
70
|
+
});
|
|
71
|
+
const prUrl = createDraftPullRequestFromEvidence(basePath, request.milestoneId, prEvidence, {
|
|
72
|
+
head: request.milestoneBranch,
|
|
73
|
+
base: prefs.prTargetBranch ?? request.integrationBranch,
|
|
74
|
+
});
|
|
75
|
+
if (!prUrl) {
|
|
76
|
+
throw new Error("gh pr create returned no URL");
|
|
77
|
+
}
|
|
78
|
+
result.prCreated = true;
|
|
79
|
+
result.prUrl = prUrl;
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
// PR creation failure is non-fatal — gh may not be installed or authenticated
|
|
83
|
+
logWarning("worktree", `PR creation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
// Project/App: gsd-pi
|
|
2
2
|
// File Purpose: ADR-015 Recovery Classification module for runtime failure taxonomy.
|
|
3
|
+
import { isToolUnavailableError } from "./auto-tool-tracking.js";
|
|
3
4
|
import { classifyError, isTransient } from "./error-classifier.js";
|
|
5
|
+
import { recoveryRemediation } from "./guidance.js";
|
|
4
6
|
import { ReconciliationFailedError } from "./state-reconciliation.js";
|
|
7
|
+
import { IllegalPhaseTransitionError } from "./state-transition-matrix.js";
|
|
5
8
|
export function classifyFailure(input) {
|
|
6
9
|
const message = errorMessage(input.error);
|
|
7
10
|
// ADR-017: ReconciliationFailedError is a typed throw from the State
|
|
@@ -9,96 +12,47 @@ export function classifyFailure(input) {
|
|
|
9
12
|
// failureKind so the taxonomy stays consistent.
|
|
10
13
|
const failureKind = input.error instanceof ReconciliationFailedError
|
|
11
14
|
? "reconciliation-drift"
|
|
12
|
-
: input.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
reason: `Tool Contract failure${unitSuffix(input)}: ${message}`,
|
|
27
|
-
exitReason: "tool-contract",
|
|
28
|
-
remediation: "Fix the Unit Tool Contract or prompt so the Unit is only asked to use tools owned by its phase.",
|
|
29
|
-
};
|
|
30
|
-
case "deterministic-policy":
|
|
31
|
-
return {
|
|
32
|
-
failureKind,
|
|
33
|
-
action: "stop",
|
|
34
|
-
reason: `Deterministic policy failure${unitSuffix(input)}: ${message}`,
|
|
35
|
-
exitReason: "deterministic-policy",
|
|
36
|
-
remediation: "Resolve the policy blocker; retrying the same Unit will repeat the failure.",
|
|
37
|
-
};
|
|
38
|
-
case "lifecycle-progression":
|
|
39
|
-
return {
|
|
40
|
-
failureKind,
|
|
41
|
-
action: "stop",
|
|
42
|
-
reason: `Lifecycle progression failure${unitSuffix(input)}: ${message}`,
|
|
43
|
-
exitReason: "lifecycle-progression",
|
|
44
|
-
remediation: "Route to the required owning Unit or restore the missing artifact before advancing lifecycle state.",
|
|
45
|
-
};
|
|
46
|
-
case "stale-worker":
|
|
47
|
-
return {
|
|
48
|
-
failureKind,
|
|
49
|
-
action: "stop",
|
|
50
|
-
reason: `Stale worker failure${unitSuffix(input)}: ${message}`,
|
|
51
|
-
exitReason: "stale-worker",
|
|
52
|
-
remediation: "Clear or reconcile the stale worker before dispatching another Unit.",
|
|
53
|
-
};
|
|
54
|
-
case "worktree-invalid":
|
|
55
|
-
return {
|
|
56
|
-
failureKind,
|
|
57
|
-
action: "stop",
|
|
58
|
-
reason: `Worktree invalid${unitSuffix(input)}: ${message}`,
|
|
59
|
-
exitReason: "worktree-invalid",
|
|
60
|
-
remediation: "Repair or recreate the milestone worktree before launching source-writing Units.",
|
|
61
|
-
};
|
|
62
|
-
case "verification-drift":
|
|
63
|
-
return {
|
|
64
|
-
failureKind,
|
|
65
|
-
action: "escalate",
|
|
66
|
-
reason: `Verification drift${unitSuffix(input)}: ${message}`,
|
|
67
|
-
exitReason: "verification-drift",
|
|
68
|
-
remediation: "Inspect the verification artifact and reconcile the state snapshot before resuming.",
|
|
69
|
-
};
|
|
70
|
-
case "reconciliation-drift":
|
|
71
|
-
return {
|
|
72
|
-
failureKind,
|
|
73
|
-
action: "escalate",
|
|
74
|
-
reason: `Reconciliation drift${unitSuffix(input)}: ${message}`,
|
|
75
|
-
exitReason: "reconciliation-drift",
|
|
76
|
-
remediation: "Inspect the persistent or repair-failed drift kinds reported by the State Reconciliation Module before resuming.",
|
|
77
|
-
};
|
|
78
|
-
case "provider": {
|
|
79
|
-
const providerClass = classifyError(message, input.retryAfterMs);
|
|
80
|
-
return {
|
|
81
|
-
failureKind,
|
|
82
|
-
action: isTransient(providerClass) ? "retry" : "escalate",
|
|
83
|
-
reason: message,
|
|
84
|
-
exitReason: `provider-${providerClass.kind}`,
|
|
85
|
-
remediation: isTransient(providerClass)
|
|
86
|
-
? "Retry after the provider/network condition clears."
|
|
87
|
-
: "Inspect provider credentials, model entitlement, or request shape.",
|
|
88
|
-
providerClass: providerClass.kind,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
case "runtime-unknown":
|
|
92
|
-
return {
|
|
93
|
-
failureKind,
|
|
94
|
-
action: "escalate",
|
|
95
|
-
reason: message,
|
|
96
|
-
exitReason: "runtime-unknown",
|
|
97
|
-
remediation: "Inspect the runtime error and add a dedicated classification if it is repeatable.",
|
|
98
|
-
};
|
|
15
|
+
: input.error instanceof IllegalPhaseTransitionError
|
|
16
|
+
? "illegal-transition"
|
|
17
|
+
: input.failureKind ?? inferFailureKind(message);
|
|
18
|
+
if (failureKind === "provider") {
|
|
19
|
+
const providerClass = classifyError(message, input.retryAfterMs);
|
|
20
|
+
const transient = isTransient(providerClass);
|
|
21
|
+
return {
|
|
22
|
+
failureKind,
|
|
23
|
+
action: transient ? "retry" : "escalate",
|
|
24
|
+
reason: message,
|
|
25
|
+
exitReason: `provider-${providerClass.kind}`,
|
|
26
|
+
remediation: recoveryRemediation(transient ? "provider-transient" : "provider-permanent"),
|
|
27
|
+
providerClass: providerClass.kind,
|
|
28
|
+
};
|
|
99
29
|
}
|
|
30
|
+
const { action, label } = FAILURE_TAXONOMY[failureKind];
|
|
31
|
+
return {
|
|
32
|
+
failureKind,
|
|
33
|
+
action,
|
|
34
|
+
reason: label ? `${label}${unitSuffix(input)}: ${message}` : message,
|
|
35
|
+
exitReason: failureKind,
|
|
36
|
+
remediation: recoveryRemediation(failureKind),
|
|
37
|
+
};
|
|
100
38
|
}
|
|
39
|
+
/** Per-kind action and reason label. Remediation lives in the Guidance module. */
|
|
40
|
+
const FAILURE_TAXONOMY = {
|
|
41
|
+
"tool-schema": { action: "stop", label: "Tool schema failure" },
|
|
42
|
+
"tool-contract": { action: "stop", label: "Tool Contract failure" },
|
|
43
|
+
"tool-unavailable": { action: "retry", label: "Tool unavailable" },
|
|
44
|
+
"deterministic-policy": { action: "stop", label: "Deterministic policy failure" },
|
|
45
|
+
"lifecycle-progression": { action: "stop", label: "Lifecycle progression failure" },
|
|
46
|
+
"stale-worker": { action: "stop", label: "Stale worker failure" },
|
|
47
|
+
"worktree-invalid": { action: "stop", label: "Worktree invalid" },
|
|
48
|
+
"verification-drift": { action: "escalate", label: "Verification drift" },
|
|
49
|
+
"reconciliation-drift": { action: "escalate", label: "Reconciliation drift" },
|
|
50
|
+
"illegal-transition": { action: "escalate", label: "Illegal phase transition" },
|
|
51
|
+
"runtime-unknown": { action: "escalate", label: null },
|
|
52
|
+
};
|
|
101
53
|
function inferFailureKind(message) {
|
|
54
|
+
if (isToolUnavailableError(message))
|
|
55
|
+
return "tool-unavailable";
|
|
102
56
|
if (/tool contract|auto-unit tool scope|phase-boundary gate|not permitted.*own/i.test(message))
|
|
103
57
|
return "tool-contract";
|
|
104
58
|
if (/lifecycle progression|required artifact|missing .*assessment|missing .*closeout|cannot legally (?:advance|progress)/i.test(message))
|
|
@@ -21,9 +21,10 @@ const EXECUTION_TOOL_NAMES = new Set([
|
|
|
21
21
|
"functions.exec_command",
|
|
22
22
|
"gsd_exec",
|
|
23
23
|
"gsd_exec_search",
|
|
24
|
+
"gsd_uat_exec",
|
|
24
25
|
"powershell",
|
|
25
26
|
]);
|
|
26
|
-
const MCP_EXECUTION_TOOL_RE = /^mcp__.+
|
|
27
|
+
const MCP_EXECUTION_TOOL_RE = /^mcp__.+__gsd_(?:uat_)?exec(?:_search)?$/;
|
|
27
28
|
// ─── Module State ───────────────────────────────────────────────────────────
|
|
28
29
|
let unitEvidence = [];
|
|
29
30
|
// ─── Public API ─────────────────────────────────────────────────────────────
|
|
@@ -146,11 +147,18 @@ export function clearEvidenceFromDisk(basePath, milestoneId, sliceId, taskId) {
|
|
|
146
147
|
* Exit codes and output are filled in by recordToolResult after execution.
|
|
147
148
|
*/
|
|
148
149
|
export function recordToolCall(toolCallId, toolName, input) {
|
|
150
|
+
// Idempotent by toolCallId: native tools reach this via both
|
|
151
|
+
// tool_execution_start and tool_call; external (pre-executed) tools only
|
|
152
|
+
// via tool_execution_start. First recording wins.
|
|
153
|
+
if (unitEvidence.some(e => e.toolCallId === toolCallId))
|
|
154
|
+
return;
|
|
149
155
|
if (isExecutionToolName(toolName)) {
|
|
150
156
|
unitEvidence.push({
|
|
151
157
|
kind: "bash",
|
|
152
158
|
toolCallId,
|
|
153
|
-
|
|
159
|
+
// gsd_exec / gsd_uat_exec carry the script body in `script` (or `code`);
|
|
160
|
+
// bash-style tools use `command`/`cmd`; gsd_exec_search uses `query`.
|
|
161
|
+
command: String(input.command ?? input.script ?? input.cmd ?? input.code ?? input.query ?? ""),
|
|
154
162
|
exitCode: -1,
|
|
155
163
|
outputSnippet: "",
|
|
156
164
|
timestamp: Date.now(),
|
|
@@ -185,9 +193,34 @@ export function recordToolResult(toolCallId, toolName, result, isError) {
|
|
|
185
193
|
if (entry.kind === "bash") {
|
|
186
194
|
const text = extractResultText(result);
|
|
187
195
|
entry.outputSnippet = text.slice(0, 500);
|
|
188
|
-
|
|
189
|
-
|
|
196
|
+
entry.exitCode = resolveExitCode(text, isError);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Resolve the exit code from a tool result's text. Handles the bash tool's
|
|
201
|
+
* prose marker, the gsd_exec / gsd_uat_exec JSON envelope (`"exit_code": N`),
|
|
202
|
+
* and a last-resort read of the run's persisted `.gsd/exec/<id>.meta.json`
|
|
203
|
+
* (covers truncated result text).
|
|
204
|
+
*/
|
|
205
|
+
function resolveExitCode(text, isError) {
|
|
206
|
+
const proseMatch = text.match(/Command exited with code (\d+)/);
|
|
207
|
+
if (proseMatch)
|
|
208
|
+
return Number(proseMatch[1]);
|
|
209
|
+
const jsonMatch = text.match(/"exit_code"\s*:\s*(-?\d+)/);
|
|
210
|
+
if (jsonMatch)
|
|
211
|
+
return Number(jsonMatch[1]);
|
|
212
|
+
const metaMatch = text.match(/"meta_path"\s*:\s*"([^"]+)"/);
|
|
213
|
+
if (metaMatch) {
|
|
214
|
+
try {
|
|
215
|
+
const meta = JSON.parse(readFileSync(metaMatch[1], "utf-8"));
|
|
216
|
+
if (typeof meta.exit_code === "number")
|
|
217
|
+
return meta.exit_code;
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
// Fall through to the isError heuristic
|
|
221
|
+
}
|
|
190
222
|
}
|
|
223
|
+
return isError ? 1 : 0;
|
|
191
224
|
}
|
|
192
225
|
// ─── Internals ──────────────────────────────────────────────────────────────
|
|
193
226
|
function extractResultText(result) {
|
|
@@ -80,8 +80,13 @@ function findMatches(claimedCommand, bashCalls) {
|
|
|
80
80
|
const exact = bashCalls.filter(b => b.command.trim() === normalized);
|
|
81
81
|
if (exact.length > 0)
|
|
82
82
|
return exact;
|
|
83
|
-
// Substring match: claimed is contained in actual or actual in claimed
|
|
84
|
-
|
|
83
|
+
// Substring match: claimed is contained in actual or actual in claimed.
|
|
84
|
+
// A claimed verification command typically appears verbatim inside a
|
|
85
|
+
// larger gsd_exec script body (cd prefix, multi-line scripts), so
|
|
86
|
+
// script-containing-claim is the common direction. Blank-command entries
|
|
87
|
+
// must be excluded — `"x".includes("")` is true, so they'd match anything.
|
|
88
|
+
const substring = bashCalls.filter(b => b.command.trim().length > 0 &&
|
|
89
|
+
(b.command.includes(normalized) || normalized.includes(b.command)));
|
|
85
90
|
if (substring.length > 0)
|
|
86
91
|
return substring;
|
|
87
92
|
// Token match: split on whitespace and check significant overlap
|
|
@@ -17,6 +17,16 @@ import { logWarning } from "../workflow-logger.js";
|
|
|
17
17
|
const _require = createRequire(import.meta.url);
|
|
18
18
|
const picomatch = _require("picomatch");
|
|
19
19
|
// ─── Public API ─────────────────────────────────────────────────────────────
|
|
20
|
+
/**
|
|
21
|
+
* Build the effective allowlist for a unit's file-change audit.
|
|
22
|
+
*
|
|
23
|
+
* When GSD manages .gitignore (manage_gitignore unset or true), ensureGitignore()
|
|
24
|
+
* appends baseline patterns at auto-start and the edit rides into the task's
|
|
25
|
+
* auto-commit — so .gitignore changes must not be attributed to the task.
|
|
26
|
+
*/
|
|
27
|
+
export function effectiveFileChangeAllowlist(baseAllowlist, manageGitignore) {
|
|
28
|
+
return manageGitignore === false ? baseAllowlist : [...baseAllowlist, ".gitignore"];
|
|
29
|
+
}
|
|
20
30
|
/**
|
|
21
31
|
* Validate file changes after auto-commit for an execute-task unit.
|
|
22
32
|
* Returns null if task data is unavailable or DB is not loaded.
|