@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
|
@@ -596,7 +596,7 @@ export const DISPATCH_RULES = [
|
|
|
596
596
|
},
|
|
597
597
|
},
|
|
598
598
|
{
|
|
599
|
-
name: "uat-verdict-gate (non-PASS
|
|
599
|
+
name: "uat-verdict-gate (non-PASS observed; closeout enforces)",
|
|
600
600
|
match: async ({ mid, basePath, prefs }) => {
|
|
601
601
|
// Only applies when UAT dispatch is enabled
|
|
602
602
|
if (!prefs?.uat_dispatch)
|
|
@@ -611,9 +611,9 @@ export const DISPATCH_RULES = [
|
|
|
611
611
|
continue;
|
|
612
612
|
const { verdict, uatType } = result;
|
|
613
613
|
if (!isAcceptableUatVerdict(verdict, uatType)) {
|
|
614
|
-
//
|
|
615
|
-
// so follow-up slices can remediate, while
|
|
616
|
-
// enforces manual UAT PASS sign-off before closure.
|
|
614
|
+
// Observe non-PASS verdicts without hard-stopping auto-mode. Allow
|
|
615
|
+
// progression so follow-up slices can remediate, while
|
|
616
|
+
// complete-milestone still enforces manual UAT PASS sign-off before closure.
|
|
617
617
|
continue;
|
|
618
618
|
}
|
|
619
619
|
}
|
|
@@ -902,9 +902,19 @@ export const DISPATCH_RULES = [
|
|
|
902
902
|
const sliceContextFile = resolveSliceFile(basePath, mid, state.activeSlice.id, "CONTEXT");
|
|
903
903
|
if (sliceContextFile && existsSync(sliceContextFile))
|
|
904
904
|
return null; // discussion already done, proceed
|
|
905
|
+
const closedSliceIds = getClosedSliceIds(mid);
|
|
906
|
+
const justClosedSliceId = closedSliceIds[closedSliceIds.length - 1];
|
|
907
|
+
let priorVerdictWarning = "";
|
|
908
|
+
if (justClosedSliceId) {
|
|
909
|
+
const prior = await readUatGateVerdict(basePath, mid, justClosedSliceId);
|
|
910
|
+
if (prior && !isAcceptableUatVerdict(prior.verdict, prior.uatType)) {
|
|
911
|
+
priorVerdictWarning =
|
|
912
|
+
` Note: the slice just closed (${justClosedSliceId}) recorded a non-PASS UAT verdict (${prior.verdict.toUpperCase()}); review before continuing.`;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
905
915
|
return {
|
|
906
916
|
action: "stop",
|
|
907
|
-
reason: `Slice ${state.activeSlice.id} requires discussion before planning (require_slice_discussion is enabled). Run /gsd discuss to discuss this slice, then /gsd auto to resume
|
|
917
|
+
reason: `Slice ${state.activeSlice.id} requires discussion before planning (require_slice_discussion is enabled). Run /gsd discuss to discuss this slice, then /gsd auto to resume.${priorVerdictWarning}`,
|
|
908
918
|
level: "warning",
|
|
909
919
|
};
|
|
910
920
|
},
|
|
@@ -326,7 +326,7 @@ export function floorThinkingLevelForUnit(unitType, level) {
|
|
|
326
326
|
return EXECUTE_TASK_MIN_THINKING_LEVEL;
|
|
327
327
|
}
|
|
328
328
|
export function resolvePreferredModelConfig(unitType, autoModeStartModel, isAutoMode = true, basePath, availableModelIds, preferredModelId) {
|
|
329
|
-
const explicitConfig = resolveModelWithFallbacksForUnit(unitType, basePath, availableModelIds, preferredModelId);
|
|
329
|
+
const explicitConfig = resolveModelWithFallbacksForUnit(unitType, basePath, availableModelIds, preferredModelId, true);
|
|
330
330
|
if (explicitConfig) {
|
|
331
331
|
return {
|
|
332
332
|
...explicitConfig,
|
|
@@ -338,23 +338,45 @@ export function resolvePreferredModelConfig(unitType, autoModeStartModel, isAuto
|
|
|
338
338
|
if (!isAutoMode)
|
|
339
339
|
return undefined;
|
|
340
340
|
const routingConfig = resolveDynamicRoutingConfig();
|
|
341
|
-
if (!routingConfig.enabled
|
|
341
|
+
if (!routingConfig.enabled)
|
|
342
342
|
return undefined;
|
|
343
|
-
//
|
|
344
|
-
//
|
|
345
|
-
//
|
|
346
|
-
|
|
343
|
+
// Only an explicit flat-rate opt-out suppresses synthesized routing.
|
|
344
|
+
// `dynamic_routing.enabled: true` is user intent to run routing even when
|
|
345
|
+
// the start provider is a flat-rate subscription. Applied ahead of both the
|
|
346
|
+
// tier-pin and profile-derived synthesis paths so the opt-out is uniform.
|
|
347
|
+
if (routingConfig.allow_flat_rate_providers === false &&
|
|
347
348
|
autoModeStartModel &&
|
|
348
349
|
isFlatRateProvider(autoModeStartModel.provider, autoModeStartModel.flatRateCtx)) {
|
|
349
350
|
return undefined;
|
|
350
351
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
if (
|
|
352
|
+
// With explicit `tier_models` pins, synthesize a heavy-tier ceiling that
|
|
353
|
+
// dynamic routing downgrades from per unit.
|
|
354
|
+
if (routingConfig.tier_models) {
|
|
355
|
+
const ceilingModel = routingConfig.tier_models.heavy
|
|
356
|
+
?? (autoModeStartModel ? `${autoModeStartModel.provider}/${autoModeStartModel.id}` : undefined);
|
|
357
|
+
if (!ceilingModel)
|
|
358
|
+
return undefined;
|
|
359
|
+
return {
|
|
360
|
+
primary: ceilingModel,
|
|
361
|
+
fallbacks: [],
|
|
362
|
+
source: "synthesized",
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
// No `tier_models` pins: fall back to the token-profile's per-phase model so
|
|
366
|
+
// profile tier resolution survives. `skipProfileDefaults` is left false here
|
|
367
|
+
// (unlike the explicit-detection call above) precisely because we WANT the
|
|
368
|
+
// profile defaults this time — they are the routing ceiling, not a hard
|
|
369
|
+
// user selection. Labeling the result `synthesized` (not `explicit`) keeps
|
|
370
|
+
// dynamic routing and fallbacks enabled downstream. Without this branch,
|
|
371
|
+
// `resolvePreferredModelConfig` returns undefined for profile-only configs
|
|
372
|
+
// and auto dispatch bleeds the start model onto every unit, silently
|
|
373
|
+
// discarding the profile (#1115: routing/fallbacks disabled when a
|
|
374
|
+
// token_profile is set but no tier_models are pinned).
|
|
375
|
+
const profileConfig = resolveModelWithFallbacksForUnit(unitType, basePath, availableModelIds, preferredModelId, false);
|
|
376
|
+
if (!profileConfig)
|
|
354
377
|
return undefined;
|
|
355
378
|
return {
|
|
356
|
-
|
|
357
|
-
fallbacks: [],
|
|
379
|
+
...profileConfig,
|
|
358
380
|
source: "synthesized",
|
|
359
381
|
};
|
|
360
382
|
}
|
|
@@ -510,15 +532,11 @@ autoModeStartThinkingLevel) {
|
|
|
510
532
|
]));
|
|
511
533
|
}
|
|
512
534
|
}
|
|
513
|
-
// Disable routing for flat-rate providers
|
|
514
|
-
//
|
|
515
|
-
// model provides no cost benefit — it only degrades quality.
|
|
535
|
+
// Disable routing for flat-rate providers only when the user explicitly
|
|
536
|
+
// opts out with dynamic_routing.allow_flat_rate_providers: false.
|
|
516
537
|
// Fail-closed: if primary model can't be resolved, fall back to
|
|
517
538
|
// provider-level signals rather than allowing unwanted downgrades.
|
|
518
|
-
|
|
519
|
-
// claude-code subscribers can still get intelligent per-task selection
|
|
520
|
-
// across their subscription (#4386).
|
|
521
|
-
if (routingConfig.enabled && !routingConfig.allow_flat_rate_providers) {
|
|
539
|
+
if (routingConfig.enabled && routingConfig.allow_flat_rate_providers === false) {
|
|
522
540
|
const primaryModel = resolveModelId(modelConfig.primary, routingEligibleModels, ctx.model?.provider);
|
|
523
541
|
if (primaryModel) {
|
|
524
542
|
const primaryFlatRateCtx = buildFlatRateContext(primaryModel.provider, ctx, prefs);
|
|
@@ -12,12 +12,13 @@ import { hasVerdict, extractVerdict } from "./verdict-parser.js";
|
|
|
12
12
|
import { loadPrompt, inlineTemplate } from "./prompt-loader.js";
|
|
13
13
|
import { resolveMilestoneFile, resolveSliceFile, resolveSlicePath, resolveTasksDir, resolveTaskFiles, resolveTaskFile, relMilestoneFile, relSliceFile, relSlicePath, relMilestonePath, relTaskFile, resolveGsdRootFile, relGsdRootFile, resolveRuntimeFile, } from "./paths.js";
|
|
14
14
|
import { resolveInlineLevel, loadEffectiveGSDPreferences } from "./preferences.js";
|
|
15
|
+
import { createRepositoryRegistryFromPreferences } from "./repository-registry.js";
|
|
15
16
|
import { isContextModeEnabled } from "./preferences-types.js";
|
|
16
17
|
import { parseRoadmap } from "./parsers-legacy.js";
|
|
17
|
-
import { join } from "node:path";
|
|
18
|
+
import { join, relative, sep } from "node:path";
|
|
18
19
|
import { existsSync } from "node:fs";
|
|
19
20
|
import { computeBudgets, resolveExecutorContextWindow, truncateAtSectionBoundary } from "./context-budget.js";
|
|
20
|
-
import { getPendingGatesForTurn } from "./gsd-db.js";
|
|
21
|
+
import { getGateResults, getPendingGatesForTurn } from "./gsd-db.js";
|
|
21
22
|
import { assertGateCoverage, getGatesForTurn, } from "./gate-registry.js";
|
|
22
23
|
import { formatDecisionsCompact, formatRequirementsCompact } from "./structured-data-formatter.js";
|
|
23
24
|
import { readPhaseAnchor, formatAnchorForPrompt } from "./phase-anchor.js";
|
|
@@ -295,6 +296,37 @@ function formatExecutorConstraints(sessionContextWindow, modelRegistry, sessionP
|
|
|
295
296
|
`- Keep individual tasks completable within a single context window — if a task needs more context than fits, split it`,
|
|
296
297
|
].join("\n");
|
|
297
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* Builds the Declared Repositories block for parent-workspace planners.
|
|
301
|
+
* Returns "" for single-repo projects so the common-case prompt is unchanged;
|
|
302
|
+
* only parent mode with declared child repos gets the registry list and the
|
|
303
|
+
* targetRepositories assignment instruction.
|
|
304
|
+
*/
|
|
305
|
+
export function buildRepoRegistryBlock(base) {
|
|
306
|
+
let registry;
|
|
307
|
+
try {
|
|
308
|
+
registry = createRepositoryRegistryFromPreferences(base, loadEffectiveGSDPreferences(base)?.preferences);
|
|
309
|
+
}
|
|
310
|
+
catch {
|
|
311
|
+
return "";
|
|
312
|
+
}
|
|
313
|
+
const hasChildRepo = registry.repositories.some((repo) => repo.id !== "project");
|
|
314
|
+
if (registry.mode !== "parent" || !hasChildRepo)
|
|
315
|
+
return "";
|
|
316
|
+
const lines = registry.repositories.map((repo) => {
|
|
317
|
+
const relPath = relative(registry.projectRoot, repo.root).split(sep).join("/") || ".";
|
|
318
|
+
const role = repo.role ? ` — ${repo.role}` : "";
|
|
319
|
+
const root = repo.id === "project" ? " (root)" : ` (${relPath})`;
|
|
320
|
+
return `- \`${repo.id}\`${root}${role}`;
|
|
321
|
+
});
|
|
322
|
+
return [
|
|
323
|
+
"### Declared Repositories",
|
|
324
|
+
"",
|
|
325
|
+
...lines,
|
|
326
|
+
"",
|
|
327
|
+
"This is a parent workspace. Assign each task's `targetRepositories` to the repository id(s) it touches (from the list above). Omit `targetRepositories` to inherit the slice-wide default from `gsd_plan_slice`; set it explicitly when a task differs (e.g. `[\"project\"]` for parent-root work).",
|
|
328
|
+
].join("\n");
|
|
329
|
+
}
|
|
298
330
|
/**
|
|
299
331
|
* Returns a markdown bullet list of known context file paths for the given
|
|
300
332
|
* milestone (and optionally slice). Falls back to a generic tool-agnostic
|
|
@@ -2174,6 +2206,7 @@ async function renderSlicePrompt(options) {
|
|
|
2174
2206
|
inlinedContext,
|
|
2175
2207
|
dependencySummaries: depContent,
|
|
2176
2208
|
sourceFilePaths: buildSourceFilePaths(base, mid, sid),
|
|
2209
|
+
repoRegistry: buildRepoRegistryBlock(base),
|
|
2177
2210
|
executorContextConstraints,
|
|
2178
2211
|
commitInstruction,
|
|
2179
2212
|
skillActivation: buildSkillActivationBlock({
|
|
@@ -2906,6 +2939,47 @@ export async function buildValidateMilestonePrompt(mid, midTitle, base, level) {
|
|
|
2906
2939
|
inlined.push(outstandingBlock);
|
|
2907
2940
|
trackPromptContext(contextTelemetry, "outstanding-items", "inline", outstandingBlock);
|
|
2908
2941
|
}
|
|
2942
|
+
const persistedGateFlags = [];
|
|
2943
|
+
try {
|
|
2944
|
+
const gateLabels = {
|
|
2945
|
+
Q3: "Threat Surface",
|
|
2946
|
+
Q4: "Requirement Impact",
|
|
2947
|
+
};
|
|
2948
|
+
for (const sid of seenValSlices) {
|
|
2949
|
+
for (const gate of getGateResults(mid, sid, "slice")) {
|
|
2950
|
+
if (gate.gate_id !== "Q3" && gate.gate_id !== "Q4")
|
|
2951
|
+
continue;
|
|
2952
|
+
const findings = gate.findings.trim();
|
|
2953
|
+
const rationale = gate.rationale.trim();
|
|
2954
|
+
const verdict = gate.verdict ?? "";
|
|
2955
|
+
const hasNonPassVerdict = verdict !== "" && verdict !== "pass" && verdict !== "omitted";
|
|
2956
|
+
if (!findings && !hasNonPassVerdict)
|
|
2957
|
+
continue;
|
|
2958
|
+
const displayVerdict = verdict || gate.status;
|
|
2959
|
+
const detail = findings || rationale || "_No findings text recorded; reconcile the persisted non-pass verdict._";
|
|
2960
|
+
persistedGateFlags.push(`- **${sid} / ${gate.gate_id} (${gateLabels[gate.gate_id]}) / ${displayVerdict}:** ${detail}`);
|
|
2961
|
+
if (findings && rationale)
|
|
2962
|
+
persistedGateFlags.push(` - Rationale: ${rationale}`);
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
catch (err) {
|
|
2967
|
+
logWarning("prompt", `buildValidateMilestonePrompt persisted gate flags lookup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2968
|
+
}
|
|
2969
|
+
if (persistedGateFlags.length > 0) {
|
|
2970
|
+
const persistedGateFlagsBlock = [
|
|
2971
|
+
"### Persisted Slice-Level Gate Flags (from quality_gates)",
|
|
2972
|
+
"",
|
|
2973
|
+
"These slice gates recorded findings or non-pass verdicts during execution. Reconcile each against the milestone-level evidence before returning MV03/MV04 verdicts.",
|
|
2974
|
+
"",
|
|
2975
|
+
...persistedGateFlags,
|
|
2976
|
+
].join("\n");
|
|
2977
|
+
inlined.push(persistedGateFlagsBlock);
|
|
2978
|
+
trackPromptContext(contextTelemetry, "persisted-slice-gate-flags", "inline", persistedGateFlagsBlock);
|
|
2979
|
+
}
|
|
2980
|
+
else {
|
|
2981
|
+
trackPromptContext(contextTelemetry, "persisted-slice-gate-flags", "skipped", null, "none");
|
|
2982
|
+
}
|
|
2909
2983
|
// Inline existing VALIDATION file if this is a re-validation round
|
|
2910
2984
|
const validationPath = resolveMilestoneFile(base, mid, "VALIDATION");
|
|
2911
2985
|
const validationRel = relMilestoneFile(base, mid, "VALIDATION");
|
|
@@ -1435,15 +1435,16 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
|
|
|
1435
1435
|
? `${s.autoModeStartModel.provider}/${s.autoModeStartModel.id}`
|
|
1436
1436
|
: ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : "default";
|
|
1437
1437
|
// Flat-rate providers (e.g. GitHub Copilot, claude-code, user-declared
|
|
1438
|
-
// subscription proxies, externalCli CLIs) suppress routing
|
|
1439
|
-
//
|
|
1438
|
+
// subscription proxies, externalCli CLIs) suppress routing only when the
|
|
1439
|
+
// user explicitly opts out with allow_flat_rate_providers: false. Reflect
|
|
1440
|
+
// that in the banner. Thread the same
|
|
1440
1441
|
// FlatRateContext used by selectAndApplyModel so user-declared
|
|
1441
1442
|
// flat-rate providers and externalCli auto-detection are respected.
|
|
1442
1443
|
const { isFlatRateProvider, buildFlatRateContext } = await import("./auto-model-selection.js");
|
|
1443
1444
|
const bannerPrefs = loadEffectiveGSDPreferencesWithRegistry(ctx.modelRegistry, base, resolveProfileAnchorProvider(ctx.model?.provider, s.autoModeStartModel?.provider), s.autoModeStartModel ? `${s.autoModeStartModel.provider}/${s.autoModeStartModel.id}` : undefined)?.preferences;
|
|
1444
1445
|
const effectiveProvider = s.autoModeStartModel?.provider ?? ctx.model?.provider;
|
|
1445
1446
|
const effectivelyEnabled = routingConfig.enabled
|
|
1446
|
-
&& (routingConfig.allow_flat_rate_providers
|
|
1447
|
+
&& (routingConfig.allow_flat_rate_providers !== false
|
|
1447
1448
|
|| !(effectiveProvider && isFlatRateProvider(effectiveProvider, buildFlatRateContext(effectiveProvider, ctx, bannerPrefs))));
|
|
1448
1449
|
// The actual ceiling may come from tier_models.heavy, not the start model.
|
|
1449
1450
|
const effectiveCeiling = (routingConfig.enabled && routingConfig.tier_models?.heavy)
|
|
@@ -56,6 +56,8 @@ export async function recoverTimedOutUnit(ctx, pi, unitType, unitId, reason, rct
|
|
|
56
56
|
lastProgressAt: Date.now(),
|
|
57
57
|
progressCount: (runtime?.progressCount ?? 0) + 1,
|
|
58
58
|
lastProgressKind: reason === "idle" ? "idle-recovery-retry" : "hard-recovery-retry",
|
|
59
|
+
// This steering retry is a fresh chance for the same unit to save a valid result.
|
|
60
|
+
harnessAbort: undefined,
|
|
59
61
|
});
|
|
60
62
|
const steeringLines = isEscalation
|
|
61
63
|
? [
|
|
@@ -151,6 +153,8 @@ export async function recoverTimedOutUnit(ctx, pi, unitType, unitId, reason, rct
|
|
|
151
153
|
lastProgressAt: Date.now(),
|
|
152
154
|
progressCount: (runtime?.progressCount ?? 0) + 1,
|
|
153
155
|
lastProgressKind: reason === "idle" ? "idle-recovery-retry" : "hard-recovery-retry",
|
|
156
|
+
// This steering retry is a fresh chance for the same unit to save a valid result.
|
|
157
|
+
harnessAbort: undefined,
|
|
154
158
|
});
|
|
155
159
|
const steeringLines = isEscalation
|
|
156
160
|
? [
|
|
@@ -986,10 +986,10 @@ export function teardownAutoWorktree(originalBasePath, milestoneId, opts = {}) {
|
|
|
986
986
|
const branch = autoWorktreeBranch(milestoneId);
|
|
987
987
|
const { preserveBranch = false, preserveWorktree = false } = opts;
|
|
988
988
|
const previousCwd = safeCwd(originalBasePath);
|
|
989
|
+
let clearActiveWorkspace = true;
|
|
989
990
|
// Wrap the entire teardown body in a single try/finally so activeWorkspace
|
|
990
|
-
// is
|
|
991
|
-
// was deleted before teardown ran).
|
|
992
|
-
// removeWorktree, leaving the registry stale on a chdir failure (H3 fix).
|
|
991
|
+
// is cleared on completion or irrecoverable failure — even if process.chdir
|
|
992
|
+
// throws (e.g. originalBasePath was deleted before teardown ran).
|
|
993
993
|
try {
|
|
994
994
|
try {
|
|
995
995
|
process.chdir(originalBasePath);
|
|
@@ -1026,17 +1026,23 @@ export function teardownAutoWorktree(originalBasePath, milestoneId, opts = {}) {
|
|
|
1026
1026
|
// 3. Remove the worktree unless this exit path explicitly preserves it
|
|
1027
1027
|
// (slice-parallel dispatch stops the parent loop but keeps the parent
|
|
1028
1028
|
// milestone worktree for restart/re-entry).
|
|
1029
|
+
let worktreeRemoved = preserveWorktree;
|
|
1029
1030
|
if (!preserveWorktree) {
|
|
1030
|
-
removeWorktree(originalBasePath, milestoneId, {
|
|
1031
|
+
worktreeRemoved = removeWorktree(originalBasePath, milestoneId, {
|
|
1031
1032
|
branch,
|
|
1032
1033
|
deleteBranch: !preserveBranch,
|
|
1033
1034
|
});
|
|
1035
|
+
if (!worktreeRemoved) {
|
|
1036
|
+
logWarning("worktree", `Worktree removal aborted for ${milestoneId}; uncommitted work was preserved. ` +
|
|
1037
|
+
`Manual quarantine recovery may be needed before retrying teardown.`, { worktree: milestoneId });
|
|
1038
|
+
clearActiveWorkspace = false;
|
|
1039
|
+
}
|
|
1034
1040
|
}
|
|
1035
1041
|
// Verify cleanup succeeded — warn if the worktree directory is still on disk.
|
|
1036
1042
|
// On Windows, bash-based cleanup can silently fail when paths contain
|
|
1037
1043
|
// backslashes (#1436), leaving ~1 GB+ orphaned directories.
|
|
1038
1044
|
const wtDir = worktreePath(originalBasePath, milestoneId);
|
|
1039
|
-
if (!preserveWorktree && existsSync(wtDir)) {
|
|
1045
|
+
if (!preserveWorktree && worktreeRemoved && existsSync(wtDir)) {
|
|
1040
1046
|
logWarning("reconcile", `Worktree directory still exists after teardown: ${wtDir}. ` +
|
|
1041
1047
|
`This is likely an orphaned directory consuming disk space. ` +
|
|
1042
1048
|
`Remove it manually with: rm -rf "${wtDir.replaceAll("\\", "/")}"`, { worktree: milestoneId });
|
|
@@ -1057,10 +1063,12 @@ export function teardownAutoWorktree(originalBasePath, milestoneId, opts = {}) {
|
|
|
1057
1063
|
}
|
|
1058
1064
|
}
|
|
1059
1065
|
finally {
|
|
1060
|
-
// Clear module state
|
|
1061
|
-
//
|
|
1062
|
-
//
|
|
1063
|
-
|
|
1066
|
+
// Clear module state when teardown completed or failed irrecoverably.
|
|
1067
|
+
// When removeWorktree returns false (quarantine failure), preserve the
|
|
1068
|
+
// registry so recovery logic still sees the active milestone worktree.
|
|
1069
|
+
if (clearActiveWorkspace) {
|
|
1070
|
+
setActiveWorkspace(null);
|
|
1071
|
+
}
|
|
1064
1072
|
}
|
|
1065
1073
|
}
|
|
1066
1074
|
/**
|
|
@@ -1491,15 +1499,28 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
|
|
|
1491
1499
|
catch (err) {
|
|
1492
1500
|
logWarning("worktree", `clearProjectRootStateFiles failed during already-merged cleanup: ${err instanceof Error ? err.message : String(err)}`);
|
|
1493
1501
|
}
|
|
1502
|
+
let worktreeRemoved = true;
|
|
1494
1503
|
try {
|
|
1495
|
-
removeWorktree(originalBasePath_, milestoneId, {
|
|
1504
|
+
worktreeRemoved = removeWorktree(originalBasePath_, milestoneId, {
|
|
1496
1505
|
branch: milestoneBranch,
|
|
1497
1506
|
deleteBranch: false,
|
|
1498
1507
|
});
|
|
1499
1508
|
}
|
|
1500
1509
|
catch (err) {
|
|
1510
|
+
worktreeRemoved = false;
|
|
1501
1511
|
logWarning("worktree", `worktree removal failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1502
1512
|
}
|
|
1513
|
+
if (!worktreeRemoved) {
|
|
1514
|
+
logWarning("worktree", `Skipping milestone branch deletion for ${milestoneBranch}; worktree removal was aborted to preserve uncommitted milestone work.`);
|
|
1515
|
+
nudgeGitBranchCache(previousCwd);
|
|
1516
|
+
try {
|
|
1517
|
+
process.chdir(originalBasePath_);
|
|
1518
|
+
}
|
|
1519
|
+
catch (err) {
|
|
1520
|
+
logWarning("worktree", `chdir to project root after already-merged cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1521
|
+
}
|
|
1522
|
+
return { commitMessage, pushed: false, prCreated: false, codeFilesChanged: true };
|
|
1523
|
+
}
|
|
1503
1524
|
try {
|
|
1504
1525
|
nativeBranchDelete(originalBasePath_, milestoneBranch);
|
|
1505
1526
|
}
|
|
@@ -1932,16 +1953,22 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
|
|
|
1932
1953
|
}
|
|
1933
1954
|
const finalizeMilestoneCleanup = () => {
|
|
1934
1955
|
// 12. Remove worktree directory first (must happen before branch deletion)
|
|
1956
|
+
let worktreeRemoved = true;
|
|
1935
1957
|
try {
|
|
1936
|
-
removeWorktree(originalBasePath_, milestoneId, {
|
|
1958
|
+
worktreeRemoved = removeWorktree(originalBasePath_, milestoneId, {
|
|
1937
1959
|
branch: milestoneBranch,
|
|
1938
1960
|
deleteBranch: false,
|
|
1939
1961
|
});
|
|
1940
1962
|
}
|
|
1941
1963
|
catch (err) {
|
|
1964
|
+
worktreeRemoved = false;
|
|
1942
1965
|
// Best-effort -- worktree dir may already be gone
|
|
1943
1966
|
logWarning("worktree", `worktree removal failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1944
1967
|
}
|
|
1968
|
+
if (!worktreeRemoved) {
|
|
1969
|
+
logWarning("worktree", `Skipping milestone branch deletion for ${milestoneBranch}; worktree removal was aborted to preserve uncommitted milestone work.`);
|
|
1970
|
+
return;
|
|
1971
|
+
}
|
|
1945
1972
|
// 13. Delete milestone branch (after worktree removal so ref is unlocked)
|
|
1946
1973
|
try {
|
|
1947
1974
|
nativeBranchDelete(originalBasePath_, milestoneBranch);
|
|
@@ -68,7 +68,7 @@ import { MILESTONE_ID_RE } from "./milestone-ids.js";
|
|
|
68
68
|
import { updateProgressWidget as _updateProgressWidget, setCompletionProgressWidget, setAutoOutcomeWidget, setAutoActiveStatus, updateSliceProgressCache, clearSliceProgressCache, unitVerb, } from "./auto-dashboard.js";
|
|
69
69
|
import { registerSigtermHandler as _registerSigtermHandler, deregisterSigtermHandler as _deregisterSigtermHandler, } from "./auto-supervisor.js";
|
|
70
70
|
import { isDbAvailable, getMilestone, getMilestoneSlices, } from "./gsd-db.js";
|
|
71
|
-
import { closeWorkflowDatabase } from "./db-workspace.js";
|
|
71
|
+
import { checkpointWorkflowDatabase, closeWorkflowDatabase } from "./db-workspace.js";
|
|
72
72
|
import { markLatestActiveForWorkerCanceled } from "./db/unit-dispatches.js";
|
|
73
73
|
import { writeUnitRuntimeRecord } from "./unit-runtime.js";
|
|
74
74
|
import { countPendingCaptures } from "./captures.js";
|
|
@@ -651,6 +651,11 @@ export function stopAutoRemote(projectRoot) {
|
|
|
651
651
|
* The normal stop path stays SIGTERM-only so cooperative sessions can clean up;
|
|
652
652
|
* this path is only for the explicit "Force start" action.
|
|
653
653
|
*/
|
|
654
|
+
// Grace window between SIGTERM and SIGKILL so the target can run its cleanup
|
|
655
|
+
// handler (lock release, DB snapshot flush, worktree teardown). Polled so a
|
|
656
|
+
// process that exits early is not made to wait the full window.
|
|
657
|
+
const FORCE_STOP_GRACE_MS = 3000;
|
|
658
|
+
const FORCE_STOP_POLL_MS = 100;
|
|
654
659
|
export function forceStopAutoRemote(projectRoot) {
|
|
655
660
|
const lock = readCrashLock(projectRoot);
|
|
656
661
|
if (!lock)
|
|
@@ -665,6 +670,14 @@ export function forceStopAutoRemote(projectRoot) {
|
|
|
665
670
|
}
|
|
666
671
|
try {
|
|
667
672
|
process.kill(lock.pid, "SIGTERM");
|
|
673
|
+
// Poll for graceful exit before escalating. Synchronous sleep keeps this
|
|
674
|
+
// function sync for its callers (Atomics.wait times out with no notify).
|
|
675
|
+
const deadline = Date.now() + FORCE_STOP_GRACE_MS;
|
|
676
|
+
while (Date.now() < deadline) {
|
|
677
|
+
if (!isLockProcessAlive(lock))
|
|
678
|
+
break;
|
|
679
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, FORCE_STOP_POLL_MS);
|
|
680
|
+
}
|
|
668
681
|
if (isLockProcessAlive(lock)) {
|
|
669
682
|
process.kill(lock.pid, "SIGKILL");
|
|
670
683
|
}
|
|
@@ -1089,6 +1102,12 @@ export async function stopAuto(ctx, pi, reason, options = {}) {
|
|
|
1089
1102
|
const installTerminalCloseoutOutcome = completionStopRequested && preserveCloseoutTranscript;
|
|
1090
1103
|
const preserveCompletionSurface = completionStopRequested || preserveCloseoutTranscript;
|
|
1091
1104
|
s.completionStopInProgress = preserveCompletionSurface;
|
|
1105
|
+
try {
|
|
1106
|
+
checkpointWorkflowDatabase();
|
|
1107
|
+
}
|
|
1108
|
+
catch (e) {
|
|
1109
|
+
debugLog("stop-checkpoint-failed", { error: e instanceof Error ? e.message : String(e) });
|
|
1110
|
+
}
|
|
1092
1111
|
playNotificationBell("stop", loadedPreferences?.notifications);
|
|
1093
1112
|
// #4764 — telemetry: record the exit reason, isolation mode, whether an auto
|
|
1094
1113
|
// worktree was active, and whether the current milestone was merged before
|
|
@@ -1719,7 +1738,7 @@ export function buildWorktreeLifecycleDeps() {
|
|
|
1719
1738
|
// C4 (#5627) — GitServiceImpl constructor → gitServiceFactory
|
|
1720
1739
|
//
|
|
1721
1740
|
// Final WorktreeLifecycleDeps shape: 3 fields (gitServiceFactory,
|
|
1722
|
-
// worktreeProjection,
|
|
1741
|
+
// worktreeProjection, mergeMilestone transaction runner). Down from 18 at slice-7
|
|
1723
1742
|
// closure.
|
|
1724
1743
|
return {
|
|
1725
1744
|
gitServiceFactory: (basePath) => {
|
|
@@ -1727,7 +1746,7 @@ export function buildWorktreeLifecycleDeps() {
|
|
|
1727
1746
|
return new GitServiceImpl(basePath, gitConfig);
|
|
1728
1747
|
},
|
|
1729
1748
|
worktreeProjection: new WorktreeStateProjection(),
|
|
1730
|
-
|
|
1749
|
+
mergeMilestone: createMilestoneMergeTransaction(mergeMilestoneToMain),
|
|
1731
1750
|
};
|
|
1732
1751
|
}
|
|
1733
1752
|
function buildLifecycle() {
|
|
@@ -1770,6 +1789,7 @@ function buildLoopDeps(pi, ctx) {
|
|
|
1770
1789
|
stopAuto,
|
|
1771
1790
|
pauseAuto,
|
|
1772
1791
|
clearUnitTimeout,
|
|
1792
|
+
checkpointWorkflowDatabase,
|
|
1773
1793
|
updateProgressWidget,
|
|
1774
1794
|
...cmux,
|
|
1775
1795
|
handleLostSessionLock: (ctx, lockStatus) => {
|
|
@@ -674,12 +674,14 @@ export function registerDbTools(pi) {
|
|
|
674
674
|
inputs: Type.Array(Type.String(), { description: "Array<string> of input files or references; pass [\"path\"] or [], never a single string" }),
|
|
675
675
|
expectedOutput: Type.Array(Type.String(), { description: "Array<string> of files this task creates or overwrites; pass [\"path\"] or [], never prose or a single string" }),
|
|
676
676
|
observabilityImpact: Type.Optional(Type.String({ description: "Task observability impact" })),
|
|
677
|
+
targetRepositories: Type.Optional(Type.Array(Type.String(), { description: "Repository id(s) this task touches (parent workspace); must match a Declared Repository. Omit for single-repo projects." })),
|
|
677
678
|
}), { description: "Optional full task replacement for the slice. Omit for incremental planning, then call gsd_plan_task once per task." })),
|
|
678
679
|
// ── Enrichment metadata (optional — defaults to empty) ────────────
|
|
679
680
|
successCriteria: Type.Optional(Type.String({ description: "Slice success criteria block" })),
|
|
680
681
|
proofLevel: Type.Optional(Type.String({ description: "Slice proof level" })),
|
|
681
682
|
integrationClosure: Type.Optional(Type.String({ description: "Slice integration closure" })),
|
|
682
683
|
observabilityImpact: Type.Optional(Type.String({ description: "Slice observability impact" })),
|
|
684
|
+
targetRepositories: Type.Optional(Type.Array(Type.String(), { description: "Slice-wide default repository id(s) (parent workspace); must match a Declared Repository. Omit for single-repo projects." })),
|
|
683
685
|
// Single-writer v3 audit trail (Stream 2): caller-provided actor identity + causation.
|
|
684
686
|
actorName: Type.Optional(Type.String({ description: "Caller-provided actor identity for the audit trail (e.g. 'executor-01', 'gsd-orchestrator')" })),
|
|
685
687
|
triggerReason: Type.Optional(Type.String({ description: "Caller-provided reason this action was triggered (e.g. 'plan-phase complete')" })),
|
|
@@ -749,6 +751,7 @@ export function registerDbTools(pi) {
|
|
|
749
751
|
inputs: Type.Array(Type.String(), { description: "Array<string> of input files or references; pass [\"path\"] or [], never a single string" }),
|
|
750
752
|
expectedOutput: Type.Array(Type.String(), { description: "Array<string> of files this task creates or overwrites; pass [\"path\"] or [], never prose or a single string" }),
|
|
751
753
|
observabilityImpact: Type.Optional(Type.String({ description: "Task observability impact" })),
|
|
754
|
+
targetRepositories: Type.Optional(Type.Array(Type.String(), { description: "Repository id(s) this task touches (parent workspace); must match a Declared Repository. Omit for single-repo projects." })),
|
|
752
755
|
// Single-writer v3 audit trail (Stream 2): caller-provided actor identity + causation.
|
|
753
756
|
actorName: Type.Optional(Type.String({ description: "Caller-provided actor identity for the audit trail (e.g. 'executor-01', 'gsd-orchestrator')" })),
|
|
754
757
|
triggerReason: Type.Optional(Type.String({ description: "Caller-provided reason this action was triggered (e.g. 'plan-phase complete')" })),
|