@opengsd/gsd-pi 1.2.0-dev.4813ead6 → 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 +6 -6
- 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 +6 -6
- 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/image-models.generated.d.ts +0 -30
- package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/image-models.generated.js +0 -30
- package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +361 -255
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +311 -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/{tkLHUSzPA2kMmWz4DmGwI → 2p9Rv9pQflAxCBbGVI2vb}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{tkLHUSzPA2kMmWz4DmGwI → 2p9Rv9pQflAxCBbGVI2vb}/_ssgManifest.js +0 -0
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
isDeterministicPolicyError,
|
|
49
49
|
isPendingUserApprovalGateError,
|
|
50
50
|
isToolInvocationError,
|
|
51
|
+
isToolUnavailableError,
|
|
51
52
|
isQueuedUserMessageSkip,
|
|
52
53
|
} from "../auto-tool-tracking.ts";
|
|
53
54
|
import {
|
|
@@ -121,6 +122,13 @@ describe("#2883: isToolInvocationError classification", () => {
|
|
|
121
122
|
assert.equal(isToolInvocationError("No such tool available: mcp__gsd-workflow__memory_query"), true);
|
|
122
123
|
});
|
|
123
124
|
|
|
125
|
+
test("isToolUnavailableError singles out the startup-race error as transient", () => {
|
|
126
|
+
assert.equal(isToolUnavailableError("No such tool available: mcp__gsd-workflow__gsd_uat_exec"), true);
|
|
127
|
+
assert.equal(isToolUnavailableError("Validation failed for tool gsd_complete_slice"), false);
|
|
128
|
+
assert.equal(isToolUnavailableError("Unexpected end of JSON input"), false);
|
|
129
|
+
assert.equal(isToolUnavailableError(""), false);
|
|
130
|
+
});
|
|
131
|
+
|
|
124
132
|
test("detects ESM export-link errors", () => {
|
|
125
133
|
assert.equal(
|
|
126
134
|
isToolInvocationError("The requested module '../paths.js' does not provide an export named 'gsdProjectionRoot'"),
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Contract coverage for the Tool Surface Readiness gate and its recovery classification.
|
|
3
|
+
|
|
4
|
+
import { describe, test } from "node:test";
|
|
5
|
+
import assert from "node:assert/strict";
|
|
6
|
+
|
|
7
|
+
import { getToolSurfaceReadinessError } from "../tool-surface-readiness.ts";
|
|
8
|
+
import { isToolUnavailableError } from "../auto-tool-tracking.ts";
|
|
9
|
+
import { classifyError, isTransient } from "../error-classifier.ts";
|
|
10
|
+
import { toMcpToolName } from "../mcp-tool-name.ts";
|
|
11
|
+
import { classifyFailure } from "../recovery-classification.ts";
|
|
12
|
+
|
|
13
|
+
const SERVER = "gsd-workflow";
|
|
14
|
+
|
|
15
|
+
function prefixed(tool: string): string {
|
|
16
|
+
return toMcpToolName(SERVER, tool);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const RUN_UAT_TOOLS = [
|
|
20
|
+
"gsd_uat_exec",
|
|
21
|
+
"gsd_uat_result_save",
|
|
22
|
+
"gsd_resume",
|
|
23
|
+
"gsd_milestone_status",
|
|
24
|
+
"gsd_journal_query",
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
describe("getToolSurfaceReadinessError", () => {
|
|
28
|
+
test("returns null when no unit type or no workflow server is in play", () => {
|
|
29
|
+
const observation = { tools: [], mcpServers: [] };
|
|
30
|
+
assert.equal(
|
|
31
|
+
getToolSurfaceReadinessError({ unitType: undefined, workflowServerName: SERVER, observation }),
|
|
32
|
+
null,
|
|
33
|
+
);
|
|
34
|
+
assert.equal(
|
|
35
|
+
getToolSurfaceReadinessError({ unitType: "run-uat", workflowServerName: undefined, observation }),
|
|
36
|
+
null,
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("returns null for units with no required workflow tools", () => {
|
|
41
|
+
const error = getToolSurfaceReadinessError({
|
|
42
|
+
unitType: "rewrite-docs",
|
|
43
|
+
workflowServerName: SERVER,
|
|
44
|
+
observation: { tools: [], mcpServers: [{ name: SERVER, status: "failed" }] },
|
|
45
|
+
});
|
|
46
|
+
assert.equal(error, null);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("returns an error when the expected workflow server is absent from the init surface", () => {
|
|
50
|
+
const error = getToolSurfaceReadinessError({
|
|
51
|
+
unitType: "run-uat",
|
|
52
|
+
workflowServerName: SERVER,
|
|
53
|
+
observation: { tools: ["read", "bash"], mcpServers: [{ name: "other-server", status: "connected" }] },
|
|
54
|
+
});
|
|
55
|
+
assert.ok(error, "expected a readiness error when the workflow server is absent");
|
|
56
|
+
assert.match(error, /workflow tool surface not ready for run-uat/);
|
|
57
|
+
assert.match(error, /absent from the init surface/);
|
|
58
|
+
assert.match(error, /gsd_uat_exec/);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("passes a still-connecting (pending) server through instead of aborting", () => {
|
|
62
|
+
// The SDK reports still-connecting servers as "pending" at init — the
|
|
63
|
+
// common healthy session. A genuine miss after pass-through is caught
|
|
64
|
+
// in-session ("No such tool available" → tool-unavailable → retry).
|
|
65
|
+
const error = getToolSurfaceReadinessError({
|
|
66
|
+
unitType: "plan-slice",
|
|
67
|
+
workflowServerName: SERVER,
|
|
68
|
+
observation: { tools: ["read", "bash"], mcpServers: [{ name: SERVER, status: "pending" }] },
|
|
69
|
+
});
|
|
70
|
+
assert.equal(error, null);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("returns null when all required tools are registered under the MCP prefix", () => {
|
|
74
|
+
const error = getToolSurfaceReadinessError({
|
|
75
|
+
unitType: "run-uat",
|
|
76
|
+
workflowServerName: SERVER,
|
|
77
|
+
observation: {
|
|
78
|
+
tools: ["read", ...RUN_UAT_TOOLS.map(prefixed)],
|
|
79
|
+
mcpServers: [{ name: SERVER, status: "connected" }],
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
assert.equal(error, null);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("reports the failed server and the missing tools when the surface never registered", () => {
|
|
86
|
+
const error = getToolSurfaceReadinessError({
|
|
87
|
+
unitType: "run-uat",
|
|
88
|
+
workflowServerName: SERVER,
|
|
89
|
+
observation: { tools: ["read", "bash"], mcpServers: [{ name: SERVER, status: "failed" }] },
|
|
90
|
+
});
|
|
91
|
+
assert.ok(error, "expected a readiness error");
|
|
92
|
+
assert.match(error, /workflow tool surface not ready for run-uat/);
|
|
93
|
+
assert.match(error, /status is "failed"/);
|
|
94
|
+
assert.match(error, /gsd_uat_exec/);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("aborts on needs-auth (terminal — cannot self-heal)", () => {
|
|
98
|
+
const error = getToolSurfaceReadinessError({
|
|
99
|
+
unitType: "run-uat",
|
|
100
|
+
workflowServerName: SERVER,
|
|
101
|
+
observation: { tools: ["read", "bash"], mcpServers: [{ name: SERVER, status: "needs-auth" }] },
|
|
102
|
+
});
|
|
103
|
+
assert.ok(error, "expected a readiness error for needs-auth");
|
|
104
|
+
assert.match(error, /status is "needs-auth"/);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("aborts on disabled (terminal — cannot self-heal)", () => {
|
|
108
|
+
const error = getToolSurfaceReadinessError({
|
|
109
|
+
unitType: "run-uat",
|
|
110
|
+
workflowServerName: SERVER,
|
|
111
|
+
observation: { tools: ["read", "bash"], mcpServers: [{ name: SERVER, status: "disabled" }] },
|
|
112
|
+
});
|
|
113
|
+
assert.ok(error, "expected a readiness error for disabled");
|
|
114
|
+
assert.match(error, /status is "disabled"/);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("reports partially-registered surfaces even when the server says connected", () => {
|
|
118
|
+
const error = getToolSurfaceReadinessError({
|
|
119
|
+
unitType: "run-uat",
|
|
120
|
+
workflowServerName: SERVER,
|
|
121
|
+
observation: {
|
|
122
|
+
tools: [prefixed("gsd_uat_exec")],
|
|
123
|
+
mcpServers: [{ name: SERVER, status: "connected" }],
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
assert.ok(error, "expected a readiness error");
|
|
127
|
+
assert.match(error, /connected but has not registered/);
|
|
128
|
+
assert.match(error, /gsd_uat_result_save/);
|
|
129
|
+
assert.doesNotMatch(error, /gsd_uat_exec,/);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe("readiness error classification contract", () => {
|
|
134
|
+
const readinessError = getToolSurfaceReadinessError({
|
|
135
|
+
unitType: "run-uat",
|
|
136
|
+
workflowServerName: SERVER,
|
|
137
|
+
observation: { tools: [], mcpServers: [{ name: SERVER, status: "failed" }] },
|
|
138
|
+
})!;
|
|
139
|
+
|
|
140
|
+
test("auto-tool-tracking treats the readiness error as tool-unavailable", () => {
|
|
141
|
+
assert.equal(isToolUnavailableError(readinessError), true);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("error-classifier treats the readiness error as transient", () => {
|
|
145
|
+
const cls = classifyError(`Claude Code error: ${readinessError}`);
|
|
146
|
+
assert.equal(isTransient(cls), true);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("Recovery Classification maps the readiness error to tool-unavailable → retry", () => {
|
|
150
|
+
const recovery = classifyFailure({ error: new Error(readinessError), unitType: "run-uat", unitId: "M001" });
|
|
151
|
+
assert.equal(recovery.failureKind, "tool-unavailable");
|
|
152
|
+
assert.equal(recovery.action, "retry");
|
|
153
|
+
assert.equal(recovery.exitReason, "tool-unavailable");
|
|
154
|
+
});
|
|
155
|
+
});
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// gsd-pi — ADR-032 Unit Closeout module tests (Interactive Closeout adapter path).
|
|
2
|
+
//
|
|
3
|
+
// All git/preference/notification effects go through the injected deps seam —
|
|
4
|
+
// no real repos, no notification store state.
|
|
5
|
+
|
|
6
|
+
import test from "node:test";
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
closeUnit,
|
|
11
|
+
isUnitCloseoutTool,
|
|
12
|
+
runInteractiveUnitCloseout,
|
|
13
|
+
type UnitCloseoutDeps,
|
|
14
|
+
} from "../unit-closeout.ts";
|
|
15
|
+
|
|
16
|
+
interface DepsLog {
|
|
17
|
+
commits: Array<{ unitType: string; unitId: string }>;
|
|
18
|
+
notices: Array<{ message: string; severity: string }>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function makeDeps(overrides: {
|
|
22
|
+
isolation?: "none" | "worktree" | "branch";
|
|
23
|
+
branch?: string | null;
|
|
24
|
+
commitResult?: string | null | (() => string | null);
|
|
25
|
+
} = {}): { deps: UnitCloseoutDeps; log: DepsLog } {
|
|
26
|
+
const log: DepsLog = { commits: [], notices: [] };
|
|
27
|
+
const deps: UnitCloseoutDeps = {
|
|
28
|
+
isolationMode: () => overrides.isolation ?? "none",
|
|
29
|
+
currentBranch: () => (overrides.branch === undefined ? "main" : overrides.branch),
|
|
30
|
+
commit: (_basePath, unitType, unitId) => {
|
|
31
|
+
log.commits.push({ unitType, unitId });
|
|
32
|
+
const result = overrides.commitResult;
|
|
33
|
+
if (typeof result === "function") return result();
|
|
34
|
+
return result === undefined ? "chore(gsd): closeout" : result;
|
|
35
|
+
},
|
|
36
|
+
notify: (message, severity) => {
|
|
37
|
+
log.notices.push({ message, severity });
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
return { deps, log };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const BASE = "/tmp/closeout-test-project";
|
|
44
|
+
|
|
45
|
+
test("task boundary commits and stays quiet", () => {
|
|
46
|
+
const { deps, log } = makeDeps({ isolation: "worktree" });
|
|
47
|
+
const result = closeUnit(
|
|
48
|
+
{ basePath: BASE, unitType: "execute-task", unitId: "M001/S01/T01", boundary: "task", outcome: "complete" },
|
|
49
|
+
deps,
|
|
50
|
+
);
|
|
51
|
+
assert.equal(result.gitVerdict, "committed");
|
|
52
|
+
assert.equal(result.notice, undefined);
|
|
53
|
+
assert.deepEqual(log.commits, [{ unitType: "execute-task", unitId: "M001/S01/T01" }]);
|
|
54
|
+
assert.equal(log.notices.length, 0);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("milestone boundary under isolation none commits without a notice", () => {
|
|
58
|
+
const { deps, log } = makeDeps({ isolation: "none" });
|
|
59
|
+
const result = closeUnit(
|
|
60
|
+
{ basePath: BASE, unitType: "complete-milestone", unitId: "M001", boundary: "milestone", outcome: "complete" },
|
|
61
|
+
deps,
|
|
62
|
+
);
|
|
63
|
+
assert.equal(result.gitVerdict, "committed");
|
|
64
|
+
assert.equal(log.notices.length, 0);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("milestone boundary off-worktree under isolation worktree fails closed loudly", () => {
|
|
68
|
+
const { deps, log } = makeDeps({ isolation: "worktree", branch: "main" });
|
|
69
|
+
const result = closeUnit(
|
|
70
|
+
{ basePath: BASE, unitType: "complete-milestone", unitId: "M001", boundary: "milestone", outcome: "complete" },
|
|
71
|
+
deps,
|
|
72
|
+
);
|
|
73
|
+
assert.equal(result.gitVerdict, "isolation-bypassed");
|
|
74
|
+
assert.equal(log.notices.length, 1);
|
|
75
|
+
assert.equal(log.notices[0].severity, "warning");
|
|
76
|
+
assert.match(log.notices[0].message, /isolation preference was not honoured/);
|
|
77
|
+
assert.match(log.notices[0].message, /git\.isolation is "worktree"/);
|
|
78
|
+
assert.match(log.notices[0].message, /committed directly on "main"/);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("milestone boundary on a milestone branch defers the merge to worktree tooling", () => {
|
|
82
|
+
const { deps, log } = makeDeps({ isolation: "worktree", branch: "milestone/M001" });
|
|
83
|
+
const result = closeUnit(
|
|
84
|
+
{ basePath: BASE, unitType: "complete-milestone", unitId: "M001", boundary: "milestone", outcome: "complete" },
|
|
85
|
+
deps,
|
|
86
|
+
);
|
|
87
|
+
assert.equal(result.gitVerdict, "milestone-branch");
|
|
88
|
+
assert.equal(log.notices.length, 1);
|
|
89
|
+
assert.equal(log.notices[0].severity, "info");
|
|
90
|
+
assert.match(log.notices[0].message, /worktree merge/);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("clean tree records nothing-to-commit, and the bypass notice says so", () => {
|
|
94
|
+
const { deps, log } = makeDeps({ isolation: "branch", branch: "main", commitResult: null });
|
|
95
|
+
const result = closeUnit(
|
|
96
|
+
{ basePath: BASE, unitType: "complete-milestone", unitId: "M001", boundary: "milestone", outcome: "complete" },
|
|
97
|
+
deps,
|
|
98
|
+
);
|
|
99
|
+
assert.equal(result.gitVerdict, "isolation-bypassed");
|
|
100
|
+
assert.equal(result.commitMessage, null);
|
|
101
|
+
assert.match(log.notices[0].message, /nothing left to commit/);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("commit failure is surfaced, never thrown", () => {
|
|
105
|
+
const { deps, log } = makeDeps({
|
|
106
|
+
commitResult: () => {
|
|
107
|
+
throw new Error("index.lock exists");
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
const result = closeUnit(
|
|
111
|
+
{ basePath: BASE, unitType: "complete-milestone", unitId: "M001", boundary: "milestone", outcome: "complete" },
|
|
112
|
+
deps,
|
|
113
|
+
);
|
|
114
|
+
assert.equal(result.gitVerdict, "commit-failed");
|
|
115
|
+
assert.equal(log.notices[0].severity, "error");
|
|
116
|
+
assert.match(log.notices[0].message, /index\.lock/);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("re-entrancy is safe: a re-fire over an already-clean tree is nothing-to-commit", () => {
|
|
120
|
+
// No result cache — re-entrancy is absorbed by git itself. The second fire
|
|
121
|
+
// sees a clean tree (commit returns null) and records nothing-to-commit.
|
|
122
|
+
let firstFire = true;
|
|
123
|
+
const { deps, log } = makeDeps({
|
|
124
|
+
isolation: "worktree",
|
|
125
|
+
branch: "main",
|
|
126
|
+
commitResult: () => {
|
|
127
|
+
const committed = firstFire;
|
|
128
|
+
firstFire = false;
|
|
129
|
+
return committed ? "chore(gsd): closeout" : null;
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
const request = {
|
|
133
|
+
basePath: BASE,
|
|
134
|
+
unitType: "complete-milestone",
|
|
135
|
+
unitId: "M001",
|
|
136
|
+
boundary: "milestone" as const,
|
|
137
|
+
outcome: "complete" as const,
|
|
138
|
+
};
|
|
139
|
+
const first = closeUnit(request, deps);
|
|
140
|
+
const second = closeUnit(request, deps);
|
|
141
|
+
assert.equal(first.gitVerdict, "isolation-bypassed");
|
|
142
|
+
assert.equal(first.commitMessage, "chore(gsd): closeout");
|
|
143
|
+
assert.equal(second.gitVerdict, "isolation-bypassed");
|
|
144
|
+
assert.equal(second.commitMessage, null);
|
|
145
|
+
assert.match(second.notice ?? "", /nothing left to commit/);
|
|
146
|
+
assert.equal(log.commits.length, 2);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// ─── Interactive adapter mapping ──────────────────────────────────────────
|
|
150
|
+
|
|
151
|
+
test("isUnitCloseoutTool recognizes exactly the closeout tools", () => {
|
|
152
|
+
assert.equal(isUnitCloseoutTool("gsd_complete_milestone"), true);
|
|
153
|
+
assert.equal(isUnitCloseoutTool("gsd_save_gate_result"), false);
|
|
154
|
+
assert.equal(isUnitCloseoutTool("read"), false);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("interactive adapter is scoped to milestone boundaries — task/slice tools do not commit", () => {
|
|
158
|
+
const { deps, log } = makeDeps();
|
|
159
|
+
assert.equal(isUnitCloseoutTool("gsd_task_complete"), false);
|
|
160
|
+
assert.equal(isUnitCloseoutTool("gsd_slice_complete"), false);
|
|
161
|
+
assert.equal(
|
|
162
|
+
runInteractiveUnitCloseout(
|
|
163
|
+
{ basePath: BASE, canonicalToolName: "gsd_task_complete", input: { milestoneId: "M001", sliceId: "S02", taskId: "T03" } },
|
|
164
|
+
deps,
|
|
165
|
+
),
|
|
166
|
+
null,
|
|
167
|
+
);
|
|
168
|
+
assert.equal(
|
|
169
|
+
runInteractiveUnitCloseout(
|
|
170
|
+
{ basePath: BASE, canonicalToolName: "gsd_slice_complete", input: { milestoneId: "M001", sliceId: "S02" } },
|
|
171
|
+
deps,
|
|
172
|
+
),
|
|
173
|
+
null,
|
|
174
|
+
);
|
|
175
|
+
assert.equal(log.commits.length, 0);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("interactive adapter maps milestone tool input to canonical unit type", () => {
|
|
179
|
+
const { deps, log } = makeDeps();
|
|
180
|
+
const result = runInteractiveUnitCloseout(
|
|
181
|
+
{ basePath: BASE, canonicalToolName: "gsd_complete_milestone", input: { milestoneId: "M001" } },
|
|
182
|
+
deps,
|
|
183
|
+
);
|
|
184
|
+
assert.equal(result?.gitVerdict, "committed");
|
|
185
|
+
assert.deepEqual(log.commits, [{ unitType: "complete-milestone", unitId: "M001" }]);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test("interactive adapter accepts snake_case ids and milestone-only input", () => {
|
|
189
|
+
const { deps, log } = makeDeps();
|
|
190
|
+
const result = runInteractiveUnitCloseout(
|
|
191
|
+
{ basePath: BASE, canonicalToolName: "gsd_complete_milestone", input: { milestone_id: "M007" } },
|
|
192
|
+
deps,
|
|
193
|
+
);
|
|
194
|
+
assert.equal(result?.gitVerdict, "committed");
|
|
195
|
+
assert.deepEqual(log.commits, [{ unitType: "complete-milestone", unitId: "M007" }]);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("interactive adapter declines unidentifiable input instead of guessing", () => {
|
|
199
|
+
const { deps, log } = makeDeps();
|
|
200
|
+
assert.equal(
|
|
201
|
+
runInteractiveUnitCloseout({ basePath: BASE, canonicalToolName: "gsd_complete_milestone", input: {} }, deps),
|
|
202
|
+
null,
|
|
203
|
+
);
|
|
204
|
+
assert.equal(
|
|
205
|
+
runInteractiveUnitCloseout({ basePath: BASE, canonicalToolName: "not_a_closeout_tool", input: { milestoneId: "M001" } }, deps),
|
|
206
|
+
null,
|
|
207
|
+
);
|
|
208
|
+
assert.equal(log.commits.length, 0);
|
|
209
|
+
});
|
|
@@ -24,6 +24,7 @@ import type {
|
|
|
24
24
|
UnitContextManifest,
|
|
25
25
|
} from "../unit-context-manifest.ts";
|
|
26
26
|
import { KNOWN_UNIT_TYPES, UNIT_MANIFESTS } from "../unit-context-manifest.ts";
|
|
27
|
+
import { getUnitToolSurfaceContract } from "../unit-tool-contracts.ts";
|
|
27
28
|
import {
|
|
28
29
|
buildExecuteTaskPrompt,
|
|
29
30
|
buildGateEvaluatePrompt,
|
|
@@ -165,12 +166,32 @@ test("Context Mode composer: every known eligible unit renders its configured la
|
|
|
165
166
|
}
|
|
166
167
|
assert.ok(out.startsWith("## Context Mode"), `${unitType} should render standalone Context Mode heading`);
|
|
167
168
|
assert.match(out, new RegExp(`Lane: \\*\\*${laneLabelByMode[manifest.contextMode]} lane\\*\\*\\.`, "i"));
|
|
168
|
-
|
|
169
|
-
|
|
169
|
+
const forbidden = getUnitToolSurfaceContract(unitType)?.forbiddenGsdTools ?? {};
|
|
170
|
+
if ("gsd_exec" in forbidden) {
|
|
171
|
+
// Units that forbid gsd_exec (run-uat) have it stripped from their
|
|
172
|
+
// Claude Code dispatch surface; guidance steering to it produces
|
|
173
|
+
// "No such tool available" loops in the dispatched agent.
|
|
174
|
+
assert.doesNotMatch(out, /`gsd_exec`/, `${unitType} forbids gsd_exec; guidance must not steer to it`);
|
|
175
|
+
assert.doesNotMatch(out, /`gsd_exec_search`/, `${unitType} guidance must not steer to gsd_exec_search`);
|
|
176
|
+
assert.match(out, /`gsd_uat_exec`/, `${unitType} guidance should steer to gsd_uat_exec instead`);
|
|
177
|
+
} else {
|
|
178
|
+
assert.match(out, /`gsd_exec`/, `${unitType} should mention gsd_exec`);
|
|
179
|
+
assert.match(out, /`gsd_exec_search`/, `${unitType} should mention gsd_exec_search`);
|
|
180
|
+
}
|
|
170
181
|
assert.match(out, /`gsd_resume`/, `${unitType} should mention gsd_resume`);
|
|
171
182
|
}
|
|
172
183
|
});
|
|
173
184
|
|
|
185
|
+
test("Context Mode composer: run-uat guidance steers to gsd_uat_exec in both render modes", () => {
|
|
186
|
+
const nested = composeContextModeInstructions("run-uat", { enabled: true, renderMode: "nested" });
|
|
187
|
+
assert.match(nested, /^Context Mode \(verification lane\): /);
|
|
188
|
+
assert.match(nested, /`gsd_uat_exec`/);
|
|
189
|
+
assert.doesNotMatch(nested, /`gsd_exec`/);
|
|
190
|
+
const standalone = composeContextModeInstructions("run-uat", { enabled: true, renderMode: "standalone" });
|
|
191
|
+
assert.match(standalone, /`gsd_uat_exec`/);
|
|
192
|
+
assert.doesNotMatch(standalone, /`gsd_exec`/);
|
|
193
|
+
});
|
|
194
|
+
|
|
174
195
|
test("Context Mode composer: workflow-preferences and research-decision render no Context Mode block", () => {
|
|
175
196
|
assert.strictEqual(
|
|
176
197
|
composeContextModeInstructions("workflow-preferences", { enabled: true, renderMode: "standalone" }),
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// gsd-pi — ADR-033 Unit Registry parity guard.
|
|
2
|
+
//
|
|
3
|
+
// Pins every view derived from UNIT_REGISTRY to the exact values the
|
|
4
|
+
// hand-maintained tables held before the registry existed. A failure here
|
|
5
|
+
// means a registry edit changed a derived surface — intended changes update
|
|
6
|
+
// the pinned expectation in the same diff.
|
|
7
|
+
|
|
8
|
+
import test from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
KNOWN_UNIT_TYPES,
|
|
13
|
+
UNIT_REGISTRY,
|
|
14
|
+
EXECUTE_TASK_UNIT_TYPES,
|
|
15
|
+
SECTION_CLOSE_GATE_UNIT_TYPES,
|
|
16
|
+
getUnitDescriptor,
|
|
17
|
+
getUnitPhaseChain,
|
|
18
|
+
} from "../unit-registry.ts";
|
|
19
|
+
import {
|
|
20
|
+
AUTO_UNIT_SCOPED_TOOLS,
|
|
21
|
+
UNIT_TOOL_CONTRACTS,
|
|
22
|
+
getUnitToolSurfaceContract,
|
|
23
|
+
} from "../unit-tool-contracts.ts";
|
|
24
|
+
import { UNIT_MANIFESTS } from "../unit-context-manifest.ts";
|
|
25
|
+
import { phaseChainForUnit } from "../preferences-models.ts";
|
|
26
|
+
|
|
27
|
+
// ─── Pinned pre-registry values ───────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
const EXPECTED_KNOWN_UNIT_TYPES = [
|
|
30
|
+
"research-milestone",
|
|
31
|
+
"plan-milestone",
|
|
32
|
+
"discuss-milestone",
|
|
33
|
+
"validate-milestone",
|
|
34
|
+
"complete-milestone",
|
|
35
|
+
"research-slice",
|
|
36
|
+
"plan-slice",
|
|
37
|
+
"refine-slice",
|
|
38
|
+
"replan-slice",
|
|
39
|
+
"complete-slice",
|
|
40
|
+
"reassess-roadmap",
|
|
41
|
+
"execute-task",
|
|
42
|
+
"reactive-execute",
|
|
43
|
+
"run-uat",
|
|
44
|
+
"gate-evaluate",
|
|
45
|
+
"rewrite-docs",
|
|
46
|
+
"triage-captures",
|
|
47
|
+
"quick-task",
|
|
48
|
+
"workflow-preferences",
|
|
49
|
+
"discuss-project",
|
|
50
|
+
"discuss-requirements",
|
|
51
|
+
"research-decision",
|
|
52
|
+
"research-project",
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
// The contract table carried two keys KNOWN_UNIT_TYPES never had (variants)
|
|
56
|
+
// and lacked two it did have (sidecars without contracts).
|
|
57
|
+
const EXPECTED_CONTRACT_ONLY_TYPES = ["discuss-slice", "execute-task-simple"];
|
|
58
|
+
const EXPECTED_CONTRACT_LESS_TYPES = ["triage-captures", "quick-task"];
|
|
59
|
+
|
|
60
|
+
const EXPECTED_EXECUTE_TASK_SET = ["execute-task", "execute-task-simple", "reactive-execute"];
|
|
61
|
+
const EXPECTED_SECTION_CLOSE_SET = [
|
|
62
|
+
...EXPECTED_EXECUTE_TASK_SET,
|
|
63
|
+
"complete-slice",
|
|
64
|
+
"validate-milestone",
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
const EXPECTED_PHASE_CHAINS: Record<string, string[] | undefined> = {
|
|
68
|
+
"research-milestone": ["research"],
|
|
69
|
+
"research-slice": ["research"],
|
|
70
|
+
"research-project": ["research"],
|
|
71
|
+
"plan-milestone": ["planning"],
|
|
72
|
+
"plan-slice": ["planning"],
|
|
73
|
+
"refine-slice": ["planning"],
|
|
74
|
+
"replan-slice": ["planning"],
|
|
75
|
+
"discuss-milestone": ["discuss", "planning"],
|
|
76
|
+
"discuss-slice": ["discuss", "planning"],
|
|
77
|
+
"discuss-project": ["discuss", "planning"],
|
|
78
|
+
"discuss-requirements": ["discuss", "planning"],
|
|
79
|
+
"workflow-preferences": ["discuss", "planning"],
|
|
80
|
+
"research-decision": ["discuss", "planning"],
|
|
81
|
+
"execute-task": ["execution"],
|
|
82
|
+
"reactive-execute": ["execution"],
|
|
83
|
+
"execute-task-simple": ["execution_simple", "execution"],
|
|
84
|
+
"complete-slice": ["completion"],
|
|
85
|
+
"complete-milestone": ["completion"],
|
|
86
|
+
"worktree-merge": ["completion"],
|
|
87
|
+
"run-uat": ["uat", "completion"],
|
|
88
|
+
"reassess-roadmap": ["validation", "planning"],
|
|
89
|
+
"rewrite-docs": ["validation", "planning"],
|
|
90
|
+
"gate-evaluate": ["validation", "planning"],
|
|
91
|
+
"validate-milestone": ["validation", "planning"],
|
|
92
|
+
"triage-captures": undefined,
|
|
93
|
+
"quick-task": undefined,
|
|
94
|
+
subagent: ["subagent"],
|
|
95
|
+
"subagent/scout": ["subagent"],
|
|
96
|
+
"no-such-unit": undefined,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// ─── Derived-view parity ──────────────────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
test("KNOWN_UNIT_TYPES derives exactly the pre-registry list, in order", () => {
|
|
102
|
+
assert.deepEqual([...KNOWN_UNIT_TYPES], EXPECTED_KNOWN_UNIT_TYPES);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("UNIT_TOOL_CONTRACTS keeps the pre-registry key set, asymmetries included", () => {
|
|
106
|
+
const contractKeys = Object.keys(UNIT_TOOL_CONTRACTS);
|
|
107
|
+
for (const variant of EXPECTED_CONTRACT_ONLY_TYPES) {
|
|
108
|
+
assert.ok(contractKeys.includes(variant), `variant ${variant} must keep its contract`);
|
|
109
|
+
assert.ok(!KNOWN_UNIT_TYPES.includes(variant as never), `${variant} must stay out of KNOWN_UNIT_TYPES`);
|
|
110
|
+
}
|
|
111
|
+
for (const sidecar of EXPECTED_CONTRACT_LESS_TYPES) {
|
|
112
|
+
assert.ok(!contractKeys.includes(sidecar), `${sidecar} must stay contract-less`);
|
|
113
|
+
assert.equal(getUnitToolSurfaceContract(sidecar), undefined);
|
|
114
|
+
}
|
|
115
|
+
const expectedKeys = [
|
|
116
|
+
...EXPECTED_KNOWN_UNIT_TYPES.filter((t) => !EXPECTED_CONTRACT_LESS_TYPES.includes(t)),
|
|
117
|
+
...EXPECTED_CONTRACT_ONLY_TYPES,
|
|
118
|
+
].sort();
|
|
119
|
+
assert.deepEqual([...contractKeys].sort(), expectedKeys);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("scope-class Sets match the pre-registry hand-maintained Sets", () => {
|
|
123
|
+
assert.deepEqual([...EXECUTE_TASK_UNIT_TYPES].sort(), [...EXPECTED_EXECUTE_TASK_SET].sort());
|
|
124
|
+
assert.deepEqual([...SECTION_CLOSE_GATE_UNIT_TYPES].sort(), [...EXPECTED_SECTION_CLOSE_SET].sort());
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("phaseChainForUnit matches the pre-registry switch for every known input", () => {
|
|
128
|
+
for (const [unitType, expected] of Object.entries(EXPECTED_PHASE_CHAINS)) {
|
|
129
|
+
assert.deepEqual(
|
|
130
|
+
phaseChainForUnit(unitType),
|
|
131
|
+
expected,
|
|
132
|
+
`phase chain for ${unitType}`,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("AUTO_UNIT_SCOPED_TOOLS mirrors each contract's allowed tools", () => {
|
|
138
|
+
for (const [unitType, contract] of Object.entries(UNIT_TOOL_CONTRACTS)) {
|
|
139
|
+
assert.deepEqual(AUTO_UNIT_SCOPED_TOOLS[unitType], contract.allowedGsdTools);
|
|
140
|
+
}
|
|
141
|
+
assert.deepEqual(
|
|
142
|
+
Object.keys(AUTO_UNIT_SCOPED_TOOLS).sort(),
|
|
143
|
+
Object.keys(UNIT_TOOL_CONTRACTS).sort(),
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// ─── Registry-internal coherence ──────────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
test("every primary unit type has a manifest; manifests cover nothing else", () => {
|
|
150
|
+
const manifestKeys = Object.keys(UNIT_MANIFESTS).sort();
|
|
151
|
+
assert.deepEqual(manifestKeys, [...KNOWN_UNIT_TYPES].sort());
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("every registry row is reachable through the descriptor accessor", () => {
|
|
155
|
+
for (const unitType of Object.keys(UNIT_REGISTRY)) {
|
|
156
|
+
const descriptor = getUnitDescriptor(unitType);
|
|
157
|
+
assert.ok(descriptor, `descriptor for ${unitType}`);
|
|
158
|
+
assert.ok(["primary", "variant"].includes(descriptor.kind));
|
|
159
|
+
assert.ok(["execute-task", "section-close", "standard"].includes(descriptor.scopeClass));
|
|
160
|
+
assert.equal(getUnitPhaseChain(unitType), descriptor.phaseChain);
|
|
161
|
+
}
|
|
162
|
+
assert.equal(getUnitDescriptor("no-such-unit"), undefined);
|
|
163
|
+
});
|
|
@@ -495,8 +495,8 @@ test("workflow MCP launch config reaches mutation tools over stdio", async () =>
|
|
|
495
495
|
estimate: "10m",
|
|
496
496
|
files: ["src/resources/extensions/gsd/workflow-mcp.ts"],
|
|
497
497
|
verify: "node --test",
|
|
498
|
-
inputs: [
|
|
499
|
-
expectedOutput: ["
|
|
498
|
+
inputs: [],
|
|
499
|
+
expectedOutput: ["src/bridge-status.md"],
|
|
500
500
|
},
|
|
501
501
|
],
|
|
502
502
|
},
|
|
@@ -528,8 +528,8 @@ test("executePlanSlice writes task planning state and rendered plan artifacts",
|
|
|
528
528
|
estimate: "15m",
|
|
529
529
|
files: ["src/resources/extensions/gsd/tools/workflow-tool-executors.ts"],
|
|
530
530
|
verify: "node --test",
|
|
531
|
-
inputs: [
|
|
532
|
-
expectedOutput: ["
|
|
531
|
+
inputs: [],
|
|
532
|
+
expectedOutput: ["src/bridge-status.md"],
|
|
533
533
|
},
|
|
534
534
|
],
|
|
535
535
|
}, base));
|