@opengsd/gsd-pi 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -11
- package/dist/onboarding.js +11 -0
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/GSD-WORKFLOW.md +20 -25
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +118 -53
- package/dist/resources/extensions/cursor-cli/index.js +16 -0
- package/dist/resources/extensions/cursor-cli/models.js +63 -0
- package/dist/resources/extensions/cursor-cli/package.json +11 -0
- package/dist/resources/extensions/cursor-cli/readiness.js +128 -0
- package/dist/resources/extensions/cursor-cli/stream-adapter.js +353 -0
- package/dist/resources/extensions/gsd/activity-log.js +4 -1
- package/dist/resources/extensions/gsd/artifact-verification.js +25 -9
- package/dist/resources/extensions/gsd/auto/closeout.js +45 -42
- package/dist/resources/extensions/gsd/auto/finalize.js +9 -0
- package/dist/resources/extensions/gsd/auto/orchestrator.js +1 -1
- package/dist/resources/extensions/gsd/auto/run-unit.js +5 -2
- package/dist/resources/extensions/gsd/auto/schedule-wakeup.js +3 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +15 -5
- package/dist/resources/extensions/gsd/auto-model-selection.js +36 -18
- package/dist/resources/extensions/gsd/auto-prompts.js +76 -2
- package/dist/resources/extensions/gsd/auto-start.js +4 -3
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +4 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +38 -11
- package/dist/resources/extensions/gsd/auto.js +23 -3
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +114 -5
- package/dist/resources/extensions/gsd/bootstrap/schedule-wakeup-tool.js +58 -9
- package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +10 -2
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +14 -1
- package/dist/resources/extensions/gsd/codebase-generator.js +157 -19
- package/dist/resources/extensions/gsd/commands/handlers/core.js +40 -4
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +153 -1
- package/dist/resources/extensions/gsd/commands-worktree.js +11 -2
- package/dist/resources/extensions/gsd/db/queries.js +39 -0
- package/dist/resources/extensions/gsd/discussion-handoff.js +8 -3
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +8 -2
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +206 -14
- package/dist/resources/extensions/gsd/doctor-providers.js +38 -10
- package/dist/resources/extensions/gsd/doctor.js +93 -3
- package/dist/resources/extensions/gsd/exec-sandbox.js +6 -2
- package/dist/resources/extensions/gsd/flat-phase-migration.js +74 -23
- package/dist/resources/extensions/gsd/git-service.js +14 -18
- package/dist/resources/extensions/gsd/gsd-db.js +52 -0
- package/dist/resources/extensions/gsd/guidance.js +1 -0
- package/dist/resources/extensions/gsd/guided-flow.js +40 -2
- package/dist/resources/extensions/gsd/key-manager.js +1 -0
- package/dist/resources/extensions/gsd/markdown-renderer.js +25 -8
- package/dist/resources/extensions/gsd/migrate-external.js +24 -7
- package/dist/resources/extensions/gsd/milestone-merge-transaction.js +3 -3
- package/dist/resources/extensions/gsd/preferences-diagnostics.js +24 -1
- package/dist/resources/extensions/gsd/preferences-models.js +6 -4
- package/dist/resources/extensions/gsd/preferences-validation.js +25 -2
- package/dist/resources/extensions/gsd/preferences.js +29 -2
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -2
- package/dist/resources/extensions/gsd/redact-secrets.js +37 -0
- package/dist/resources/extensions/gsd/repo-identity.js +16 -0
- package/dist/resources/extensions/gsd/repository-registry.js +6 -1
- package/dist/resources/extensions/gsd/root-write-leak-guard.js +4 -2
- package/dist/resources/extensions/gsd/safe-fs.js +23 -2
- package/dist/resources/extensions/gsd/skills/gsd-headless/SKILL.md +7 -9
- package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -0
- package/dist/resources/extensions/gsd/tools/plan-task.js +76 -8
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +40 -0
- package/dist/resources/extensions/gsd/unit-context-manifest.js +5 -4
- package/dist/resources/extensions/gsd/unit-registry.js +27 -2
- package/dist/resources/extensions/gsd/unit-runtime.js +21 -0
- package/dist/resources/extensions/gsd/workspace.js +26 -8
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +67 -5
- package/dist/resources/extensions/gsd/worktree-manager.js +97 -4
- 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 +7 -7
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- 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/update/route.js +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 +7 -7
- 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/node_modules/@gsd/native/package.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/wizard.d.ts +8 -0
- package/dist/wizard.js +8 -1
- package/integrations/hermes/CONTEXT.md +47 -0
- package/integrations/hermes/README.md +21 -0
- package/integrations/hermes/__init__.py +31 -0
- package/integrations/hermes/docs/issue-1162-grilling.md +197 -0
- package/integrations/hermes/docs/setup.md +4 -0
- package/integrations/hermes/open_gsd_hermes/commands.py +141 -0
- package/integrations/hermes/open_gsd_hermes/config.py +2 -0
- package/integrations/hermes/open_gsd_hermes/gsd_client.py +594 -31
- package/integrations/hermes/open_gsd_hermes/notifications.py +10 -1
- package/integrations/hermes/plugin.yaml +4 -0
- package/integrations/hermes/tests/test_config.py +6 -0
- package/integrations/hermes/tests/test_directory_loader.py +37 -0
- package/integrations/hermes/tests/test_gsd_client_cache.py +3 -0
- package/integrations/hermes/tests/test_gsd_client_execute.py +35 -0
- package/integrations/hermes/tests/test_gsd_client_process_cwd.py +22 -0
- package/integrations/hermes/tests/test_gsd_client_timeout.py +98 -0
- package/integrations/hermes/tests/test_milestone_client.py +447 -0
- package/integrations/hermes/tests/test_new_milestone_command.py +313 -0
- package/integrations/hermes/tests/test_supervisor_fsm.py +40 -0
- package/package.json +8 -8
- package/packages/cloud-mcp-gateway/dist/auth-store.d.ts +1 -0
- package/packages/cloud-mcp-gateway/dist/auth-store.d.ts.map +1 -1
- package/packages/cloud-mcp-gateway/dist/auth-store.js +21 -1
- package/packages/cloud-mcp-gateway/dist/auth-store.js.map +1 -1
- package/packages/cloud-mcp-gateway/dist/auth-store.test.js +34 -0
- package/packages/cloud-mcp-gateway/dist/auth-store.test.js.map +1 -1
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/dist/cloud-runtime.d.ts +2 -0
- package/packages/daemon/dist/cloud-runtime.d.ts.map +1 -1
- package/packages/daemon/dist/cloud-runtime.js +25 -1
- package/packages/daemon/dist/cloud-runtime.js.map +1 -1
- package/packages/daemon/dist/cloud-runtime.test.js +51 -0
- package/packages/daemon/dist/cloud-runtime.test.js.map +1 -1
- package/packages/daemon/dist/message-batcher.d.ts +7 -0
- package/packages/daemon/dist/message-batcher.d.ts.map +1 -1
- package/packages/daemon/dist/message-batcher.js +13 -0
- package/packages/daemon/dist/message-batcher.js.map +1 -1
- package/packages/daemon/dist/message-batcher.test.js +48 -0
- package/packages/daemon/dist/message-batcher.test.js.map +1 -1
- package/packages/daemon/dist/session-manager.js +1 -1
- package/packages/daemon/dist/session-manager.js.map +1 -1
- package/packages/daemon/package.json +5 -5
- package/packages/gsd-agent-core/dist/compaction/compaction.d.ts +13 -1
- package/packages/gsd-agent-core/dist/compaction/compaction.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/compaction/compaction.js +15 -1
- package/packages/gsd-agent-core/dist/compaction/compaction.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-navigation.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-navigation.js +4 -1
- package/packages/gsd-agent-core/dist/session/agent-session-navigation.js.map +1 -1
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js +1 -1
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/dist/server.d.ts +18 -5
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +46 -5
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/session-manager.js +1 -1
- package/packages/mcp-server/dist/session-manager.js.map +1 -1
- package/packages/mcp-server/package.json +4 -4
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/env-api-keys.d.ts.map +1 -1
- package/packages/pi-ai/dist/env-api-keys.js +3 -0
- package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +2 -2
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager-types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.js +3 -0
- package/packages/pi-coding-agent/dist/core/session-manager.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/dist/rpc-client.js +1 -1
- package/packages/rpc-client/dist/rpc-client.js.map +1 -1
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/scripts/link-workspace-packages.cjs +14 -4
- package/src/resources/GSD-WORKFLOW.md +20 -25
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +130 -51
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +371 -39
- package/src/resources/extensions/cursor-cli/index.ts +18 -0
- package/src/resources/extensions/cursor-cli/models.ts +65 -0
- package/src/resources/extensions/cursor-cli/package.json +11 -0
- package/src/resources/extensions/cursor-cli/readiness.ts +143 -0
- package/src/resources/extensions/cursor-cli/stream-adapter.ts +440 -0
- package/src/resources/extensions/cursor-cli/tests/index.test.ts +54 -0
- package/src/resources/extensions/cursor-cli/tests/readiness.test.ts +29 -0
- package/src/resources/extensions/cursor-cli/tests/stream-adapter.test.ts +166 -0
- package/src/resources/extensions/gsd/activity-log.ts +4 -1
- package/src/resources/extensions/gsd/artifact-verification.ts +25 -9
- package/src/resources/extensions/gsd/auto/closeout.ts +54 -56
- package/src/resources/extensions/gsd/auto/finalize.ts +8 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
- package/src/resources/extensions/gsd/auto/orchestrator.ts +1 -1
- package/src/resources/extensions/gsd/auto/run-unit.ts +5 -2
- package/src/resources/extensions/gsd/auto/schedule-wakeup.ts +8 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +17 -5
- package/src/resources/extensions/gsd/auto-model-selection.ts +49 -18
- package/src/resources/extensions/gsd/auto-prompts.ts +73 -2
- package/src/resources/extensions/gsd/auto-start.ts +4 -3
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +4 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +50 -11
- package/src/resources/extensions/gsd/auto.ts +22 -3
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +128 -5
- package/src/resources/extensions/gsd/bootstrap/schedule-wakeup-tool.ts +80 -12
- package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +12 -2
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +23 -1
- package/src/resources/extensions/gsd/codebase-generator.ts +179 -18
- package/src/resources/extensions/gsd/commands/handlers/core.ts +39 -3
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +135 -1
- package/src/resources/extensions/gsd/commands-worktree.ts +13 -2
- package/src/resources/extensions/gsd/db/queries.ts +40 -0
- package/src/resources/extensions/gsd/discussion-handoff.ts +10 -2
- package/src/resources/extensions/gsd/docs/preferences-reference.md +8 -2
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +269 -18
- package/src/resources/extensions/gsd/doctor-providers.ts +39 -10
- package/src/resources/extensions/gsd/doctor-types.ts +5 -1
- package/src/resources/extensions/gsd/doctor.ts +95 -3
- package/src/resources/extensions/gsd/exec-sandbox.ts +6 -2
- package/src/resources/extensions/gsd/flat-phase-migration.ts +98 -22
- package/src/resources/extensions/gsd/git-service.ts +14 -17
- package/src/resources/extensions/gsd/gsd-db.ts +72 -0
- package/src/resources/extensions/gsd/guidance.ts +2 -0
- package/src/resources/extensions/gsd/guided-flow.ts +45 -2
- package/src/resources/extensions/gsd/key-manager.ts +1 -0
- package/src/resources/extensions/gsd/markdown-renderer.ts +25 -7
- package/src/resources/extensions/gsd/migrate-external.ts +23 -6
- package/src/resources/extensions/gsd/milestone-merge-transaction.ts +4 -4
- package/src/resources/extensions/gsd/model-router.ts +4 -5
- package/src/resources/extensions/gsd/preferences-diagnostics.ts +24 -0
- package/src/resources/extensions/gsd/preferences-models.ts +6 -3
- package/src/resources/extensions/gsd/preferences-validation.ts +28 -2
- package/src/resources/extensions/gsd/preferences.ts +38 -4
- package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -2
- package/src/resources/extensions/gsd/redact-secrets.ts +40 -0
- package/src/resources/extensions/gsd/repo-identity.ts +17 -0
- package/src/resources/extensions/gsd/repository-registry.ts +7 -1
- package/src/resources/extensions/gsd/root-write-leak-guard.ts +3 -2
- package/src/resources/extensions/gsd/safe-fs.ts +22 -2
- package/src/resources/extensions/gsd/skills/gsd-headless/SKILL.md +7 -9
- package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +131 -5
- package/src/resources/extensions/gsd/tests/auto-migrating-recovery.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +178 -0
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +43 -1
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/auto-remote-session-lock-cleanup.test.ts +41 -0
- package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +41 -0
- package/src/resources/extensions/gsd/tests/auto-session-scope.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/auto-timeout-recovery-harness-abort.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/batch-task-query.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/check-auto-start-ready-guard.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +365 -0
- package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +62 -1
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +138 -1
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +397 -5
- package/src/resources/extensions/gsd/tests/doctor-workspace.test.ts +207 -0
- package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +38 -1
- package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +87 -19
- package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +89 -0
- package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +122 -0
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/migrate-external-worktree.test.ts +116 -0
- package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +152 -22
- package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +50 -6
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +17 -0
- package/src/resources/extensions/gsd/tests/plan-task.test.ts +264 -1
- package/src/resources/extensions/gsd/tests/preferences.test.ts +150 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +142 -0
- package/src/resources/extensions/gsd/tests/recovery-verify-logs.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/redact-secrets.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/register-hooks-flat-phase-migration.test.ts +73 -0
- package/src/resources/extensions/gsd/tests/register-hooks-loop-guard-auto.test.ts +275 -0
- package/src/resources/extensions/gsd/tests/repository-registry.test.ts +41 -2
- package/src/resources/extensions/gsd/tests/require-slice-discussion-dispatch.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/root-write-leak-guard.test.ts +31 -9
- package/src/resources/extensions/gsd/tests/schedule-wakeup-tool.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/status-per-repo.test.ts +94 -0
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +66 -18
- package/src/resources/extensions/gsd/tests/unit-registry.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +52 -0
- package/src/resources/extensions/gsd/tests/validator-scope-parity.test.ts +53 -1
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +208 -1
- package/src/resources/extensions/gsd/tests/workspace.test.ts +18 -4
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +31 -1
- package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +32 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +107 -9
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +58 -0
- package/src/resources/extensions/gsd/unit-context-manifest.ts +11 -5
- package/src/resources/extensions/gsd/unit-registry.ts +34 -2
- package/src/resources/extensions/gsd/unit-runtime.ts +42 -0
- package/src/resources/extensions/gsd/workspace.ts +42 -8
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +135 -11
- package/src/resources/extensions/gsd/worktree-manager.ts +143 -5
- /package/dist/web/standalone/.next/static/{zQLcXm0Za8lexoXtgDpMG → bCg3aYrFN1DKZr2dqgE7L}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{zQLcXm0Za8lexoXtgDpMG → bCg3aYrFN1DKZr2dqgE7L}/_ssgManifest.js +0 -0
|
@@ -12,6 +12,7 @@ import { appendEvent } from "../workflow-events.js";
|
|
|
12
12
|
import { logWarning } from "../workflow-logger.js";
|
|
13
13
|
import { loadEffectiveGSDPreferences } from "../preferences.js";
|
|
14
14
|
import { validatePathOnlyPlanningFields, validatePlanningPathScope } from "../planning-path-scope.js";
|
|
15
|
+
import { createRepositoryRegistryFromPreferences, defaultRepositoryTargets, type RepositoryRegistry } from "../repository-registry.js";
|
|
15
16
|
import type { GateId } from "../types.js";
|
|
16
17
|
|
|
17
18
|
export interface PlanTaskParams {
|
|
@@ -26,6 +27,8 @@ export interface PlanTaskParams {
|
|
|
26
27
|
inputs: string[];
|
|
27
28
|
expectedOutput: string[];
|
|
28
29
|
observabilityImpact?: string;
|
|
30
|
+
/** Repository id(s) this task touches (parent workspace); omitted for single-repo projects. */
|
|
31
|
+
targetRepositories?: string[];
|
|
29
32
|
fullPlanMd?: string;
|
|
30
33
|
/** Optional caller-provided identity for audit trail */
|
|
31
34
|
actorName?: string;
|
|
@@ -40,6 +43,63 @@ export interface PlanTaskResult {
|
|
|
40
43
|
taskPlanPath: string;
|
|
41
44
|
}
|
|
42
45
|
|
|
46
|
+
function validateRepositoryTargetIds(field: string, value: unknown): string[] {
|
|
47
|
+
const ids = validateStringArray(value, field);
|
|
48
|
+
if (ids.length === 0) throw new Error(`${field} must include at least one repository id when provided`);
|
|
49
|
+
const deduped = Array.from(new Set(ids.map((id) => id.trim()).filter(Boolean)));
|
|
50
|
+
if (deduped.length === 0) throw new Error(`${field} must include at least one repository id when provided`);
|
|
51
|
+
return deduped;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function validateReferencedRepositories(
|
|
55
|
+
targetRepositories: string[] | undefined,
|
|
56
|
+
registry: RepositoryRegistry,
|
|
57
|
+
): string | null {
|
|
58
|
+
if (!targetRepositories) return null;
|
|
59
|
+
const known = new Set(registry.repositories.map((repo) => repo.id));
|
|
60
|
+
const missing = targetRepositories.filter((id) => !known.has(id));
|
|
61
|
+
if (missing.length === 0) return null;
|
|
62
|
+
return `unknown targetRepositories: ${missing.join(", ")}. Declared repositories: ${Array.from(known).join(", ")}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function resolveAllowedRootsForPathScope(
|
|
66
|
+
targetRepositories: string[],
|
|
67
|
+
registry: RepositoryRegistry,
|
|
68
|
+
): string[] {
|
|
69
|
+
if (targetRepositories.length === 0) return [registry.projectRoot];
|
|
70
|
+
const roots = targetRepositories
|
|
71
|
+
.map((id) => registry.byId.get(id)?.root)
|
|
72
|
+
.filter((root): root is string => typeof root === "string");
|
|
73
|
+
return roots.length > 0 ? roots : [registry.projectRoot];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function validatePathScopeForTargetRepositories(
|
|
77
|
+
params: PlanTaskParams,
|
|
78
|
+
basePath: string,
|
|
79
|
+
registry: RepositoryRegistry,
|
|
80
|
+
targetRepositories: string[],
|
|
81
|
+
): string | null {
|
|
82
|
+
return validatePlanningPathScope(
|
|
83
|
+
basePath,
|
|
84
|
+
[
|
|
85
|
+
{ field: "files", values: params.files },
|
|
86
|
+
{ field: "inputs", values: params.inputs },
|
|
87
|
+
{ field: "expectedOutput", values: params.expectedOutput },
|
|
88
|
+
],
|
|
89
|
+
resolveAllowedRootsForPathScope(targetRepositories, registry),
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function resolveEffectiveTargetRepositories(
|
|
94
|
+
taskTargetRepositories: string[] | undefined,
|
|
95
|
+
sliceTargetRepositories: string[] | undefined,
|
|
96
|
+
defaultTargets: string[],
|
|
97
|
+
): string[] {
|
|
98
|
+
if (taskTargetRepositories) return taskTargetRepositories;
|
|
99
|
+
if (sliceTargetRepositories?.length) return sliceTargetRepositories;
|
|
100
|
+
return defaultTargets;
|
|
101
|
+
}
|
|
102
|
+
|
|
43
103
|
function validateParams(params: PlanTaskParams): PlanTaskParams {
|
|
44
104
|
if (!isNonEmptyString(params?.milestoneId)) throw new Error("milestoneId is required");
|
|
45
105
|
if (!isNonEmptyString(params?.sliceId)) throw new Error("sliceId is required");
|
|
@@ -57,6 +117,9 @@ function validateParams(params: PlanTaskParams): PlanTaskParams {
|
|
|
57
117
|
files: validateStringArray(params.files, "files"),
|
|
58
118
|
inputs: validateStringArray(params.inputs, "inputs"),
|
|
59
119
|
expectedOutput: validateStringArray(params.expectedOutput, "expectedOutput"),
|
|
120
|
+
...(params.targetRepositories !== undefined
|
|
121
|
+
? { targetRepositories: validateRepositoryTargetIds("targetRepositories", params.targetRepositories) }
|
|
122
|
+
: {}),
|
|
60
123
|
};
|
|
61
124
|
}
|
|
62
125
|
|
|
@@ -84,23 +147,19 @@ export async function handlePlanTask(
|
|
|
84
147
|
return { error: `validation failed: ${pathOnlyError}` };
|
|
85
148
|
}
|
|
86
149
|
|
|
87
|
-
const pathScopeError = validatePlanningPathScope(basePath, [
|
|
88
|
-
{ field: "files", values: params.files },
|
|
89
|
-
{ field: "inputs", values: params.inputs },
|
|
90
|
-
{ field: "expectedOutput", values: params.expectedOutput },
|
|
91
|
-
]);
|
|
92
|
-
if (pathScopeError) {
|
|
93
|
-
return { error: `validation failed: ${pathScopeError}` };
|
|
94
|
-
}
|
|
95
|
-
|
|
96
150
|
let taskGates: GateId[];
|
|
151
|
+
let repositoryRegistry: RepositoryRegistry;
|
|
97
152
|
try {
|
|
98
153
|
taskGates = resolveTaskGates(basePath);
|
|
154
|
+
const loaded = loadEffectiveGSDPreferences(basePath);
|
|
155
|
+
repositoryRegistry = createRepositoryRegistryFromPreferences(basePath, loaded?.preferences);
|
|
99
156
|
} catch (err) {
|
|
100
157
|
const message = err instanceof Error ? err.message : String(err);
|
|
101
158
|
return { error: `validation failed: ${message}` };
|
|
102
159
|
}
|
|
103
160
|
|
|
161
|
+
const defaultTargets = defaultRepositoryTargets(repositoryRegistry);
|
|
162
|
+
|
|
104
163
|
// ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
|
|
105
164
|
// Guards must be inside the transaction so the state they check cannot
|
|
106
165
|
// change between the read and the write (#2723).
|
|
@@ -124,6 +183,44 @@ export async function handlePlanTask(
|
|
|
124
183
|
return;
|
|
125
184
|
}
|
|
126
185
|
|
|
186
|
+
let effectiveTargetRepositories = resolveEffectiveTargetRepositories(
|
|
187
|
+
params.targetRepositories,
|
|
188
|
+
parentSlice.target_repositories,
|
|
189
|
+
defaultTargets,
|
|
190
|
+
);
|
|
191
|
+
const repoValidationError = validateReferencedRepositories(effectiveTargetRepositories, repositoryRegistry);
|
|
192
|
+
if (repoValidationError) {
|
|
193
|
+
guardError = `validation failed: ${repoValidationError}`;
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let pathScopeError = validatePathScopeForTargetRepositories(
|
|
198
|
+
params,
|
|
199
|
+
basePath,
|
|
200
|
+
repositoryRegistry,
|
|
201
|
+
effectiveTargetRepositories,
|
|
202
|
+
);
|
|
203
|
+
const storedTaskTargets = existingTask?.target_repositories?.length
|
|
204
|
+
? existingTask.target_repositories
|
|
205
|
+
: undefined;
|
|
206
|
+
// Omitted targetRepositories inherit the current slice default first.
|
|
207
|
+
// Fall back to the stored task target only when that inherited scope
|
|
208
|
+
// rejects the replan paths, preserving explicit per-task parent-root work.
|
|
209
|
+
if (pathScopeError && params.targetRepositories === undefined && storedTaskTargets) {
|
|
210
|
+
const storedRepoValidationError = validateReferencedRepositories(storedTaskTargets, repositoryRegistry);
|
|
211
|
+
const storedPathScopeError = storedRepoValidationError
|
|
212
|
+
? storedRepoValidationError
|
|
213
|
+
: validatePathScopeForTargetRepositories(params, basePath, repositoryRegistry, storedTaskTargets);
|
|
214
|
+
if (!storedPathScopeError) {
|
|
215
|
+
effectiveTargetRepositories = storedTaskTargets;
|
|
216
|
+
pathScopeError = null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (pathScopeError) {
|
|
220
|
+
guardError = `validation failed: ${pathScopeError}`;
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
127
224
|
if (!existingTask) {
|
|
128
225
|
insertTask({
|
|
129
226
|
id: params.taskId,
|
|
@@ -143,6 +240,7 @@ export async function handlePlanTask(
|
|
|
143
240
|
expectedOutput: params.expectedOutput,
|
|
144
241
|
observabilityImpact: params.observabilityImpact ?? "",
|
|
145
242
|
fullPlanMd: params.fullPlanMd,
|
|
243
|
+
targetRepositories: effectiveTargetRepositories,
|
|
146
244
|
});
|
|
147
245
|
for (const gid of taskGates) {
|
|
148
246
|
insertGateRow({ milestoneId: params.milestoneId, sliceId: params.sliceId, gateId: gid, scope: "task", taskId: params.taskId });
|
|
@@ -53,11 +53,13 @@ import { loadEffectiveGSDPreferences } from "../preferences.js";
|
|
|
53
53
|
import { parseProject } from "../schemas/parsers.js";
|
|
54
54
|
import { autoSession, getAutoRuntimeSnapshot, isAutoActive } from "../auto-runtime-state.js";
|
|
55
55
|
import { renderPlanFromDb } from "../markdown-renderer.js";
|
|
56
|
+
import { readUnitHarnessAbort, type UnitHarnessAbortRecord } from "../unit-runtime.js";
|
|
56
57
|
import {
|
|
57
58
|
prepareUatRun,
|
|
58
59
|
saveUatAttemptArtifact,
|
|
59
60
|
type UatResultSaveParams,
|
|
60
61
|
} from "../uat-run.js";
|
|
62
|
+
import { appendNotification } from "../notification-store.js";
|
|
61
63
|
import { registerAutoWorker, markWorkerStopping, getAutoWorker } from "../db/auto-workers.js";
|
|
62
64
|
import {
|
|
63
65
|
claimMilestoneLease,
|
|
@@ -116,6 +118,48 @@ function blockIfWrongAutoUnit(requiredUnitType: string, operation: string): Tool
|
|
|
116
118
|
};
|
|
117
119
|
}
|
|
118
120
|
|
|
121
|
+
function blockIfHarnessAbortedUnit(operation: string, basePath: string): ToolExecutionResult | null {
|
|
122
|
+
const snapshot = getAutoRuntimeSnapshot();
|
|
123
|
+
if (!snapshot.active || !snapshot.currentUnit) return null;
|
|
124
|
+
|
|
125
|
+
const abort = readUnitHarnessAbort(
|
|
126
|
+
snapshot.basePath || basePath,
|
|
127
|
+
snapshot.currentUnit.type,
|
|
128
|
+
snapshot.currentUnit.id,
|
|
129
|
+
snapshot.currentUnit.startedAt,
|
|
130
|
+
);
|
|
131
|
+
if (!abort) return null;
|
|
132
|
+
|
|
133
|
+
return harnessAbortResult(operation, snapshot.currentUnit.type, snapshot.currentUnit.id, abort);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function harnessAbortResult(
|
|
137
|
+
operation: string,
|
|
138
|
+
unitType: string,
|
|
139
|
+
unitId: string,
|
|
140
|
+
abort: UnitHarnessAbortRecord,
|
|
141
|
+
): ToolExecutionResult {
|
|
142
|
+
const toolText = abort.toolName ? ` while calling ${abort.toolName}` : "";
|
|
143
|
+
const message =
|
|
144
|
+
`Retryable harness abort: ${unitType} ${unitId} was truncated by ${abort.kind}${toolText}. ` +
|
|
145
|
+
"This gate result was not saved; leave the gate pending so auto-mode can rerun it.";
|
|
146
|
+
return {
|
|
147
|
+
content: [{ type: "text", text: message }],
|
|
148
|
+
details: {
|
|
149
|
+
operation,
|
|
150
|
+
error: "harness_aborted_needs_retry",
|
|
151
|
+
retryable: true,
|
|
152
|
+
unitType,
|
|
153
|
+
unitId,
|
|
154
|
+
harnessAbortKind: abort.kind,
|
|
155
|
+
harnessAbortReason: abort.reason,
|
|
156
|
+
harnessAbortToolName: abort.toolName,
|
|
157
|
+
harnessAbortCount: abort.count,
|
|
158
|
+
},
|
|
159
|
+
isError: true,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
119
163
|
function milestoneLeaseConflictResult(
|
|
120
164
|
milestoneId: string,
|
|
121
165
|
byWorker: string,
|
|
@@ -1135,6 +1179,9 @@ export async function executeSaveGateResult(
|
|
|
1135
1179
|
params: SaveGateResultParams,
|
|
1136
1180
|
basePath: string = process.cwd(),
|
|
1137
1181
|
): Promise<ToolExecutionResult> {
|
|
1182
|
+
const harnessAbort = blockIfHarnessAbortedUnit("save_gate_result", basePath);
|
|
1183
|
+
if (harnessAbort) return harnessAbort;
|
|
1184
|
+
|
|
1138
1185
|
const dbAvailable = await ensureDbOpen(basePath);
|
|
1139
1186
|
if (!dbAvailable) {
|
|
1140
1187
|
return {
|
|
@@ -1206,6 +1253,9 @@ export async function executeUatResultSave(
|
|
|
1206
1253
|
const unitGuard = blockIfWrongAutoUnit("run-uat", "save_uat_result");
|
|
1207
1254
|
if (unitGuard) return unitGuard;
|
|
1208
1255
|
|
|
1256
|
+
const harnessAbort = blockIfHarnessAbortedUnit("save_uat_result", basePath);
|
|
1257
|
+
if (harnessAbort) return harnessAbort;
|
|
1258
|
+
|
|
1209
1259
|
const dbAvailable = await ensureDbOpen(basePath);
|
|
1210
1260
|
if (!dbAvailable) return errorResult("save_uat_result", "GSD database is not available.", "db_unavailable");
|
|
1211
1261
|
|
|
@@ -1268,6 +1318,14 @@ export async function executeUatResultSave(
|
|
|
1268
1318
|
evaluatedAt: run.evaluatedAt,
|
|
1269
1319
|
});
|
|
1270
1320
|
invalidateStateCache();
|
|
1321
|
+
if (run.hasHuman) {
|
|
1322
|
+
appendNotification(
|
|
1323
|
+
`UAT for ${run.params.milestoneId}/${run.params.sliceId} has NEEDS-HUMAN checks awaiting human validation`,
|
|
1324
|
+
"warning",
|
|
1325
|
+
"notify",
|
|
1326
|
+
{ kind: "uat-needs-human", scope: `${run.params.milestoneId}/${run.params.sliceId}` },
|
|
1327
|
+
);
|
|
1328
|
+
}
|
|
1271
1329
|
const savedText = `UAT result saved for ${run.params.milestoneId}/${run.params.sliceId}: ${run.params.verdict}`;
|
|
1272
1330
|
return {
|
|
1273
1331
|
content: [{
|
|
@@ -140,6 +140,10 @@ export type ContextModePolicy =
|
|
|
140
140
|
* restricted to .gsd/**. Subagent dispatch is denied unless
|
|
141
141
|
* `allowedSubagents` opts a unit into controlled read-only
|
|
142
142
|
* specialist delegation.
|
|
143
|
+
* - "workflow-only"
|
|
144
|
+
* — Read tools + GSD workflow tools only. No generic writes
|
|
145
|
+
* and no generic shell; used by lanes whose durable output
|
|
146
|
+
* must be produced by a dedicated DB-backed workflow tool.
|
|
143
147
|
*
|
|
144
148
|
* The allowlist for "docs" is declared per-manifest rather than hardcoded so
|
|
145
149
|
* projects with non-standard doc layouts can extend it without forking the
|
|
@@ -152,7 +156,8 @@ export type ToolsPolicy =
|
|
|
152
156
|
| { readonly mode: "planning" }
|
|
153
157
|
| { readonly mode: "planning-dispatch"; readonly allowedSubagents: readonly string[] }
|
|
154
158
|
| { readonly mode: "docs"; readonly allowedPathGlobs: readonly string[] }
|
|
155
|
-
| { readonly mode: "verification"; readonly allowedSubagents?: readonly string[] }
|
|
159
|
+
| { readonly mode: "verification"; readonly allowedSubagents?: readonly string[] }
|
|
160
|
+
| { readonly mode: "workflow-only"; readonly allowedSubagents?: readonly string[] };
|
|
156
161
|
|
|
157
162
|
// ─── Computed-artifact registry (#4924 v2 contract) ───────────────────────
|
|
158
163
|
|
|
@@ -405,9 +410,10 @@ export const UNIT_MANIFESTS: Record<UnitType, UnitContextManifest> = {
|
|
|
405
410
|
codebaseMap: false,
|
|
406
411
|
preferences: "active-only",
|
|
407
412
|
contextMode: "verification",
|
|
408
|
-
// Validation
|
|
409
|
-
//
|
|
410
|
-
|
|
413
|
+
// Validation must persist its durable output through gsd_validate_milestone;
|
|
414
|
+
// generic write/bash tools can create filesystem artifacts that bypass DB
|
|
415
|
+
// state and trigger destructive recovery loops.
|
|
416
|
+
tools: { mode: "workflow-only", allowedSubagents: ["reviewer", "security", "tester"] },
|
|
411
417
|
artifacts: {
|
|
412
418
|
inline: ["roadmap", "requirements", "templates"],
|
|
413
419
|
excerpt: ["slice-summary", "slice-assessment"],
|
|
@@ -774,7 +780,7 @@ export function compileSubagentPermissionContract(
|
|
|
774
780
|
return { allowed: true, allowedSubagents: ["*"], toolsMode: policy.mode };
|
|
775
781
|
}
|
|
776
782
|
if (
|
|
777
|
-
(policy.mode === "planning-dispatch" || policy.mode === "verification") &&
|
|
783
|
+
(policy.mode === "planning-dispatch" || policy.mode === "verification" || policy.mode === "workflow-only") &&
|
|
778
784
|
Array.isArray(policy.allowedSubagents)
|
|
779
785
|
) {
|
|
780
786
|
return {
|
|
@@ -20,8 +20,9 @@
|
|
|
20
20
|
//
|
|
21
21
|
// Not yet declared here (remaining ADR-033 steps): the manifest data
|
|
22
22
|
// (`UNIT_MANIFESTS` stays in unit-context-manifest.ts, already type-enforced
|
|
23
|
-
// against the registry's `UnitType`)
|
|
24
|
-
//
|
|
23
|
+
// against the registry's `UnitType`). Prompt-template association is declared
|
|
24
|
+
// for direct one-template units; conditional/composite prompt selection stays
|
|
25
|
+
// with the prompt builders until it has a richer registry shape.
|
|
25
26
|
|
|
26
27
|
import type { CanonicalWorkflowToolName } from "@opengsd/contracts";
|
|
27
28
|
import { BROWSER_CONTRACT_TOOL_NAMES } from "../shared/browser-contract.js";
|
|
@@ -61,6 +62,12 @@ export interface UnitDescriptor {
|
|
|
61
62
|
* resolves against session defaults).
|
|
62
63
|
*/
|
|
63
64
|
readonly phaseChain: readonly GSDModelPhaseKey[] | null;
|
|
65
|
+
/**
|
|
66
|
+
* Prompt template id passed to loadPrompt for units with a direct
|
|
67
|
+
* one-template builder. Omitted for conditional, composite, or not-yet
|
|
68
|
+
* verified prompt associations.
|
|
69
|
+
*/
|
|
70
|
+
readonly promptTemplate?: string;
|
|
64
71
|
/** `null` = the unit has no scoped gsd-tool contract. */
|
|
65
72
|
readonly toolContract: UnitToolSurfaceContract | null;
|
|
66
73
|
}
|
|
@@ -97,6 +104,7 @@ export const UNIT_REGISTRY = {
|
|
|
97
104
|
kind: "primary",
|
|
98
105
|
scopeClass: "standard",
|
|
99
106
|
phaseChain: ["research"],
|
|
107
|
+
promptTemplate: "research-milestone",
|
|
100
108
|
toolContract: {
|
|
101
109
|
allowedGsdTools: [
|
|
102
110
|
"gsd_summary_save",
|
|
@@ -112,6 +120,7 @@ export const UNIT_REGISTRY = {
|
|
|
112
120
|
kind: "primary",
|
|
113
121
|
scopeClass: "standard",
|
|
114
122
|
phaseChain: ["planning"],
|
|
123
|
+
promptTemplate: "plan-milestone",
|
|
115
124
|
toolContract: {
|
|
116
125
|
allowedGsdTools: [
|
|
117
126
|
"gsd_milestone_status",
|
|
@@ -160,6 +169,7 @@ export const UNIT_REGISTRY = {
|
|
|
160
169
|
kind: "primary",
|
|
161
170
|
scopeClass: "section-close",
|
|
162
171
|
phaseChain: ["validation", "planning"],
|
|
172
|
+
promptTemplate: "validate-milestone",
|
|
163
173
|
toolContract: {
|
|
164
174
|
allowedGsdTools: [
|
|
165
175
|
"gsd_milestone_status",
|
|
@@ -177,6 +187,7 @@ export const UNIT_REGISTRY = {
|
|
|
177
187
|
kind: "primary",
|
|
178
188
|
scopeClass: "standard",
|
|
179
189
|
phaseChain: ["completion"],
|
|
190
|
+
promptTemplate: "complete-milestone",
|
|
180
191
|
toolContract: {
|
|
181
192
|
allowedGsdTools: [
|
|
182
193
|
"gsd_milestone_status",
|
|
@@ -200,6 +211,7 @@ export const UNIT_REGISTRY = {
|
|
|
200
211
|
kind: "primary",
|
|
201
212
|
scopeClass: "standard",
|
|
202
213
|
phaseChain: ["research"],
|
|
214
|
+
promptTemplate: "research-slice",
|
|
203
215
|
toolContract: {
|
|
204
216
|
allowedGsdTools: [
|
|
205
217
|
"gsd_milestone_status",
|
|
@@ -216,6 +228,7 @@ export const UNIT_REGISTRY = {
|
|
|
216
228
|
kind: "primary",
|
|
217
229
|
scopeClass: "standard",
|
|
218
230
|
phaseChain: ["planning"],
|
|
231
|
+
promptTemplate: "plan-slice",
|
|
219
232
|
toolContract: {
|
|
220
233
|
allowedGsdTools: [
|
|
221
234
|
"gsd_milestone_status",
|
|
@@ -234,6 +247,7 @@ export const UNIT_REGISTRY = {
|
|
|
234
247
|
kind: "primary",
|
|
235
248
|
scopeClass: "standard",
|
|
236
249
|
phaseChain: ["planning"],
|
|
250
|
+
promptTemplate: "refine-slice",
|
|
237
251
|
toolContract: {
|
|
238
252
|
allowedGsdTools: [
|
|
239
253
|
"gsd_milestone_status",
|
|
@@ -250,6 +264,7 @@ export const UNIT_REGISTRY = {
|
|
|
250
264
|
kind: "primary",
|
|
251
265
|
scopeClass: "standard",
|
|
252
266
|
phaseChain: ["planning"],
|
|
267
|
+
promptTemplate: "replan-slice",
|
|
253
268
|
toolContract: {
|
|
254
269
|
allowedGsdTools: ["gsd_replan_slice", "gsd_decision_save"],
|
|
255
270
|
requiredWorkflowTools: ["gsd_replan_slice"],
|
|
@@ -259,6 +274,7 @@ export const UNIT_REGISTRY = {
|
|
|
259
274
|
kind: "primary",
|
|
260
275
|
scopeClass: "section-close",
|
|
261
276
|
phaseChain: ["completion"],
|
|
277
|
+
promptTemplate: "complete-slice",
|
|
262
278
|
toolContract: {
|
|
263
279
|
allowedGsdTools: [
|
|
264
280
|
"gsd_milestone_status",
|
|
@@ -293,6 +309,7 @@ export const UNIT_REGISTRY = {
|
|
|
293
309
|
kind: "primary",
|
|
294
310
|
scopeClass: "standard",
|
|
295
311
|
phaseChain: ["validation", "planning"],
|
|
312
|
+
promptTemplate: "reassess-roadmap",
|
|
296
313
|
toolContract: {
|
|
297
314
|
allowedGsdTools: ["gsd_milestone_status", "gsd_reassess_roadmap"],
|
|
298
315
|
requiredWorkflowTools: ["gsd_milestone_status", "gsd_reassess_roadmap"],
|
|
@@ -302,6 +319,7 @@ export const UNIT_REGISTRY = {
|
|
|
302
319
|
kind: "primary",
|
|
303
320
|
scopeClass: "execute-task",
|
|
304
321
|
phaseChain: ["execution"],
|
|
322
|
+
promptTemplate: "execute-task",
|
|
305
323
|
toolContract: {
|
|
306
324
|
allowedGsdTools: [
|
|
307
325
|
"gsd_task_complete",
|
|
@@ -346,6 +364,7 @@ export const UNIT_REGISTRY = {
|
|
|
346
364
|
kind: "primary",
|
|
347
365
|
scopeClass: "execute-task",
|
|
348
366
|
phaseChain: ["execution"],
|
|
367
|
+
promptTemplate: "reactive-execute",
|
|
349
368
|
toolContract: {
|
|
350
369
|
allowedGsdTools: [
|
|
351
370
|
"gsd_milestone_status",
|
|
@@ -361,6 +380,7 @@ export const UNIT_REGISTRY = {
|
|
|
361
380
|
kind: "primary",
|
|
362
381
|
scopeClass: "standard",
|
|
363
382
|
phaseChain: ["uat", "completion"],
|
|
383
|
+
promptTemplate: "run-uat",
|
|
364
384
|
toolContract: {
|
|
365
385
|
allowedGsdTools: [...RUN_UAT_WORKFLOW_TOOL_NAMES, "subagent"],
|
|
366
386
|
requiredWorkflowTools: [...RUN_UAT_WORKFLOW_TOOL_NAMES],
|
|
@@ -375,6 +395,7 @@ export const UNIT_REGISTRY = {
|
|
|
375
395
|
kind: "primary",
|
|
376
396
|
scopeClass: "standard",
|
|
377
397
|
phaseChain: ["validation", "planning"],
|
|
398
|
+
promptTemplate: "gate-evaluate",
|
|
378
399
|
toolContract: {
|
|
379
400
|
allowedGsdTools: ["gsd_save_gate_result"],
|
|
380
401
|
requiredWorkflowTools: ["gsd_save_gate_result"],
|
|
@@ -384,6 +405,7 @@ export const UNIT_REGISTRY = {
|
|
|
384
405
|
kind: "primary",
|
|
385
406
|
scopeClass: "standard",
|
|
386
407
|
phaseChain: ["validation", "planning"],
|
|
408
|
+
promptTemplate: "rewrite-docs",
|
|
387
409
|
toolContract: {
|
|
388
410
|
allowedGsdTools: ["gsd_summary_save", "gsd_decision_save"],
|
|
389
411
|
requiredWorkflowTools: [],
|
|
@@ -408,6 +430,7 @@ export const UNIT_REGISTRY = {
|
|
|
408
430
|
kind: "primary",
|
|
409
431
|
scopeClass: "standard",
|
|
410
432
|
phaseChain: ["discuss", "planning"],
|
|
433
|
+
promptTemplate: "guided-workflow-preferences",
|
|
411
434
|
toolContract: {
|
|
412
435
|
allowedGsdTools: ["gsd_summary_save"],
|
|
413
436
|
requiredWorkflowTools: [],
|
|
@@ -417,6 +440,7 @@ export const UNIT_REGISTRY = {
|
|
|
417
440
|
kind: "primary",
|
|
418
441
|
scopeClass: "standard",
|
|
419
442
|
phaseChain: ["discuss", "planning"],
|
|
443
|
+
promptTemplate: "guided-discuss-project",
|
|
420
444
|
toolContract: {
|
|
421
445
|
allowedGsdTools: ["gsd_summary_save", "gsd_decision_save", "gsd_requirement_save"],
|
|
422
446
|
requiredWorkflowTools: ["ask_user_questions", "gsd_summary_save"],
|
|
@@ -426,6 +450,7 @@ export const UNIT_REGISTRY = {
|
|
|
426
450
|
kind: "primary",
|
|
427
451
|
scopeClass: "standard",
|
|
428
452
|
phaseChain: ["discuss", "planning"],
|
|
453
|
+
promptTemplate: "guided-discuss-requirements",
|
|
429
454
|
toolContract: {
|
|
430
455
|
allowedGsdTools: ["gsd_requirement_save", "gsd_summary_save"],
|
|
431
456
|
requiredWorkflowTools: ["ask_user_questions", "gsd_requirement_save", "gsd_summary_save"],
|
|
@@ -435,6 +460,7 @@ export const UNIT_REGISTRY = {
|
|
|
435
460
|
kind: "primary",
|
|
436
461
|
scopeClass: "standard",
|
|
437
462
|
phaseChain: ["discuss", "planning"],
|
|
463
|
+
promptTemplate: "guided-research-decision",
|
|
438
464
|
toolContract: {
|
|
439
465
|
allowedGsdTools: ["gsd_summary_save"],
|
|
440
466
|
requiredWorkflowTools: ["ask_user_questions"],
|
|
@@ -447,6 +473,7 @@ export const UNIT_REGISTRY = {
|
|
|
447
473
|
kind: "primary",
|
|
448
474
|
scopeClass: "standard",
|
|
449
475
|
phaseChain: ["research"],
|
|
476
|
+
promptTemplate: "guided-research-project",
|
|
450
477
|
toolContract: {
|
|
451
478
|
allowedGsdTools: [],
|
|
452
479
|
requiredWorkflowTools: [],
|
|
@@ -494,3 +521,8 @@ export function getUnitDescriptor(unitType: string): UnitDescriptor | undefined
|
|
|
494
521
|
export function getUnitPhaseChain(unitType: string): readonly GSDModelPhaseKey[] | null {
|
|
495
522
|
return getUnitDescriptor(unitType)?.phaseChain ?? null;
|
|
496
523
|
}
|
|
524
|
+
|
|
525
|
+
/** Prompt template id for units with a direct one-template builder. */
|
|
526
|
+
export function getUnitPromptTemplate(unitType: string): string | undefined {
|
|
527
|
+
return getUnitDescriptor(unitType)?.promptTemplate;
|
|
528
|
+
}
|
|
@@ -100,6 +100,14 @@ export interface ExecuteTaskRecoveryStatus {
|
|
|
100
100
|
mustHavesMentionedInSummary: number;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
export interface UnitHarnessAbortRecord {
|
|
104
|
+
kind: "tool-loop-guard" | "tool-error" | "turn-abort";
|
|
105
|
+
reason: string;
|
|
106
|
+
toolName?: string;
|
|
107
|
+
count?: number;
|
|
108
|
+
recordedAt: number;
|
|
109
|
+
}
|
|
110
|
+
|
|
103
111
|
export interface AutoUnitRuntimeRecord {
|
|
104
112
|
version: 1;
|
|
105
113
|
unitType: string;
|
|
@@ -116,6 +124,7 @@ export interface AutoUnitRuntimeRecord {
|
|
|
116
124
|
recovery?: ExecuteTaskRecoveryStatus;
|
|
117
125
|
recoveryAttempts?: number;
|
|
118
126
|
lastRecoveryReason?: "idle" | "hard";
|
|
127
|
+
harnessAbort?: UnitHarnessAbortRecord;
|
|
119
128
|
}
|
|
120
129
|
|
|
121
130
|
function runtimeDir(basePath: string): string {
|
|
@@ -138,6 +147,8 @@ export function writeUnitRuntimeRecord(
|
|
|
138
147
|
const path = runtimePath(basePath, unitType, unitId);
|
|
139
148
|
return withRecordLock(path, () => {
|
|
140
149
|
const prev = readUnitRuntimeRecord(basePath, unitType, unitId);
|
|
150
|
+
const sameRun = prev?.startedAt === startedAt;
|
|
151
|
+
const updatesHarnessAbort = Object.prototype.hasOwnProperty.call(updates, "harnessAbort");
|
|
141
152
|
const next: AutoUnitRuntimeRecord = {
|
|
142
153
|
version: 1,
|
|
143
154
|
unitType,
|
|
@@ -154,12 +165,32 @@ export function writeUnitRuntimeRecord(
|
|
|
154
165
|
recovery: updates.recovery ?? prev?.recovery,
|
|
155
166
|
recoveryAttempts: updates.recoveryAttempts ?? prev?.recoveryAttempts ?? 0,
|
|
156
167
|
lastRecoveryReason: updates.lastRecoveryReason ?? prev?.lastRecoveryReason,
|
|
168
|
+
harnessAbort: updatesHarnessAbort
|
|
169
|
+
? updates.harnessAbort
|
|
170
|
+
: (sameRun ? prev?.harnessAbort : undefined),
|
|
157
171
|
};
|
|
158
172
|
atomicWriteSync(path, JSON.stringify(next, null, 2) + "\n", "utf-8");
|
|
159
173
|
return next;
|
|
160
174
|
});
|
|
161
175
|
}
|
|
162
176
|
|
|
177
|
+
export function recordUnitHarnessAbort(
|
|
178
|
+
basePath: string,
|
|
179
|
+
unitType: string,
|
|
180
|
+
unitId: string,
|
|
181
|
+
startedAt: number,
|
|
182
|
+
abort: Omit<UnitHarnessAbortRecord, "recordedAt"> & { recordedAt?: number },
|
|
183
|
+
): AutoUnitRuntimeRecord {
|
|
184
|
+
return writeUnitRuntimeRecord(basePath, unitType, unitId, startedAt, {
|
|
185
|
+
harnessAbort: {
|
|
186
|
+
...abort,
|
|
187
|
+
recordedAt: abort.recordedAt ?? Date.now(),
|
|
188
|
+
},
|
|
189
|
+
lastProgressAt: Date.now(),
|
|
190
|
+
lastProgressKind: `harness-abort:${abort.kind}`,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
163
194
|
export function readUnitRuntimeRecord(basePath: string, unitType: string, unitId: string): AutoUnitRuntimeRecord | null {
|
|
164
195
|
const path = runtimePath(basePath, unitType, unitId);
|
|
165
196
|
if (!existsSync(path)) return null;
|
|
@@ -170,6 +201,17 @@ export function readUnitRuntimeRecord(basePath: string, unitType: string, unitId
|
|
|
170
201
|
}
|
|
171
202
|
}
|
|
172
203
|
|
|
204
|
+
export function readUnitHarnessAbort(
|
|
205
|
+
basePath: string,
|
|
206
|
+
unitType: string,
|
|
207
|
+
unitId: string,
|
|
208
|
+
startedAt: number,
|
|
209
|
+
): UnitHarnessAbortRecord | null {
|
|
210
|
+
const record = readUnitRuntimeRecord(basePath, unitType, unitId);
|
|
211
|
+
if (!record || record.startedAt !== startedAt) return null;
|
|
212
|
+
return record.harnessAbort ?? null;
|
|
213
|
+
}
|
|
214
|
+
|
|
173
215
|
export function clearUnitRuntimeRecord(basePath: string, unitType: string, unitId: string): void {
|
|
174
216
|
const path = runtimePath(basePath, unitType, unitId);
|
|
175
217
|
if (existsSync(path)) unlinkSync(path);
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
// gsd-pi + Workspace handle: single source of truth for path resolution per milestone
|
|
2
2
|
|
|
3
|
-
import { join, resolve } from "node:path";
|
|
4
|
-
import {
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
buildMilestoneFileName,
|
|
6
|
+
canonicalPhaseDirName,
|
|
7
|
+
isLegacyMilestonesLayout,
|
|
8
|
+
milestonesDir,
|
|
9
|
+
type GsdPathContract,
|
|
10
|
+
resolveGsdPathContract,
|
|
11
|
+
resolveMilestoneFile,
|
|
12
|
+
resolveMilestonePath,
|
|
13
|
+
normalizeRealPath,
|
|
14
|
+
} from "./paths.js";
|
|
5
15
|
import { isGsdWorktreePath, resolveWorktreeProjectRoot } from "./worktree-root.js";
|
|
6
16
|
|
|
7
17
|
export type GsdWorkspaceMode = "project" | "worktree";
|
|
@@ -31,6 +41,29 @@ function tryRealpath(p: string): string {
|
|
|
31
41
|
return normalizeRealPath(p);
|
|
32
42
|
}
|
|
33
43
|
|
|
44
|
+
function isLegacyMilestoneDirectory(dir: string): boolean {
|
|
45
|
+
const parent = dirname(dir);
|
|
46
|
+
return parent.endsWith("/milestones") || parent.endsWith("\\milestones");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function scopeMilestoneDir(basePath: string, milestoneId: string): string {
|
|
50
|
+
return resolveMilestonePath(basePath, milestoneId) ?? join(
|
|
51
|
+
milestonesDir(basePath),
|
|
52
|
+
isLegacyMilestonesLayout(basePath) ? milestoneId : canonicalPhaseDirName(milestoneId),
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function scopeMilestoneFile(basePath: string, milestoneId: string, suffix: string): string {
|
|
57
|
+
const existing = resolveMilestoneFile(basePath, milestoneId, suffix);
|
|
58
|
+
if (existing) return existing;
|
|
59
|
+
|
|
60
|
+
const dir = scopeMilestoneDir(basePath, milestoneId);
|
|
61
|
+
const filename = isLegacyMilestoneDirectory(dir)
|
|
62
|
+
? `${milestoneId}-${suffix}.md`
|
|
63
|
+
: buildMilestoneFileName(milestoneId, suffix);
|
|
64
|
+
return join(dir, filename);
|
|
65
|
+
}
|
|
66
|
+
|
|
34
67
|
/**
|
|
35
68
|
* Create an immutable GsdWorkspace handle from a raw base path.
|
|
36
69
|
* Resolves both the project root and (when applicable) the worktree root,
|
|
@@ -73,22 +106,23 @@ export function createWorkspace(rawBasePath: string): GsdWorkspace {
|
|
|
73
106
|
* Bind a milestoneId to a workspace, producing an immutable MilestoneScope
|
|
74
107
|
* with path-returning closures for DB-authoritative project state.
|
|
75
108
|
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
109
|
+
* DB paths route to contract.projectGsd. Milestone artifact paths resolve from
|
|
110
|
+
* the pinned project root with the layout-aware resolvers, so flat-phase and
|
|
111
|
+
* legacy projects use the same scope surface.
|
|
79
112
|
*/
|
|
80
113
|
export function scopeMilestone(workspace: GsdWorkspace, milestoneId: string): MilestoneScope {
|
|
81
114
|
const { contract } = workspace;
|
|
82
115
|
const gsd = contract.projectGsd;
|
|
116
|
+
const basePath = workspace.projectRoot;
|
|
83
117
|
|
|
84
118
|
const scope: MilestoneScope = Object.freeze({
|
|
85
119
|
workspace,
|
|
86
120
|
milestoneId,
|
|
87
|
-
contextFile: () =>
|
|
88
|
-
roadmapFile: () =>
|
|
121
|
+
contextFile: () => scopeMilestoneFile(basePath, milestoneId, "CONTEXT"),
|
|
122
|
+
roadmapFile: () => scopeMilestoneFile(basePath, milestoneId, "ROADMAP"),
|
|
89
123
|
stateFile: () => join(gsd, "STATE.md"),
|
|
90
124
|
dbPath: () => contract.projectDb,
|
|
91
|
-
milestoneDir: () =>
|
|
125
|
+
milestoneDir: () => scopeMilestoneDir(basePath, milestoneId),
|
|
92
126
|
metaJson: () => join(gsd, `${milestoneId}-META.json`),
|
|
93
127
|
});
|
|
94
128
|
|