@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
|
@@ -186,6 +186,58 @@ test("uok gitops turn action commit creates commit with unit trailer", () => {
|
|
|
186
186
|
}
|
|
187
187
|
});
|
|
188
188
|
|
|
189
|
+
test("uok gitops turn action commit defaults to child repositories in parent mode", () => {
|
|
190
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-uok-gitops-parent-commit-"));
|
|
191
|
+
try {
|
|
192
|
+
initRepo(root);
|
|
193
|
+
mkdirSync(join(root, ".gsd"), { recursive: true });
|
|
194
|
+
mkdirSync(join(root, "frontend"), { recursive: true });
|
|
195
|
+
mkdirSync(join(root, "backend"), { recursive: true });
|
|
196
|
+
initRepo(join(root, "frontend"));
|
|
197
|
+
initRepo(join(root, "backend"));
|
|
198
|
+
writeFileSync(join(root, ".gitignore"), "frontend/\nbackend/\n", "utf-8");
|
|
199
|
+
run("git add .gitignore", root);
|
|
200
|
+
run('git commit -m "chore: ignore nested repos"', root);
|
|
201
|
+
writeFileSync(join(root, ".gsd", "PREFERENCES.md"), `---
|
|
202
|
+
version: 1
|
|
203
|
+
workspace:
|
|
204
|
+
mode: parent
|
|
205
|
+
repositories:
|
|
206
|
+
frontend:
|
|
207
|
+
path: frontend
|
|
208
|
+
backend:
|
|
209
|
+
path: backend
|
|
210
|
+
---
|
|
211
|
+
`, "utf-8");
|
|
212
|
+
run("git add .gsd/PREFERENCES.md", root);
|
|
213
|
+
run('git commit -m "chore: configure parent workspace repos"', root);
|
|
214
|
+
|
|
215
|
+
const rootHeadBefore = run("git rev-parse HEAD", root);
|
|
216
|
+
writeFileSync(join(root, "frontend", "README.md"), "# frontend dirty\n", "utf-8");
|
|
217
|
+
writeFileSync(join(root, "backend", "README.md"), "# backend dirty\n", "utf-8");
|
|
218
|
+
|
|
219
|
+
const result = runTurnGitAction({
|
|
220
|
+
basePath: root,
|
|
221
|
+
action: "commit",
|
|
222
|
+
unitType: "execute-task",
|
|
223
|
+
unitId: "M001/S01/T-parent",
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
assert.equal(result.status, "ok");
|
|
227
|
+
assert.equal(result.commitMessages?.project, undefined);
|
|
228
|
+
assert.equal(typeof result.commitMessages?.frontend, "string");
|
|
229
|
+
assert.equal(typeof result.commitMessages?.backend, "string");
|
|
230
|
+
assert.equal(result.commitMessage, result.commitMessages?.frontend);
|
|
231
|
+
assert.equal(run("git rev-parse HEAD", root), rootHeadBefore);
|
|
232
|
+
assert.equal(run("git status --porcelain", join(root, "frontend")), "");
|
|
233
|
+
assert.equal(run("git status --porcelain", join(root, "backend")), "");
|
|
234
|
+
assert.ok(run("git log -1 --pretty=%B", join(root, "frontend")).includes("GSD-Unit: M001/S01/T-parent"));
|
|
235
|
+
assert.ok(run("git log -1 --pretty=%B", join(root, "backend")).includes("GSD-Unit: M001/S01/T-parent"));
|
|
236
|
+
} finally {
|
|
237
|
+
rmSync(root, { recursive: true, force: true });
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
189
241
|
test("uok gitops turn action commit reuses the collected dirty status", () => {
|
|
190
242
|
const repo = makeRepo();
|
|
191
243
|
const wrapperDir = mkdtempSync(join(tmpdir(), "gsd-uok-git-wrapper-"));
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
|
|
24
24
|
function makeProjectDir(label = "gsd-vsp-"): string {
|
|
25
25
|
const dir = realpathSync(mkdtempSync(join(tmpdir(), label)));
|
|
26
|
-
mkdirSync(join(dir, ".gsd", "
|
|
26
|
+
mkdirSync(join(dir, ".gsd", "phases", "01-m001"), { recursive: true });
|
|
27
27
|
return dir;
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -236,4 +236,56 @@ describe("validator-scope-parity: scope uses canonical projectRoot not worktree
|
|
|
236
236
|
"verifyExpectedArtifactForScope should return false when artifact is absent",
|
|
237
237
|
);
|
|
238
238
|
});
|
|
239
|
+
|
|
240
|
+
test("verifyExpectedArtifactForScope rejects zero-slice scoped plan-milestone roadmaps", () => {
|
|
241
|
+
const ws = createWorkspace(base);
|
|
242
|
+
const scope = scopeMilestone(ws, "M001");
|
|
243
|
+
|
|
244
|
+
writeFileSync(scope.roadmapFile(), [
|
|
245
|
+
"# M001: Placeholder",
|
|
246
|
+
"",
|
|
247
|
+
"## Slices",
|
|
248
|
+
"",
|
|
249
|
+
"_TBD_",
|
|
250
|
+
"",
|
|
251
|
+
].join("\n"));
|
|
252
|
+
|
|
253
|
+
const ready = verifyExpectedArtifactForScope(scope, "plan-milestone", "M001");
|
|
254
|
+
assert.equal(
|
|
255
|
+
ready,
|
|
256
|
+
false,
|
|
257
|
+
"scoped plan-milestone verification must parse roadmap content, not only check existence",
|
|
258
|
+
);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
test("verifyExpectedArtifactForScope verifies the scoped plan-milestone roadmap path", () => {
|
|
262
|
+
const ws = createWorkspace(base);
|
|
263
|
+
const realScope = scopeMilestone(ws, "M001");
|
|
264
|
+
const scopedRoadmap = join(base, "scoped-M001-ROADMAP.md");
|
|
265
|
+
const scope = {
|
|
266
|
+
...realScope,
|
|
267
|
+
roadmapFile: () => scopedRoadmap,
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
writeFileSync(scopedRoadmap, [
|
|
271
|
+
"# M001: Scoped roadmap",
|
|
272
|
+
"",
|
|
273
|
+
"## Slices",
|
|
274
|
+
"",
|
|
275
|
+
"- [ ] **S01: First slice** `risk:low` `depends:[]`",
|
|
276
|
+
" > After this: a real slice exists.",
|
|
277
|
+
"",
|
|
278
|
+
].join("\n"));
|
|
279
|
+
|
|
280
|
+
assert.equal(
|
|
281
|
+
resolveExpectedArtifactPathForScope(scope, "plan-milestone", "M001"),
|
|
282
|
+
scopedRoadmap,
|
|
283
|
+
"scoped plan-milestone artifact path must come from scope.roadmapFile()",
|
|
284
|
+
);
|
|
285
|
+
assert.equal(
|
|
286
|
+
verifyExpectedArtifactForScope(scope, "plan-milestone", "M001"),
|
|
287
|
+
true,
|
|
288
|
+
"scoped plan-milestone verification must read the roadmap from scope.roadmapFile()",
|
|
289
|
+
);
|
|
290
|
+
});
|
|
239
291
|
});
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
getArtifact,
|
|
13
13
|
getAssessment,
|
|
14
14
|
insertAssessment,
|
|
15
|
+
insertGateRow,
|
|
15
16
|
insertMilestone,
|
|
16
17
|
upsertRequirement,
|
|
17
18
|
getAllMilestones,
|
|
@@ -21,6 +22,7 @@ import { claimMilestoneLease, getMilestoneLease } from "../db/milestone-leases.t
|
|
|
21
22
|
import { deriveState, invalidateStateCache } from "../state.ts";
|
|
22
23
|
import { autoSession } from "../auto-runtime-state.ts";
|
|
23
24
|
import { normalizeRealPath } from "../paths.ts";
|
|
25
|
+
import { recordUnitHarnessAbort } from "../unit-runtime.ts";
|
|
24
26
|
import { markApprovalGateVerified, markDepthVerified, clearDiscussionFlowState, loadWriteGateSnapshot, setPendingGate } from "../bootstrap/write-gate.ts";
|
|
25
27
|
import {
|
|
26
28
|
executeCompleteMilestone,
|
|
@@ -37,6 +39,11 @@ import {
|
|
|
37
39
|
executeValidateMilestone,
|
|
38
40
|
executeUatResultSave,
|
|
39
41
|
} from "../tools/workflow-tool-executors.ts";
|
|
42
|
+
import {
|
|
43
|
+
initNotificationStore,
|
|
44
|
+
readNotifications,
|
|
45
|
+
_resetNotificationStore,
|
|
46
|
+
} from "../notification-store.ts";
|
|
40
47
|
|
|
41
48
|
function makeTmpBase(): string {
|
|
42
49
|
const base = join(tmpdir(), `gsd-workflow-executors-${randomUUID()}`);
|
|
@@ -910,6 +917,104 @@ test("executeUatResultSave accepts gsd_uat_exec evidence written in a milestone
|
|
|
910
917
|
}
|
|
911
918
|
});
|
|
912
919
|
|
|
920
|
+
test("executeUatResultSave leaves UAT pending after a harness-aborted turn", async () => {
|
|
921
|
+
const base = makeTmpBase();
|
|
922
|
+
const worktree = join(base, ".gsd", "worktrees", "M001");
|
|
923
|
+
const worktreeExecDir = join(worktree, ".gsd", "exec");
|
|
924
|
+
const evidenceId = "harness-aborted-uat-evidence";
|
|
925
|
+
const startedAt = Date.now();
|
|
926
|
+
try {
|
|
927
|
+
openTestDb(base);
|
|
928
|
+
seedMilestone("M001", "Milestone One");
|
|
929
|
+
seedSlice("M001", "S04", "complete");
|
|
930
|
+
mkdirSync(worktreeExecDir, { recursive: true });
|
|
931
|
+
writeFileSync(
|
|
932
|
+
join(worktreeExecDir, `${evidenceId}.meta.json`),
|
|
933
|
+
JSON.stringify({
|
|
934
|
+
id: evidenceId,
|
|
935
|
+
metadata: {
|
|
936
|
+
kind: "uat_exec",
|
|
937
|
+
milestoneId: "M001",
|
|
938
|
+
sliceId: "S04",
|
|
939
|
+
checkId: "UAT-01",
|
|
940
|
+
intent: "uat-runtime-check",
|
|
941
|
+
},
|
|
942
|
+
}),
|
|
943
|
+
"utf-8",
|
|
944
|
+
);
|
|
945
|
+
_getAdapter()!.prepare(
|
|
946
|
+
`INSERT INTO quality_gates (milestone_id, slice_id, gate_id, scope, task_id, status)
|
|
947
|
+
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
948
|
+
).run("M001", "S04", "UAT", "slice", "", "pending");
|
|
949
|
+
autoSession.reset();
|
|
950
|
+
autoSession.active = true;
|
|
951
|
+
autoSession.basePath = base;
|
|
952
|
+
autoSession.currentUnit = { type: "run-uat", id: "M001/S04", startedAt, workspaceRoot: worktree };
|
|
953
|
+
recordUnitHarnessAbort(base, "run-uat", "M001/S04", startedAt, {
|
|
954
|
+
kind: "tool-loop-guard",
|
|
955
|
+
reason: "Tool loop detected (repeated tool): browser_click called 7 times this turn.",
|
|
956
|
+
toolName: "browser_click",
|
|
957
|
+
count: 7,
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
const result = await inProjectDir(worktree, () => executeUatResultSave({
|
|
961
|
+
milestoneId: "M001",
|
|
962
|
+
sliceId: "S04",
|
|
963
|
+
uatType: "runtime-executable",
|
|
964
|
+
verdict: "FAIL",
|
|
965
|
+
checks: [{
|
|
966
|
+
id: "UAT-01",
|
|
967
|
+
description: "Runtime behavior could not be fully evaluated before harness abort",
|
|
968
|
+
mode: "runtime",
|
|
969
|
+
result: "FAIL",
|
|
970
|
+
evidence: [{ kind: "gsd_uat_exec", ref: evidenceId }],
|
|
971
|
+
notes: "Partial failure should be retried because the harness aborted the turn.",
|
|
972
|
+
}],
|
|
973
|
+
presentation: {
|
|
974
|
+
surface: "mcp",
|
|
975
|
+
presentedTools: [
|
|
976
|
+
"gsd_uat_exec",
|
|
977
|
+
"gsd_uat_result_save",
|
|
978
|
+
"gsd_resume",
|
|
979
|
+
"gsd_milestone_status",
|
|
980
|
+
"gsd_journal_query",
|
|
981
|
+
],
|
|
982
|
+
blockedTools: [
|
|
983
|
+
{ name: "gsd_exec", reason: "forbidden during run-uat" },
|
|
984
|
+
{ name: "gsd_summary_save", reason: "forbidden during run-uat" },
|
|
985
|
+
{ name: "gsd_save_gate_result", reason: "forbidden during run-uat" },
|
|
986
|
+
],
|
|
987
|
+
},
|
|
988
|
+
notes: "Partial UAT result after harness abort.",
|
|
989
|
+
}, worktree));
|
|
990
|
+
|
|
991
|
+
assert.equal(result.isError, true);
|
|
992
|
+
assert.equal(result.details.operation, "save_uat_result");
|
|
993
|
+
assert.equal(result.details.error, "harness_aborted_needs_retry");
|
|
994
|
+
assert.equal(result.details.retryable, true);
|
|
995
|
+
assert.equal(result.details.harnessAbortKind, "tool-loop-guard");
|
|
996
|
+
assert.equal(
|
|
997
|
+
existsSync(join(base, ".gsd", "uat", "M001", "S04", "attempt-1.json")),
|
|
998
|
+
false,
|
|
999
|
+
"harness-aborted UAT should not persist an attempt artifact",
|
|
1000
|
+
);
|
|
1001
|
+
assert.equal(
|
|
1002
|
+
existsSync(join(base, ".gsd", "phases", "01-m001", "01-04-ASSESSMENT.md")),
|
|
1003
|
+
false,
|
|
1004
|
+
"harness-aborted UAT should not write an ASSESSMENT verdict",
|
|
1005
|
+
);
|
|
1006
|
+
const row = _getAdapter()!.prepare(
|
|
1007
|
+
"SELECT status, verdict FROM quality_gates WHERE milestone_id = ? AND slice_id = ? AND gate_id = ?",
|
|
1008
|
+
).get("M001", "S04", "UAT") as Record<string, unknown>;
|
|
1009
|
+
assert.equal(row.status, "pending");
|
|
1010
|
+
assert.equal(row.verdict, "");
|
|
1011
|
+
} finally {
|
|
1012
|
+
autoSession.reset();
|
|
1013
|
+
closeDatabase();
|
|
1014
|
+
cleanup(base);
|
|
1015
|
+
}
|
|
1016
|
+
});
|
|
1017
|
+
|
|
913
1018
|
test("executeUatResultSave supplies canonical presentation and normalizes verdict casing", async () => {
|
|
914
1019
|
const base = makeTmpBase();
|
|
915
1020
|
const worktree = join(base, ".gsd", "worktrees", "M001");
|
|
@@ -1100,13 +1205,14 @@ test("executeUatResultSave merges canonical plan ID and read-only tools when pre
|
|
|
1100
1205
|
}
|
|
1101
1206
|
});
|
|
1102
1207
|
|
|
1103
|
-
test("executeUatResultSave surfaces the worktree validation path for NEEDS-HUMAN checks", async () => {
|
|
1208
|
+
test("executeUatResultSave surfaces the worktree validation path and notification for NEEDS-HUMAN checks", async () => {
|
|
1104
1209
|
const base = makeTmpBase();
|
|
1105
1210
|
const worktree = join(base, ".gsd", "worktrees", "M001");
|
|
1106
1211
|
const worktreeExecDir = join(worktree, ".gsd", "exec");
|
|
1107
1212
|
const evidenceId = "uat-human-validation-evidence";
|
|
1108
1213
|
try {
|
|
1109
1214
|
openTestDb(base);
|
|
1215
|
+
initNotificationStore(base);
|
|
1110
1216
|
seedMilestone("M001", "Milestone One");
|
|
1111
1217
|
seedSlice("M001", "S07", "complete");
|
|
1112
1218
|
mkdirSync(worktreeExecDir, { recursive: true });
|
|
@@ -1166,7 +1272,17 @@ test("executeUatResultSave surfaces the worktree validation path for NEEDS-HUMAN
|
|
|
1166
1272
|
assert.match(assessment, /## Manual Validation/);
|
|
1167
1273
|
assert.ok(assessment.includes(worktree), "assessment should include the worktree checkout path");
|
|
1168
1274
|
assert.match(assessment, /git worktree/);
|
|
1275
|
+
|
|
1276
|
+
const notifications = readNotifications(base, { kind: "uat-needs-human", scope: "M001/S07" });
|
|
1277
|
+
assert.equal(notifications.length, 1);
|
|
1278
|
+
assert.equal(notifications[0].severity, "warning");
|
|
1279
|
+
assert.equal(notifications[0].source, "notify");
|
|
1280
|
+
assert.equal(
|
|
1281
|
+
notifications[0].message,
|
|
1282
|
+
"UAT for M001/S07 has NEEDS-HUMAN checks awaiting human validation",
|
|
1283
|
+
);
|
|
1169
1284
|
} finally {
|
|
1285
|
+
_resetNotificationStore();
|
|
1170
1286
|
closeDatabase();
|
|
1171
1287
|
cleanup(base);
|
|
1172
1288
|
}
|
|
@@ -1757,6 +1873,97 @@ test("executeSaveGateResult validates inputs and persists verdicts", async () =>
|
|
|
1757
1873
|
}
|
|
1758
1874
|
});
|
|
1759
1875
|
|
|
1876
|
+
test("executeSaveGateResult leaves quality gates pending after a retryable tool error", async () => {
|
|
1877
|
+
const base = makeTmpBase();
|
|
1878
|
+
const startedAt = Date.now();
|
|
1879
|
+
try {
|
|
1880
|
+
openTestDb(base);
|
|
1881
|
+
seedMilestone("M005", "Milestone Five");
|
|
1882
|
+
seedSlice("M005", "S05", "pending");
|
|
1883
|
+
insertGateRow({ milestoneId: "M005", sliceId: "S05", gateId: "Q3", scope: "slice" });
|
|
1884
|
+
autoSession.reset();
|
|
1885
|
+
autoSession.active = true;
|
|
1886
|
+
autoSession.basePath = base;
|
|
1887
|
+
autoSession.currentUnit = { type: "plan-slice", id: "M005/S05", startedAt };
|
|
1888
|
+
recordUnitHarnessAbort(base, "plan-slice", "M005/S05", startedAt, {
|
|
1889
|
+
kind: "tool-error",
|
|
1890
|
+
reason: "Input validation error: selector is required",
|
|
1891
|
+
toolName: "browser_click",
|
|
1892
|
+
});
|
|
1893
|
+
|
|
1894
|
+
const result = await inProjectDir(base, () => executeSaveGateResult({
|
|
1895
|
+
milestoneId: "M005",
|
|
1896
|
+
sliceId: "S05",
|
|
1897
|
+
gateId: "Q3",
|
|
1898
|
+
verdict: "flag",
|
|
1899
|
+
rationale: "Partial gate failure after harness abort.",
|
|
1900
|
+
findings: "This should not be persisted as a product-quality finding.",
|
|
1901
|
+
}, base));
|
|
1902
|
+
|
|
1903
|
+
assert.equal(result.isError, true);
|
|
1904
|
+
assert.equal(result.details.operation, "save_gate_result");
|
|
1905
|
+
assert.equal(result.details.error, "harness_aborted_needs_retry");
|
|
1906
|
+
assert.equal(result.details.retryable, true);
|
|
1907
|
+
assert.equal(result.details.harnessAbortKind, "tool-error");
|
|
1908
|
+
const row = _getAdapter()!.prepare(
|
|
1909
|
+
"SELECT status, verdict, rationale, findings FROM quality_gates WHERE milestone_id = ? AND slice_id = ? AND gate_id = ? AND task_id = ''",
|
|
1910
|
+
).get("M005", "S05", "Q3") as Record<string, unknown>;
|
|
1911
|
+
assert.equal(row.status, "pending");
|
|
1912
|
+
assert.equal(row.verdict, "");
|
|
1913
|
+
assert.equal(row.rationale, "");
|
|
1914
|
+
assert.equal(row.findings, "");
|
|
1915
|
+
} finally {
|
|
1916
|
+
autoSession.reset();
|
|
1917
|
+
closeDatabase();
|
|
1918
|
+
cleanup(base);
|
|
1919
|
+
}
|
|
1920
|
+
});
|
|
1921
|
+
|
|
1922
|
+
test("executeSaveGateResult leaves quality gates pending after a turn abort", async () => {
|
|
1923
|
+
const base = makeTmpBase();
|
|
1924
|
+
const startedAt = Date.now();
|
|
1925
|
+
try {
|
|
1926
|
+
openTestDb(base);
|
|
1927
|
+
seedMilestone("M005", "Milestone Five");
|
|
1928
|
+
seedSlice("M005", "S05", "pending");
|
|
1929
|
+
insertGateRow({ milestoneId: "M005", sliceId: "S05", gateId: "Q3", scope: "slice" });
|
|
1930
|
+
autoSession.reset();
|
|
1931
|
+
autoSession.active = true;
|
|
1932
|
+
autoSession.basePath = base;
|
|
1933
|
+
autoSession.currentUnit = { type: "plan-slice", id: "M005/S05", startedAt };
|
|
1934
|
+
recordUnitHarnessAbort(base, "plan-slice", "M005/S05", startedAt, {
|
|
1935
|
+
kind: "turn-abort",
|
|
1936
|
+
reason: "Agent turn aborted before the gate evaluation completed. origin=timeout stopReason=aborted",
|
|
1937
|
+
});
|
|
1938
|
+
|
|
1939
|
+
const result = await inProjectDir(base, () => executeSaveGateResult({
|
|
1940
|
+
milestoneId: "M005",
|
|
1941
|
+
sliceId: "S05",
|
|
1942
|
+
gateId: "Q3",
|
|
1943
|
+
verdict: "flag",
|
|
1944
|
+
rationale: "Partial gate failure after a turn abort.",
|
|
1945
|
+
findings: "This should not be persisted as a product-quality finding.",
|
|
1946
|
+
}, base));
|
|
1947
|
+
|
|
1948
|
+
assert.equal(result.isError, true);
|
|
1949
|
+
assert.equal(result.details.operation, "save_gate_result");
|
|
1950
|
+
assert.equal(result.details.error, "harness_aborted_needs_retry");
|
|
1951
|
+
assert.equal(result.details.retryable, true);
|
|
1952
|
+
assert.equal(result.details.harnessAbortKind, "turn-abort");
|
|
1953
|
+
const row = _getAdapter()!.prepare(
|
|
1954
|
+
"SELECT status, verdict, rationale, findings FROM quality_gates WHERE milestone_id = ? AND slice_id = ? AND gate_id = ? AND task_id = ''",
|
|
1955
|
+
).get("M005", "S05", "Q3") as Record<string, unknown>;
|
|
1956
|
+
assert.equal(row.status, "pending");
|
|
1957
|
+
assert.equal(row.verdict, "");
|
|
1958
|
+
assert.equal(row.rationale, "");
|
|
1959
|
+
assert.equal(row.findings, "");
|
|
1960
|
+
} finally {
|
|
1961
|
+
autoSession.reset();
|
|
1962
|
+
closeDatabase();
|
|
1963
|
+
cleanup(base);
|
|
1964
|
+
}
|
|
1965
|
+
});
|
|
1966
|
+
|
|
1760
1967
|
test("executeSaveGateResult fails when no canonical gate row is updated", async () => {
|
|
1761
1968
|
const base = makeTmpBase();
|
|
1762
1969
|
try {
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
rmSync,
|
|
9
9
|
realpathSync,
|
|
10
10
|
symlinkSync,
|
|
11
|
+
writeFileSync,
|
|
11
12
|
} from "node:fs";
|
|
12
13
|
import { tmpdir } from "node:os";
|
|
13
14
|
import { join } from "node:path";
|
|
@@ -118,17 +119,17 @@ describe("scopeMilestone path methods", () => {
|
|
|
118
119
|
rmSync(projectDir, { recursive: true, force: true });
|
|
119
120
|
});
|
|
120
121
|
|
|
121
|
-
test("produces
|
|
122
|
+
test("produces flat-phase paths for a known milestone ID", () => {
|
|
122
123
|
const ws = createWorkspace(projectDir);
|
|
123
124
|
const scope = scopeMilestone(ws, MID);
|
|
124
125
|
const gsd = ws.contract.projectGsd;
|
|
125
126
|
|
|
126
127
|
assert.equal(scope.milestoneId, MID);
|
|
127
|
-
assert.equal(scope.contextFile(), join(gsd, "
|
|
128
|
-
assert.equal(scope.roadmapFile(), join(gsd, "
|
|
128
|
+
assert.equal(scope.contextFile(), join(gsd, "phases", "01-m001", "01-CONTEXT.md"));
|
|
129
|
+
assert.equal(scope.roadmapFile(), join(gsd, "phases", "01-m001", "01-ROADMAP.md"));
|
|
129
130
|
assert.equal(scope.stateFile(), join(gsd, "STATE.md"));
|
|
130
131
|
assert.equal(scope.dbPath(), ws.contract.projectDb);
|
|
131
|
-
assert.equal(scope.milestoneDir(), join(gsd, "
|
|
132
|
+
assert.equal(scope.milestoneDir(), join(gsd, "phases", "01-m001"));
|
|
132
133
|
assert.equal(scope.metaJson(), join(gsd, `${MID}-META.json`));
|
|
133
134
|
});
|
|
134
135
|
|
|
@@ -144,6 +145,19 @@ describe("scopeMilestone path methods", () => {
|
|
|
144
145
|
assert.equal(scope1.milestoneDir(), scope2.milestoneDir());
|
|
145
146
|
assert.equal(scope1.metaJson(), scope2.metaJson());
|
|
146
147
|
});
|
|
148
|
+
|
|
149
|
+
test("uses legacy milestone paths when legacy content exists", () => {
|
|
150
|
+
const legacyDir = join(projectDir, ".gsd", "milestones", MID);
|
|
151
|
+
mkdirSync(legacyDir, { recursive: true });
|
|
152
|
+
writeFileSync(join(legacyDir, `${MID}-CONTEXT.md`), "# Legacy context\n");
|
|
153
|
+
|
|
154
|
+
const ws = createWorkspace(projectDir);
|
|
155
|
+
const scope = scopeMilestone(ws, MID);
|
|
156
|
+
|
|
157
|
+
assert.equal(scope.contextFile(), join(legacyDir, `${MID}-CONTEXT.md`));
|
|
158
|
+
assert.equal(scope.roadmapFile(), join(legacyDir, `${MID}-ROADMAP.md`));
|
|
159
|
+
assert.equal(scope.milestoneDir(), legacyDir);
|
|
160
|
+
});
|
|
147
161
|
});
|
|
148
162
|
|
|
149
163
|
describe("createWorkspace: contract.projectGsd is realpath-canonicalized when basePath is a symlink", () => {
|
|
@@ -107,7 +107,7 @@ function makeDeps(
|
|
|
107
107
|
// real `getAutoWorktreePath` / `createAutoWorktree` / etc. and the
|
|
108
108
|
// success/existing/failure branches won't fire as expected.
|
|
109
109
|
const deps: LegacyTestDeps = {
|
|
110
|
-
|
|
110
|
+
mergeMilestone: () => ({ pushed: false, codeFilesChanged: true }),
|
|
111
111
|
worktreeProjection: new WorktreeStateProjection(),
|
|
112
112
|
// ADR-016 phase 2 / C4 (#5627): GitServiceImpl constructor → factory.
|
|
113
113
|
gitServiceFactory: () => ({}) as unknown as ReturnType<
|
|
@@ -283,7 +283,7 @@ describe("worktree journal events", () => {
|
|
|
283
283
|
|
|
284
284
|
const s = makeSession({ basePath: wt, originalBasePath: tmp });
|
|
285
285
|
const deps = makeDeps({
|
|
286
|
-
|
|
286
|
+
mergeMilestone: () => ({ pushed: false, codeFilesChanged: true }),
|
|
287
287
|
});
|
|
288
288
|
process.chdir(wt);
|
|
289
289
|
|
|
@@ -304,7 +304,7 @@ describe("worktree journal events", () => {
|
|
|
304
304
|
const wt = setupMergeWorktree(tmp, "M001");
|
|
305
305
|
const s = makeSession({ basePath: wt, originalBasePath: tmp });
|
|
306
306
|
const deps = makeDeps({
|
|
307
|
-
|
|
307
|
+
mergeMilestone: () => { throw new Error("conflict in main"); },
|
|
308
308
|
});
|
|
309
309
|
// Since #4380, mergeAndExit re-throws all errors after emitting the journal
|
|
310
310
|
// event and restoring state. Lifecycle now wraps that throw in a typed
|
|
@@ -347,7 +347,7 @@ describe("worktree journal events", () => {
|
|
|
347
347
|
const s = makeSession({ basePath: wt, originalBasePath: tmp });
|
|
348
348
|
let attempt = 0;
|
|
349
349
|
const deps = makeDeps({
|
|
350
|
-
|
|
350
|
+
mergeMilestone: () => {
|
|
351
351
|
attempt += 1;
|
|
352
352
|
throw new MergeConflictError(
|
|
353
353
|
attempt === 1 ? ["src/a.ts"] : ["src/b.ts", "src/c.ts"],
|
|
@@ -46,7 +46,7 @@ function makeDeps(
|
|
|
46
46
|
): LegacyTestDeps & { calls: CallLog[] } {
|
|
47
47
|
const calls: CallLog[] = [];
|
|
48
48
|
// ADR-016 phase 2 / C-track close-out: WorktreeLifecycleDeps is now a
|
|
49
|
-
// 3-field bag (gitServiceFactory, worktreeProjection,
|
|
49
|
+
// 3-field bag (gitServiceFactory, worktreeProjection, mergeMilestone).
|
|
50
50
|
// Tests still pass legacy override hooks via `LegacyTestDeps` — Lifecycle
|
|
51
51
|
// ignores the extras structurally and reads them through the C1-healing
|
|
52
52
|
// primitive-override pattern when stubs are needed.
|
|
@@ -73,7 +73,7 @@ function makeDeps(
|
|
|
73
73
|
},
|
|
74
74
|
autoWorktreeBranch: (mid: string) => `milestone/${mid}`,
|
|
75
75
|
teardownAutoWorktree: () => {},
|
|
76
|
-
|
|
76
|
+
mergeMilestone: () => ({ pushed: false, codeFilesChanged: true }),
|
|
77
77
|
...overrides,
|
|
78
78
|
};
|
|
79
79
|
return deps;
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* - syncWorktreeStateBack skips non-standard milestone projection dir names
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, readFileSync } from 'node:fs';
|
|
26
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, readFileSync, statSync } from 'node:fs';
|
|
27
27
|
import { join } from 'node:path';
|
|
28
28
|
import { tmpdir } from 'node:os';
|
|
29
29
|
|
|
@@ -148,6 +148,36 @@ describe('worktree-sync-milestones', async () => {
|
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
// ─── 3c. artifact file/dir collisions preserve existing worktree files ─
|
|
152
|
+
console.log('\n=== 3c. artifact file/dir collisions preserve existing worktree files (#1157) ===');
|
|
153
|
+
{
|
|
154
|
+
const mainBase = createBase('main');
|
|
155
|
+
const wtBase = createBase('wt');
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
const mainSliceDir = join(mainBase, '.gsd', 'milestones', 'M001', 'slices', 'S01');
|
|
159
|
+
mkdirSync(join(mainSliceDir, 'S01-SUMMARY.md'), { recursive: true });
|
|
160
|
+
mkdirSync(join(mainSliceDir, 'S01-ASSESSMENT.md'), { recursive: true });
|
|
161
|
+
|
|
162
|
+
const wtSliceDir = join(wtBase, '.gsd', 'milestones', 'M001', 'slices', 'S01');
|
|
163
|
+
mkdirSync(wtSliceDir, { recursive: true });
|
|
164
|
+
writeFileSync(join(wtSliceDir, 'S01-SUMMARY.md'), '# Valid summary\n');
|
|
165
|
+
writeFileSync(join(wtSliceDir, 'S01-ASSESSMENT.md'), '---\nverdict: pass\n---\n# Valid assessment\n');
|
|
166
|
+
|
|
167
|
+
syncProjectRootToWorktree(mainBase, wtBase, 'M001');
|
|
168
|
+
|
|
169
|
+
const summaryPath = join(wtSliceDir, 'S01-SUMMARY.md');
|
|
170
|
+
const assessmentPath = join(wtSliceDir, 'S01-ASSESSMENT.md');
|
|
171
|
+
assert.ok(statSync(summaryPath).isFile(), 'existing SUMMARY.md stays a file');
|
|
172
|
+
assert.ok(statSync(assessmentPath).isFile(), 'existing ASSESSMENT.md stays a file');
|
|
173
|
+
assert.equal(readFileSync(summaryPath, 'utf-8'), '# Valid summary\n');
|
|
174
|
+
assert.equal(readFileSync(assessmentPath, 'utf-8'), '---\nverdict: pass\n---\n# Valid assessment\n');
|
|
175
|
+
} finally {
|
|
176
|
+
cleanup(mainBase);
|
|
177
|
+
cleanup(wtBase);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
151
181
|
// ─── 4. No-op when paths are equal ────────────────────────────────────
|
|
152
182
|
console.log('\n=== 4. no-op when paths are equal ===');
|
|
153
183
|
{
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
existsSync,
|
|
21
21
|
realpathSync,
|
|
22
22
|
readFileSync,
|
|
23
|
+
readdirSync,
|
|
23
24
|
} from "node:fs";
|
|
24
25
|
import { join } from "node:path";
|
|
25
26
|
import { tmpdir } from "node:os";
|
|
@@ -132,6 +133,37 @@ describe("worktree-teardown-safety", () => {
|
|
|
132
133
|
);
|
|
133
134
|
});
|
|
134
135
|
|
|
136
|
+
it("removeWorktree quarantines dirty worktree contents before forced cleanup", () => {
|
|
137
|
+
const tempDir = createTempRepo();
|
|
138
|
+
dirs.push(tempDir);
|
|
139
|
+
|
|
140
|
+
const wt = createWorktree(tempDir, "dirty-wt");
|
|
141
|
+
writeFileSync(join(wt.path, "generated-feature.ts"), "export const answer = 42;\n");
|
|
142
|
+
writeFileSync(join(wt.path, "README.md"), "# changed in worktree\n");
|
|
143
|
+
|
|
144
|
+
removeWorktree(tempDir, "dirty-wt", { deleteBranch: false });
|
|
145
|
+
|
|
146
|
+
assertTrue(!existsSync(wt.path), "original dirty worktree path removed after quarantine");
|
|
147
|
+
const quarantineRoot = join(tempDir, ".gsd", "quarantine", "worktrees");
|
|
148
|
+
const entries = readdirSync(quarantineRoot).filter((entry) => entry.startsWith("dirty-wt-"));
|
|
149
|
+
assertEq(entries.length, 1, "dirty worktree snapshot quarantined");
|
|
150
|
+
const quarantineDir = join(quarantineRoot, entries[0]!);
|
|
151
|
+
assertEq(
|
|
152
|
+
readFileSync(join(quarantineDir, "generated-feature.ts"), "utf-8"),
|
|
153
|
+
"export const answer = 42;\n",
|
|
154
|
+
"untracked source file survives in quarantine",
|
|
155
|
+
);
|
|
156
|
+
assertEq(
|
|
157
|
+
readFileSync(join(quarantineDir, "README.md"), "utf-8"),
|
|
158
|
+
"# changed in worktree\n",
|
|
159
|
+
"modified tracked file survives in quarantine",
|
|
160
|
+
);
|
|
161
|
+
assertTrue(
|
|
162
|
+
existsSync(join(quarantineDir, ".gsd-quarantine.json")),
|
|
163
|
+
"quarantine metadata written",
|
|
164
|
+
);
|
|
165
|
+
});
|
|
166
|
+
|
|
135
167
|
it("worktreePath always returns paths under .gsd/worktrees/", () => {
|
|
136
168
|
const tempDir = createTempRepo();
|
|
137
169
|
dirs.push(tempDir);
|