gsd-pi 2.80.0-dev.fbe7c8c6f → 2.81.0-dev.3cddbbba2
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 +47 -59
- package/dist/claude-cli-check.d.ts +30 -0
- package/dist/claude-cli-check.js +18 -7
- package/dist/cli.js +0 -19
- package/dist/headless-query.d.ts +10 -0
- package/dist/headless-query.js +6 -4
- package/dist/loader-entrypoint.d.ts +8 -0
- package/dist/loader-entrypoint.js +27 -0
- package/dist/loader.js +2 -11
- package/dist/mcp-server.d.ts +1 -0
- package/dist/mcp-server.js +6 -3
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/claude-code-cli/readiness.js +18 -7
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +40 -3
- package/dist/resources/extensions/github-sync/sync.js +4 -1
- package/dist/resources/extensions/gsd/auto/contracts.js +2 -0
- package/dist/resources/extensions/gsd/auto/loop.js +214 -17
- package/dist/resources/extensions/gsd/auto/orchestrator.js +48 -4
- package/dist/resources/extensions/gsd/auto/phases.js +372 -134
- package/dist/resources/extensions/gsd/auto/resolve.js +29 -0
- package/dist/resources/extensions/gsd/auto/run-unit.js +88 -33
- package/dist/resources/extensions/gsd/auto/session.js +18 -1
- package/dist/resources/extensions/gsd/auto/unit-runner-events.js +7 -0
- package/dist/resources/extensions/gsd/auto/verification-retry-policy.js +43 -0
- package/dist/resources/extensions/gsd/auto/workflow-dispatch-claim.js +33 -1
- package/dist/resources/extensions/gsd/auto/workflow-worker-heartbeat.js +9 -1
- package/dist/resources/extensions/gsd/auto-dashboard.js +199 -177
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +5 -32
- package/dist/resources/extensions/gsd/auto-dispatch.js +30 -11
- package/dist/resources/extensions/gsd/auto-post-unit.js +119 -79
- package/dist/resources/extensions/gsd/auto-prompts.js +103 -16
- package/dist/resources/extensions/gsd/auto-recovery.js +43 -1
- package/dist/resources/extensions/gsd/auto-runtime-state.js +5 -0
- package/dist/resources/extensions/gsd/auto-start.js +251 -16
- package/dist/resources/extensions/gsd/auto-supervisor.js +8 -1
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +2 -2
- package/dist/resources/extensions/gsd/auto-unit-closeout.js +33 -5
- package/dist/resources/extensions/gsd/auto-verification.js +12 -6
- package/dist/resources/extensions/gsd/auto-worktree.js +237 -336
- package/dist/resources/extensions/gsd/auto.js +493 -129
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +133 -12
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +44 -37
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +37 -10
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +30 -20
- package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +4 -1
- package/dist/resources/extensions/gsd/bootstrap/memory-tools.js +6 -4
- package/dist/resources/extensions/gsd/bootstrap/query-tools.js +5 -3
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +1 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +337 -55
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +4 -8
- package/dist/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.js +4 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +82 -23
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +19 -2
- package/dist/resources/extensions/gsd/clean-root-preflight.js +24 -6
- package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +4 -10
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -2
- package/dist/resources/extensions/gsd/commands-config.js +1 -1
- package/dist/resources/extensions/gsd/commands-eval-review.js +2 -2
- package/dist/resources/extensions/gsd/commands-handlers.js +23 -9
- package/dist/resources/extensions/gsd/context-budget.js +37 -2
- package/dist/resources/extensions/gsd/crash-recovery.js +56 -10
- package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -1
- package/dist/resources/extensions/gsd/db/unit-dispatches.js +92 -0
- package/dist/resources/extensions/gsd/db-base-schema.js +4 -2
- package/dist/resources/extensions/gsd/db-migration-steps.js +6 -0
- package/dist/resources/extensions/gsd/ecosystem/gsd-extension-api.js +2 -0
- package/dist/resources/extensions/gsd/git-service.js +75 -6
- package/dist/resources/extensions/gsd/gsd-db.js +46 -13
- package/dist/resources/extensions/gsd/guided-flow.js +119 -42
- package/dist/resources/extensions/gsd/health-widget-core.js +1 -1
- package/dist/resources/extensions/gsd/health-widget.js +6 -9
- package/dist/resources/extensions/gsd/init-wizard.js +4 -1
- package/dist/resources/extensions/gsd/memory-store.js +69 -12
- package/dist/resources/extensions/gsd/migrate/command.js +40 -1
- package/dist/resources/extensions/gsd/migration-auto-check.js +87 -0
- package/dist/resources/extensions/gsd/native-git-bridge.js +46 -22
- package/dist/resources/extensions/gsd/notification-overlay.js +35 -40
- package/dist/resources/extensions/gsd/orphan-stash-audit.js +101 -0
- package/dist/resources/extensions/gsd/parallel-merge.js +53 -30
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +25 -33
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +13 -3
- package/dist/resources/extensions/gsd/planning-path-scope.js +26 -0
- package/dist/resources/extensions/gsd/pre-execution-checks.js +22 -0
- package/dist/resources/extensions/gsd/prompt-loader.js +28 -2
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +22 -17
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +14 -12
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +20 -2
- package/dist/resources/extensions/gsd/prompts/discuss.md +20 -2
- package/dist/resources/extensions/gsd/prompts/execute-task.md +4 -2
- package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/quick-task.md +1 -5
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +2 -2
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
- package/dist/resources/extensions/gsd/quick.js +34 -2
- package/dist/resources/extensions/gsd/recovery-classification.js +94 -0
- package/dist/resources/extensions/gsd/slice-cadence.js +45 -2
- package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +15 -9
- package/dist/resources/extensions/gsd/state-reconciliation.js +27 -0
- package/dist/resources/extensions/gsd/tool-contract.js +50 -0
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +6 -7
- package/dist/resources/extensions/gsd/tools/complete-task.js +1 -1
- package/dist/resources/extensions/gsd/tools/context-mode-tool-result.js +15 -0
- package/dist/resources/extensions/gsd/tools/exec-search-tool.js +5 -0
- package/dist/resources/extensions/gsd/tools/exec-tool.js +3 -15
- package/dist/resources/extensions/gsd/tools/memory-tools.js +1 -0
- package/dist/resources/extensions/gsd/tools/plan-slice.js +9 -0
- package/dist/resources/extensions/gsd/tools/plan-task.js +9 -0
- package/dist/resources/extensions/gsd/tools/resume-tool.js +5 -0
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +1 -1
- package/dist/resources/extensions/gsd/tui/render-kit.js +74 -0
- package/dist/resources/extensions/gsd/unit-context-composer.js +12 -3
- package/dist/resources/extensions/gsd/unit-runtime.js +22 -0
- package/dist/resources/extensions/gsd/watch/header-renderer.js +92 -69
- package/dist/resources/extensions/gsd/watch/splash-palette.js +10 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +2 -2
- package/dist/resources/extensions/gsd/workflow-protocol.js +131 -0
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +1364 -0
- package/dist/resources/extensions/gsd/worktree-safety.js +119 -0
- package/dist/resources/extensions/gsd/worktree-state-projection.js +317 -0
- package/dist/resources/extensions/gsd/worktree-telemetry.js +3 -1
- package/dist/resources/skills/web-quality-audit/scripts/analyze.sh +0 -0
- 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 +13 -13
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- 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/page_client-reference-manifest.js +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/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
- 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/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +3 -3
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/8359.e059d86b255fce1c.js +10 -0
- package/dist/web/standalone/.next/static/chunks/app/{page-fab3ebb85b006001.js → page-752f1e2ebdaa3e45.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{webpack-0481f1221120a7c6.js → webpack-de742b64187e13fe.js} +1 -1
- package/dist/welcome-screen.d.ts +2 -7
- package/dist/welcome-screen.js +68 -75
- package/package.json +3 -3
- package/packages/daemon/package.json +2 -2
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +22 -17
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +2 -2
- package/packages/mcp-server/src/workflow-tools.test.ts +75 -2
- package/packages/mcp-server/src/workflow-tools.ts +30 -16
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/native/package.json +1 -1
- package/packages/native/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +4 -1
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/dist/agent.d.ts +9 -2
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +43 -11
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/dist/index.d.ts +1 -0
- package/packages/pi-agent-core/dist/index.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/index.js +2 -0
- package/packages/pi-agent-core/dist/index.js.map +1 -1
- package/packages/pi-agent-core/dist/token-audit.d.ts +47 -0
- package/packages/pi-agent-core/dist/token-audit.d.ts.map +1 -0
- package/packages/pi-agent-core/dist/token-audit.js +221 -0
- package/packages/pi-agent-core/dist/token-audit.js.map +1 -0
- package/packages/pi-agent-core/dist/types.d.ts +31 -0
- package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/types.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-agent-core/src/agent-loop.test.ts +128 -0
- package/packages/pi-agent-core/src/agent-loop.ts +4 -1
- package/packages/pi-agent-core/src/agent.ts +52 -11
- package/packages/pi-agent-core/src/index.ts +2 -0
- package/packages/pi-agent-core/src/token-audit.test.ts +189 -0
- package/packages/pi-agent-core/src/token-audit.ts +287 -0
- package/packages/pi-agent-core/src/types.ts +26 -10
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-ai/dist/providers/anthropic-auth.test.js +35 -13
- package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.js +21 -11
- package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.d.ts +7 -0
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +9 -7
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/minimax-tool-name.test.js +23 -14
- package/packages/pi-ai/dist/providers/minimax-tool-name.test.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +2 -0
- 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/dist/utils/oauth/github-copilot.test.js +48 -21
- package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.js +22 -21
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.js +22 -21
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-ai/src/providers/anthropic-auth.test.ts +39 -25
- package/packages/pi-ai/src/providers/anthropic-bearer-auth.test.ts +26 -22
- package/packages/pi-ai/src/providers/anthropic.ts +22 -9
- package/packages/pi-ai/src/providers/minimax-tool-name.test.ts +34 -21
- package/packages/pi-ai/src/types.ts +3 -0
- package/packages/pi-ai/src/utils/oauth/github-copilot.test.ts +56 -22
- package/packages/pi-ai/src/utils/oauth/google-antigravity.test.ts +24 -28
- package/packages/pi-ai/src/utils/oauth/google-gemini-cli.test.ts +24 -28
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +36 -1
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +30 -1
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +21 -2
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +94 -16
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +6 -2
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +11 -0
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js +9 -0
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js +103 -0
- package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts +15 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.js +66 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.test.js +24 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +8 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +6 -6
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +5 -3
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +60 -4
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/hooks-runner.test.js +2 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.js +46 -0
- package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts +10 -2
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +81 -4
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +20 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +25 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skill-tool.test.js +22 -0
- package/packages/pi-coding-agent/dist/core/skill-tool.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +6 -7
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +2 -3
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js +22 -56
- package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.js +3 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.js +12 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/find.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/tools/find.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/find.js +14 -6
- package/packages/pi-coding-agent/dist/core/tools/find.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/grep.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/tools/grep.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/grep.js +12 -3
- package/packages/pi-coding-agent/dist/core/tools/grep.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/hashline-read.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/tools/hashline-read.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/hashline-read.js +3 -1
- package/packages/pi-coding-agent/dist/core/tools/hashline-read.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts +2 -1
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.js +1 -0
- package/packages/pi-coding-agent/dist/core/tools/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/ls.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/tools/ls.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/ls.js +10 -3
- package/packages/pi-coding-agent/dist/core/tools/ls.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/read.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/tools/read.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/read.js +3 -1
- package/packages/pi-coding-agent/dist/core/tools/read.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/spawn-shell-windows.test.js +7 -62
- package/packages/pi-coding-agent/dist/core/tools/spawn-shell-windows.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/tool-target-metadata.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/tools/tool-target-metadata.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/tool-target-metadata.test.js +115 -0
- package/packages/pi-coding-agent/dist/core/tools/tool-target-metadata.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/tool-target.d.ts +19 -0
- package/packages/pi-coding-agent/dist/core/tools/tool-target.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/tool-target.js +20 -0
- package/packages/pi-coding-agent/dist/core/tools/tool-target.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/write.d.ts +4 -0
- package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.js +9 -1
- package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.js +47 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +161 -7
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.js +40 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts +0 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +30 -29
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js +10 -3
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +13 -13
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts +1 -3
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +58 -3
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts +2 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js +12 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +14 -41
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +25 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +6 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +225 -73
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.d.ts +35 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.js +152 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.d.ts +16 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.js +73 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +12 -8
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +31 -6
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +71 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +25 -3
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.js +17 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +105 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +27 -26
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +9 -6
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js +28 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js +3 -2
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +40 -1
- package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +40 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +102 -16
- package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +6 -2
- package/packages/pi-coding-agent/src/core/compaction/compaction.ts +18 -0
- package/packages/pi-coding-agent/src/core/compaction-threshold.test.ts +121 -0
- package/packages/pi-coding-agent/src/core/db-snapshot.test.ts +32 -0
- package/packages/pi-coding-agent/src/core/db-snapshot.ts +66 -0
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +10 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +5 -3
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +8 -5
- package/packages/pi-coding-agent/src/core/extensions/types.ts +63 -2
- package/packages/pi-coding-agent/src/core/hooks-runner.test.ts +2 -0
- package/packages/pi-coding-agent/src/core/sdk-tool-filter.test.ts +60 -0
- package/packages/pi-coding-agent/src/core/sdk.ts +92 -4
- package/packages/pi-coding-agent/src/core/settings-manager.ts +39 -1
- package/packages/pi-coding-agent/src/core/skill-tool.test.ts +28 -0
- package/packages/pi-coding-agent/src/core/system-prompt.ts +8 -10
- package/packages/pi-coding-agent/src/core/tools/bash-spawn-windows.test.ts +22 -66
- package/packages/pi-coding-agent/src/core/tools/bash.ts +4 -1
- package/packages/pi-coding-agent/src/core/tools/edit.ts +13 -1
- package/packages/pi-coding-agent/src/core/tools/find.ts +15 -6
- package/packages/pi-coding-agent/src/core/tools/grep.ts +13 -3
- package/packages/pi-coding-agent/src/core/tools/hashline-read.ts +4 -1
- package/packages/pi-coding-agent/src/core/tools/index.ts +8 -0
- package/packages/pi-coding-agent/src/core/tools/ls.ts +11 -3
- package/packages/pi-coding-agent/src/core/tools/read.ts +4 -1
- package/packages/pi-coding-agent/src/core/tools/spawn-shell-windows.test.ts +11 -72
- package/packages/pi-coding-agent/src/core/tools/tool-target-metadata.test.ts +127 -0
- package/packages/pi-coding-agent/src/core/tools/tool-target.ts +48 -0
- package/packages/pi-coding-agent/src/core/tools/write.ts +14 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/assistant-message-design.test.ts +56 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +228 -7
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/user-message-design.test.ts +48 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.test.ts +10 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +43 -42
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +14 -14
- package/packages/pi-coding-agent/src/modes/interactive/components/bash-execution.ts +64 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/diff.ts +13 -7
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +15 -42
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +31 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +247 -94
- package/packages/pi-coding-agent/src/modes/interactive/components/transcript-design.ts +196 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tui-style-kit.ts +94 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +14 -9
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +75 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +39 -8
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +27 -3
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme-highlight.test.ts +23 -0
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +106 -1
- package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +27 -26
- package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +9 -6
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +1 -1
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage-safety-guard.test.ts +31 -0
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage.ts +3 -2
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +14 -1
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
- package/packages/pi-tui/dist/overlay-layout.d.ts.map +1 -1
- package/packages/pi-tui/dist/overlay-layout.js +9 -6
- package/packages/pi-tui/dist/overlay-layout.js.map +1 -1
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +18 -8
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/package.json +1 -1
- package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +20 -1
- package/packages/pi-tui/src/overlay-layout.ts +10 -7
- package/packages/pi-tui/src/tui.ts +20 -8
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/packages/rpc-client/README.md +7 -0
- package/packages/rpc-client/package.json +1 -1
- package/pkg/dist/modes/interactive/theme/theme-highlight.test.d.ts +2 -0
- package/pkg/dist/modes/interactive/theme/theme-highlight.test.d.ts.map +1 -0
- package/pkg/dist/modes/interactive/theme/theme-highlight.test.js +17 -0
- package/pkg/dist/modes/interactive/theme/theme-highlight.test.js.map +1 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme.js +105 -1
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
- package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/themes.js +27 -26
- package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/readiness.ts +25 -7
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +42 -3
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +67 -0
- package/src/resources/extensions/github-sync/sync.ts +8 -1
- package/src/resources/extensions/github-sync/tests/sync-source.test.ts +6 -18
- package/src/resources/extensions/gsd/auto/contracts.ts +19 -2
- package/src/resources/extensions/gsd/auto/loop-deps.ts +19 -16
- package/src/resources/extensions/gsd/auto/loop.ts +247 -25
- package/src/resources/extensions/gsd/auto/orchestrator.ts +52 -4
- package/src/resources/extensions/gsd/auto/phases.ts +512 -202
- package/src/resources/extensions/gsd/auto/resolve.ts +42 -1
- package/src/resources/extensions/gsd/auto/run-unit.ts +97 -34
- package/src/resources/extensions/gsd/auto/session.ts +19 -1
- package/src/resources/extensions/gsd/auto/types.ts +3 -0
- package/src/resources/extensions/gsd/auto/unit-runner-events.ts +15 -0
- package/src/resources/extensions/gsd/auto/verification-retry-policy.ts +82 -0
- package/src/resources/extensions/gsd/auto/workflow-dispatch-claim.ts +63 -1
- package/src/resources/extensions/gsd/auto/workflow-worker-heartbeat.ts +14 -1
- package/src/resources/extensions/gsd/auto-dashboard.ts +249 -182
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +8 -34
- package/src/resources/extensions/gsd/auto-dispatch.ts +31 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +131 -81
- package/src/resources/extensions/gsd/auto-prompts.ts +112 -15
- package/src/resources/extensions/gsd/auto-recovery.ts +54 -0
- package/src/resources/extensions/gsd/auto-runtime-state.ts +5 -0
- package/src/resources/extensions/gsd/auto-start.ts +321 -21
- package/src/resources/extensions/gsd/auto-supervisor.ts +7 -0
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +2 -2
- package/src/resources/extensions/gsd/auto-unit-closeout.ts +51 -0
- package/src/resources/extensions/gsd/auto-verification.ts +12 -6
- package/src/resources/extensions/gsd/auto-worktree.ts +267 -360
- package/src/resources/extensions/gsd/auto.ts +578 -126
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +166 -12
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +45 -37
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +36 -10
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +32 -19
- package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +5 -1
- package/src/resources/extensions/gsd/bootstrap/memory-tools.ts +7 -4
- package/src/resources/extensions/gsd/bootstrap/query-tools.ts +6 -3
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +1 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +384 -55
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +5 -8
- package/src/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.ts +4 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +90 -22
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +19 -2
- package/src/resources/extensions/gsd/clean-root-preflight.ts +32 -7
- package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +4 -10
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +4 -2
- package/src/resources/extensions/gsd/commands-config.ts +1 -1
- package/src/resources/extensions/gsd/commands-eval-review.ts +2 -2
- package/src/resources/extensions/gsd/commands-handlers.ts +34 -15
- package/src/resources/extensions/gsd/context-budget.ts +44 -2
- package/src/resources/extensions/gsd/crash-recovery.ts +67 -10
- package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -1
- package/src/resources/extensions/gsd/db/unit-dispatches.ts +107 -0
- package/src/resources/extensions/gsd/db-base-schema.ts +4 -2
- package/src/resources/extensions/gsd/db-migration-steps.ts +8 -0
- package/src/resources/extensions/gsd/ecosystem/gsd-extension-api.ts +3 -0
- package/src/resources/extensions/gsd/git-service.ts +89 -10
- package/src/resources/extensions/gsd/gsd-db.ts +50 -13
- package/src/resources/extensions/gsd/guided-flow.ts +148 -49
- package/src/resources/extensions/gsd/health-widget-core.ts +1 -1
- package/src/resources/extensions/gsd/health-widget.ts +8 -9
- package/src/resources/extensions/gsd/init-wizard.ts +5 -1
- package/src/resources/extensions/gsd/journal.ts +2 -0
- package/src/resources/extensions/gsd/memory-store.ts +77 -12
- package/src/resources/extensions/gsd/migrate/command.ts +47 -1
- package/src/resources/extensions/gsd/migration-auto-check.ts +129 -0
- package/src/resources/extensions/gsd/native-git-bridge.ts +53 -19
- package/src/resources/extensions/gsd/notification-overlay.ts +50 -46
- package/src/resources/extensions/gsd/orphan-stash-audit.ts +117 -0
- package/src/resources/extensions/gsd/parallel-merge.ts +61 -34
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +33 -35
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +13 -3
- package/src/resources/extensions/gsd/planning-path-scope.ts +35 -0
- package/src/resources/extensions/gsd/pre-execution-checks.ts +23 -0
- package/src/resources/extensions/gsd/preferences-types.ts +1 -1
- package/src/resources/extensions/gsd/prompt-loader.ts +27 -2
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +22 -17
- package/src/resources/extensions/gsd/prompts/complete-slice.md +14 -12
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +20 -2
- package/src/resources/extensions/gsd/prompts/discuss.md +20 -2
- package/src/resources/extensions/gsd/prompts/execute-task.md +4 -2
- package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/quick-task.md +1 -5
- package/src/resources/extensions/gsd/prompts/replan-slice.md +2 -2
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
- package/src/resources/extensions/gsd/quick.ts +37 -2
- package/src/resources/extensions/gsd/recovery-classification.ts +122 -0
- package/src/resources/extensions/gsd/slice-cadence.ts +49 -2
- package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +23 -9
- package/src/resources/extensions/gsd/state-reconciliation.ts +57 -0
- package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +59 -89
- package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +47 -172
- package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +0 -35
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +134 -9
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +1125 -215
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +80 -59
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +119 -2
- package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +3 -47
- package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +363 -18
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +175 -11
- package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +54 -95
- package/src/resources/extensions/gsd/tests/auto-project-root-env.test.ts +67 -26
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +14 -1
- package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +32 -30
- package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +16 -1
- package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +32 -128
- package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +20 -54
- package/src/resources/extensions/gsd/tests/auto-start-cold-db-bootstrap.test.ts +20 -30
- package/src/resources/extensions/gsd/tests/auto-start-index-lock.test.ts +17 -29
- package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +160 -0
- package/src/resources/extensions/gsd/tests/auto-start-time-persistence.test.ts +21 -39
- package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +15 -24
- package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +44 -29
- package/src/resources/extensions/gsd/tests/auto-unit-closeout.test.ts +68 -0
- package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +39 -51
- package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +159 -213
- package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +15 -32
- package/src/resources/extensions/gsd/tests/browser-teardown.test.ts +0 -41
- package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +15 -6
- package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +34 -27
- package/src/resources/extensions/gsd/tests/cmux.test.ts +51 -53
- package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +39 -61
- package/src/resources/extensions/gsd/tests/commands-config.test.ts +26 -19
- package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +14 -1
- package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/complete-task-normalize-lists.test.ts +29 -33
- package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +45 -108
- package/src/resources/extensions/gsd/tests/context-budget.test.ts +10 -1
- package/src/resources/extensions/gsd/tests/context-store.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +90 -31
- package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +46 -11
- package/src/resources/extensions/gsd/tests/cwd-fallback-hardening.test.ts +138 -0
- package/src/resources/extensions/gsd/tests/dashboard-custom-engine.test.ts +4 -68
- package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +24 -11
- package/src/resources/extensions/gsd/tests/deferred-milestone-dir-4996.test.ts +14 -65
- package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +44 -37
- package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +100 -38
- package/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts +25 -15
- package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +313 -0
- package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +35 -17
- package/src/resources/extensions/gsd/tests/error-success-mask.test.ts +16 -21
- package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +15 -82
- package/src/resources/extensions/gsd/tests/exec-history.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +65 -0
- package/src/resources/extensions/gsd/tests/execute-task-rendering.test.ts +5 -2
- package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +219 -0
- package/src/resources/extensions/gsd/tests/finalize-survivor-branch.test.ts +151 -0
- package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +2 -20
- package/src/resources/extensions/gsd/tests/frontmatter-parse-noise.test.ts +18 -26
- package/src/resources/extensions/gsd/tests/header-renderer.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +14 -4
- package/src/resources/extensions/gsd/tests/init-skip-git.test.ts +9 -12
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/integration/commands-eval-review.integration.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/integration/git-locale.test.ts +31 -20
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +92 -0
- package/src/resources/extensions/gsd/tests/integration/milestone-transition-worktree.test.ts +0 -47
- package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +116 -24
- package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +60 -202
- package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +13 -56
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +248 -11
- package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/knowledge.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/lazy-pi-tui-import.test.ts +44 -6
- package/src/resources/extensions/gsd/tests/memory-decay-factor.test.ts +90 -0
- package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +21 -35
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +77 -12
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +267 -0
- package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +88 -98
- package/src/resources/extensions/gsd/tests/model-unittype-mapping.test.ts +70 -278
- package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +34 -2
- package/src/resources/extensions/gsd/tests/needs-remediation-revalidation.test.ts +37 -30
- package/src/resources/extensions/gsd/tests/note-captures-executed.test.ts +32 -28
- package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +78 -41
- package/src/resources/extensions/gsd/tests/notifications-handler.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +12 -182
- package/src/resources/extensions/gsd/tests/orphan-merge-bootstrap.test.ts +144 -0
- package/src/resources/extensions/gsd/tests/orphan-stash-audit.test.ts +201 -0
- package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +38 -6
- package/src/resources/extensions/gsd/tests/parallel-orchestrator-fast-forward.test.ts +113 -0
- package/src/resources/extensions/gsd/tests/phantom-ghost-detection.test.ts +24 -37
- package/src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts +9 -24
- package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +95 -75
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/plan-task.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/post-unit-state-rebuild.test.ts +36 -22
- package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +36 -30
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +45 -5
- package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +74 -4
- package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +20 -22
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +130 -32
- package/src/resources/extensions/gsd/tests/project-root-cwd-crash.test.ts +18 -36
- package/src/resources/extensions/gsd/tests/projection-no-plan-overwrite.test.ts +35 -73
- package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +76 -138
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +24 -1
- package/src/resources/extensions/gsd/tests/prompt-duplication-cuts.test.ts +230 -0
- package/src/resources/extensions/gsd/tests/prompt-path-audit.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +70 -106
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +59 -161
- package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +33 -29
- package/src/resources/extensions/gsd/tests/queue-auto-guard.test.ts +22 -196
- package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +23 -93
- package/src/resources/extensions/gsd/tests/quick-external-gsd.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/quick-turn-end-cleanup.test.ts +50 -79
- package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +27 -13
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +151 -251
- package/src/resources/extensions/gsd/tests/resource-loader-import-path.test.ts +41 -29
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +58 -69
- package/src/resources/extensions/gsd/tests/resume-dispatch-worktree.test.ts +36 -164
- package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +57 -41
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +91 -0
- package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +156 -0
- package/src/resources/extensions/gsd/tests/select-resumable-milestone.test.ts +96 -0
- package/src/resources/extensions/gsd/tests/session-model-override.test.ts +14 -9
- package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +77 -0
- package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +222 -0
- package/src/resources/extensions/gsd/tests/show-config-command.test.ts +44 -42
- package/src/resources/extensions/gsd/tests/signal-handlers.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/skip-slice-state-rebuild.test.ts +56 -24
- package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +51 -11
- package/src/resources/extensions/gsd/tests/slice-cadence.test.ts +23 -0
- package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +66 -50
- package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +68 -107
- package/src/resources/extensions/gsd/tests/slice-sequence-insert.test.ts +115 -42
- package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +21 -77
- package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +25 -116
- package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +21 -57
- package/src/resources/extensions/gsd/tests/stale-dirlistcache-4648.test.ts +29 -76
- package/src/resources/extensions/gsd/tests/stale-lockfile-recovery.test.ts +33 -24
- package/src/resources/extensions/gsd/tests/stale-slice-rows.test.ts +39 -30
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -1
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +65 -56
- package/src/resources/extensions/gsd/tests/status-db-open.test.ts +35 -40
- package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +48 -46
- package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +14 -102
- package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +78 -232
- package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +32 -35
- package/src/resources/extensions/gsd/tests/system-context-memory.test.ts +112 -0
- package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +7 -9
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +84 -309
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +295 -0
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +134 -341
- package/src/resources/extensions/gsd/tests/tui-header-lifecycle.test.ts +330 -0
- package/src/resources/extensions/gsd/tests/tui-render-kit.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +136 -4
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +8 -25
- package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +80 -1
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +5 -99
- package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +43 -36
- package/src/resources/extensions/gsd/tests/verification-retry-policy.test.ts +83 -0
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -444
- package/src/resources/extensions/gsd/tests/workflow-dispatch-claim.test.ts +142 -0
- package/src/resources/extensions/gsd/tests/workflow-logger-wiring.test.ts +44 -189
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +8 -57
- package/src/resources/extensions/gsd/tests/workflow-protocol-excerpt.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/workflow-worker-heartbeat.test.ts +32 -1
- package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +21 -44
- package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +27 -26
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +14 -13
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +197 -78
- package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +888 -0
- package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +20 -18
- package/src/resources/extensions/gsd/tests/worktree-path-injection.test.ts +22 -19
- package/src/resources/extensions/gsd/tests/worktree-project-root-degrade.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +327 -0
- package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +59 -2
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +40 -1
- package/src/resources/extensions/gsd/tests/zero-slice-roadmap-guided.test.ts +19 -13
- package/src/resources/extensions/gsd/tool-contract.ts +82 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +14 -15
- package/src/resources/extensions/gsd/tools/complete-task.ts +1 -1
- package/src/resources/extensions/gsd/tools/context-mode-tool-result.ts +25 -0
- package/src/resources/extensions/gsd/tools/exec-search-tool.ts +7 -7
- package/src/resources/extensions/gsd/tools/exec-tool.ts +4 -23
- package/src/resources/extensions/gsd/tools/memory-tools.ts +1 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +10 -0
- package/src/resources/extensions/gsd/tools/resume-tool.ts +7 -7
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +1 -1
- package/src/resources/extensions/gsd/tui/render-kit.ts +109 -0
- package/src/resources/extensions/gsd/unit-context-composer.ts +19 -4
- package/src/resources/extensions/gsd/unit-runtime.ts +25 -0
- package/src/resources/extensions/gsd/watch/header-renderer.ts +121 -79
- package/src/resources/extensions/gsd/watch/splash-palette.ts +11 -0
- package/src/resources/extensions/gsd/workflow-mcp.ts +2 -2
- package/src/resources/extensions/gsd/workflow-protocol.ts +160 -0
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +1882 -0
- package/src/resources/extensions/gsd/worktree-safety.ts +282 -0
- package/src/resources/extensions/gsd/worktree-state-projection.ts +404 -0
- package/src/resources/extensions/gsd/worktree-telemetry.ts +7 -2
- package/src/resources/skills/create-gsd-extension/templates/templates.test.ts +86 -40
- package/src/resources/skills/web-quality-audit/scripts/analyze.sh +0 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +0 -733
- package/dist/web/standalone/.next/static/chunks/8336.631939fb583761fa.js +0 -10
- package/src/resources/extensions/gsd/tests/sync-layer-scope.test.ts +0 -434
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +0 -1247
- package/src/resources/extensions/gsd/worktree-resolver.ts +0 -909
- /package/dist/web/standalone/.next/static/{yTuahMMuJzVnsov5PreWl → F5x9E6H9k_52fjqyql93y}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{yTuahMMuJzVnsov5PreWl → F5x9E6H9k_52fjqyql93y}/_ssgManifest.js +0 -0
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
// Project/App: GSD-2
|
|
2
|
+
// File Purpose: Auto-loop execution, dispatch, recovery, and cancellation regression tests.
|
|
3
|
+
|
|
1
4
|
import test, { mock } from "node:test";
|
|
2
5
|
import assert from "node:assert/strict";
|
|
3
|
-
import { mkdtempSync } from "node:fs";
|
|
6
|
+
import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
|
|
4
7
|
import { tmpdir } from "node:os";
|
|
5
8
|
import { join } from "node:path";
|
|
6
9
|
|
|
@@ -10,13 +13,21 @@ import {
|
|
|
10
13
|
_resetPendingResolve,
|
|
11
14
|
_hasPendingResolveForTest,
|
|
12
15
|
_setActiveSession,
|
|
16
|
+
_setSessionSwitchInFlight,
|
|
17
|
+
_markSessionSwitchAbortGraceWindow,
|
|
18
|
+
_clearSessionSwitchAbortGraceWindow,
|
|
19
|
+
_consumePendingSwitchCancellation,
|
|
13
20
|
isSessionSwitchInFlight,
|
|
21
|
+
isSessionSwitchAbortGraceActive,
|
|
14
22
|
} from "../auto/resolve.js";
|
|
15
|
-
import { runUnit } from "../auto/run-unit.js";
|
|
23
|
+
import { runUnit, shouldDeferUnitFailsafeTimeout } from "../auto/run-unit.js";
|
|
24
|
+
import { writeUnitRuntimeRecord, readUnitRuntimeRecord } from "../unit-runtime.js";
|
|
16
25
|
import { autoLoop } from "../auto/loop.js";
|
|
26
|
+
import { runDispatch, runUnitPhase } from "../auto/phases.js";
|
|
17
27
|
import { detectStuck } from "../auto/detect-stuck.js";
|
|
18
28
|
import type { UnitResult, AgentEndEvent } from "../auto/types.js";
|
|
19
29
|
import type { LoopDeps } from "../auto/loop-deps.js";
|
|
30
|
+
import { WorktreeStateProjection } from "../worktree-state-projection.js";
|
|
20
31
|
import { ModelPolicyDispatchBlockedError } from "../auto-model-selection.js";
|
|
21
32
|
import type { SessionLockStatus } from "../session-lock.js";
|
|
22
33
|
|
|
@@ -64,7 +75,7 @@ function makeMockSession(opts?: {
|
|
|
64
75
|
verbose: false,
|
|
65
76
|
basePath: process.cwd(),
|
|
66
77
|
cmdCtx: {
|
|
67
|
-
newSession: (options?: { abortSignal?: AbortSignal }) => {
|
|
78
|
+
newSession: (options?: { abortSignal?: AbortSignal; workspaceRoot?: string }) => {
|
|
68
79
|
opts?.onNewSessionStart?.(session);
|
|
69
80
|
if (opts?.newSessionThrows) {
|
|
70
81
|
return Promise.reject(new Error(opts.newSessionThrows));
|
|
@@ -76,7 +87,7 @@ function makeMockSession(opts?: {
|
|
|
76
87
|
setTimeout(() => {
|
|
77
88
|
// Simulate AgentSession.newSession() checking abortSignal after
|
|
78
89
|
// its internal async work (abort()) completes — this is where the
|
|
79
|
-
// real code
|
|
90
|
+
// real code selects a workspace root and rebuilds the tool runtime.
|
|
80
91
|
// If the signal is aborted, the real code discards the session.
|
|
81
92
|
opts?.onSignalCheck?.(options?.abortSignal?.aborted ?? false);
|
|
82
93
|
opts?.onNewSessionSettle?.(session);
|
|
@@ -155,6 +166,109 @@ test("resolveAgentEnd resolves a pending runUnit promise", async () => {
|
|
|
155
166
|
assert.deepEqual(result.event, event);
|
|
156
167
|
});
|
|
157
168
|
|
|
169
|
+
test("runUnit failsafe defers cancellation while timeout recovery is making fresh progress", async () => {
|
|
170
|
+
_resetPendingResolve();
|
|
171
|
+
mock.timers.enable();
|
|
172
|
+
const originalCwd = process.cwd();
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
mock.timers.setTime(10_000);
|
|
176
|
+
const ctx = makeMockCtx();
|
|
177
|
+
const pi = makeMockPi();
|
|
178
|
+
const s = makeMockSession();
|
|
179
|
+
s.basePath = mkdtempSync(join(tmpdir(), "gsd-rununit-recovery-"));
|
|
180
|
+
s.currentUnit = { type: "task", id: "T01", startedAt: 1234 };
|
|
181
|
+
|
|
182
|
+
const resultPromise = runUnit(ctx, pi, s, "task", "T01", "prompt");
|
|
183
|
+
await waitForMicrotasks(() => pi.calls.length === 1, "unit dispatch");
|
|
184
|
+
|
|
185
|
+
writeUnitRuntimeRecord(s.basePath, "task", "T01", 1234, {
|
|
186
|
+
phase: "recovered",
|
|
187
|
+
recoveryAttempts: 1,
|
|
188
|
+
lastProgressKind: "hard-recovery-retry",
|
|
189
|
+
lastProgressAt: Date.now(),
|
|
190
|
+
});
|
|
191
|
+
assert.equal(
|
|
192
|
+
shouldDeferUnitFailsafeTimeout(readUnitRuntimeRecord(s.basePath, "task", "T01"), {
|
|
193
|
+
nowMs: Date.now(),
|
|
194
|
+
currentUnitStartedAt: s.currentUnit.startedAt,
|
|
195
|
+
freshProgressMs: 30_000,
|
|
196
|
+
}),
|
|
197
|
+
true,
|
|
198
|
+
"fresh recovery runtime should defer the failsafe",
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
setTimeout(() => {
|
|
202
|
+
writeUnitRuntimeRecord(s.basePath, "task", "T01", 1234, {
|
|
203
|
+
phase: "recovered",
|
|
204
|
+
recoveryAttempts: 1,
|
|
205
|
+
lastProgressKind: "hard-recovery-retry",
|
|
206
|
+
lastProgressAt: Date.now(),
|
|
207
|
+
});
|
|
208
|
+
}, (30 * 60 * 1000) + 29_000);
|
|
209
|
+
|
|
210
|
+
mock.timers.tick((30 * 60 * 1000) + 31_000);
|
|
211
|
+
await Promise.resolve();
|
|
212
|
+
|
|
213
|
+
resolveAgentEnd(makeEvent());
|
|
214
|
+
const result = await resultPromise;
|
|
215
|
+
assert.equal(result.status, "completed");
|
|
216
|
+
} finally {
|
|
217
|
+
mock.timers.reset();
|
|
218
|
+
process.chdir(originalCwd);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test("shouldDeferUnitFailsafeTimeout rejects stale runtime progress", () => {
|
|
223
|
+
assert.equal(
|
|
224
|
+
shouldDeferUnitFailsafeTimeout({
|
|
225
|
+
version: 1,
|
|
226
|
+
unitType: "task",
|
|
227
|
+
unitId: "T01",
|
|
228
|
+
startedAt: 1234,
|
|
229
|
+
updatedAt: 1,
|
|
230
|
+
phase: "recovered",
|
|
231
|
+
wrapupWarningSent: false,
|
|
232
|
+
continueHereFired: false,
|
|
233
|
+
timeoutAt: 1,
|
|
234
|
+
lastProgressAt: 1,
|
|
235
|
+
progressCount: 1,
|
|
236
|
+
lastProgressKind: "hard-recovery-retry",
|
|
237
|
+
recoveryAttempts: 1,
|
|
238
|
+
}, {
|
|
239
|
+
nowMs: 120_000,
|
|
240
|
+
currentUnitStartedAt: 1234,
|
|
241
|
+
freshProgressMs: 30_000,
|
|
242
|
+
}),
|
|
243
|
+
false,
|
|
244
|
+
);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("shouldDeferUnitFailsafeTimeout rejects future runtime progress", () => {
|
|
248
|
+
assert.equal(
|
|
249
|
+
shouldDeferUnitFailsafeTimeout({
|
|
250
|
+
version: 1,
|
|
251
|
+
unitType: "task",
|
|
252
|
+
unitId: "T01",
|
|
253
|
+
startedAt: 1234,
|
|
254
|
+
updatedAt: 1,
|
|
255
|
+
phase: "recovered",
|
|
256
|
+
wrapupWarningSent: false,
|
|
257
|
+
continueHereFired: false,
|
|
258
|
+
timeoutAt: 1,
|
|
259
|
+
lastProgressAt: 150_000,
|
|
260
|
+
progressCount: 1,
|
|
261
|
+
lastProgressKind: "hard-recovery-retry",
|
|
262
|
+
recoveryAttempts: 1,
|
|
263
|
+
}, {
|
|
264
|
+
nowMs: 120_000,
|
|
265
|
+
currentUnitStartedAt: 1234,
|
|
266
|
+
freshProgressMs: 30_000,
|
|
267
|
+
}),
|
|
268
|
+
false,
|
|
269
|
+
);
|
|
270
|
+
});
|
|
271
|
+
|
|
158
272
|
test("resolveAgentEnd drops event when no promise is pending", () => {
|
|
159
273
|
_resetPendingResolve();
|
|
160
274
|
|
|
@@ -206,6 +320,28 @@ test("runUnit returns cancelled when session creation fails", async () => {
|
|
|
206
320
|
assert.equal(pi.calls.length, 0);
|
|
207
321
|
});
|
|
208
322
|
|
|
323
|
+
test("runUnit clears queued switch cancellation when session creation fails", async () => {
|
|
324
|
+
_resetPendingResolve();
|
|
325
|
+
|
|
326
|
+
const ctx = makeMockCtx();
|
|
327
|
+
const pi = makeMockPi();
|
|
328
|
+
const s = makeMockSession({
|
|
329
|
+
newSessionThrows: "connection refused",
|
|
330
|
+
onNewSessionStart: () => {
|
|
331
|
+
resolveAgentEndCancelled({
|
|
332
|
+
message: "Claude Code process aborted by user",
|
|
333
|
+
category: "aborted",
|
|
334
|
+
isTransient: false,
|
|
335
|
+
});
|
|
336
|
+
},
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
const result = await runUnit(ctx, pi, s, "task", "T01", "prompt");
|
|
340
|
+
|
|
341
|
+
assert.equal(result.status, "cancelled");
|
|
342
|
+
assert.equal(_consumePendingSwitchCancellation(), null);
|
|
343
|
+
});
|
|
344
|
+
|
|
209
345
|
test("runUnit returns cancelled when session creation times out", async () => {
|
|
210
346
|
_resetPendingResolve();
|
|
211
347
|
|
|
@@ -221,6 +357,34 @@ test("runUnit returns cancelled when session creation times out", async () => {
|
|
|
221
357
|
assert.equal(pi.calls.length, 0);
|
|
222
358
|
});
|
|
223
359
|
|
|
360
|
+
test("runUnit consumes a cancellation queued during session switch before dispatch", async () => {
|
|
361
|
+
_resetPendingResolve();
|
|
362
|
+
|
|
363
|
+
const ctx = makeMockCtx();
|
|
364
|
+
const pi = makeMockPi();
|
|
365
|
+
let cancellationQueued = false;
|
|
366
|
+
const s = makeMockSession({
|
|
367
|
+
newSessionDelayMs: 10,
|
|
368
|
+
onNewSessionStart: () => {
|
|
369
|
+
setTimeout(() => {
|
|
370
|
+
cancellationQueued = !resolveAgentEndCancelled({
|
|
371
|
+
message: "Claude Code process aborted by user",
|
|
372
|
+
category: "aborted",
|
|
373
|
+
isTransient: false,
|
|
374
|
+
});
|
|
375
|
+
}, 0);
|
|
376
|
+
},
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
const result = await runUnit(ctx, pi, s, "plan-slice", "M009/S01", "prompt");
|
|
380
|
+
|
|
381
|
+
assert.equal(cancellationQueued, true);
|
|
382
|
+
assert.equal(result.status, "cancelled");
|
|
383
|
+
assert.equal(result.errorContext?.category, "aborted");
|
|
384
|
+
assert.equal(result.errorContext?.message, "Claude Code process aborted by user");
|
|
385
|
+
assert.equal(pi.calls.length, 0, "queued switch cancellation must prevent prompt dispatch");
|
|
386
|
+
});
|
|
387
|
+
|
|
224
388
|
test("runUnit keeps the session-switch guard across a late newSession settlement", async () => {
|
|
225
389
|
_resetPendingResolve();
|
|
226
390
|
mock.timers.enable();
|
|
@@ -496,10 +660,9 @@ test("runUnit proceeds when isProviderRequestReady throws (defensive) (#4555)",
|
|
|
496
660
|
assert.equal(pi.calls.length, 0);
|
|
497
661
|
});
|
|
498
662
|
|
|
499
|
-
test("late-resolving newSession() after timeout receives aborted signal so tool runtime is not configured with root
|
|
500
|
-
// When newSession() times out in runUnit(),
|
|
501
|
-
//
|
|
502
|
-
// configure the tool runtime (which would give it root cwd, not worktree cwd).
|
|
663
|
+
test("late-resolving newSession() after timeout receives aborted signal so tool runtime is not configured with stale workspace root (#3731)", async () => {
|
|
664
|
+
// When newSession() times out in runUnit(), a late resolution must not
|
|
665
|
+
// configure the tool runtime against a stale workspace root.
|
|
503
666
|
//
|
|
504
667
|
// The fix: runUnit creates an AbortController, aborts it on timeout, and passes
|
|
505
668
|
// the signal to newSession(). AgentSession.newSession() checks the signal after
|
|
@@ -514,8 +677,8 @@ test("late-resolving newSession() after timeout receives aborted signal so tool
|
|
|
514
677
|
|
|
515
678
|
// newSession mock simulates AgentSession.newSession() behavior:
|
|
516
679
|
// after an internal delay (representing await this.abort()), it checks the
|
|
517
|
-
// abortSignal
|
|
518
|
-
//
|
|
680
|
+
// abortSignal before selecting the workspace root and calling _buildRuntime.
|
|
681
|
+
// If aborted, the real code must discard the session.
|
|
519
682
|
const s = makeMockSession({
|
|
520
683
|
newSessionDelayMs: 200_000, // longer than NEW_SESSION_TIMEOUT_MS (120s)
|
|
521
684
|
onSignalCheck: (aborted) => {
|
|
@@ -548,7 +711,7 @@ test("late-resolving newSession() after timeout receives aborted signal so tool
|
|
|
548
711
|
abortedWhenLateSessionSettled,
|
|
549
712
|
true,
|
|
550
713
|
"runUnit must pass an aborted AbortSignal to newSession() when it resolves after the session-creation timeout (#3731). " +
|
|
551
|
-
"Without this, AgentSession.newSession()
|
|
714
|
+
"Without this, AgentSession.newSession() can rebuild the tool runtime with a stale workspace root.",
|
|
552
715
|
);
|
|
553
716
|
} finally {
|
|
554
717
|
mock.timers.reset();
|
|
@@ -613,7 +776,6 @@ function makeMockDeps(
|
|
|
613
776
|
preferences: { uok: { plan_v2: { enabled: false } } },
|
|
614
777
|
}),
|
|
615
778
|
preDispatchHealthGate: async () => ({ proceed: true, fixesApplied: [] }),
|
|
616
|
-
syncProjectRootToWorktree: () => {},
|
|
617
779
|
checkResourcesStale: () => null,
|
|
618
780
|
validateSessionLock: () => ({ valid: true } as SessionLockStatus),
|
|
619
781
|
updateSessionLock: () => {
|
|
@@ -627,7 +789,6 @@ function makeMockDeps(
|
|
|
627
789
|
pruneQueueOrder: () => {},
|
|
628
790
|
isInAutoWorktree: () => false,
|
|
629
791
|
shouldUseWorktreeIsolation: () => false,
|
|
630
|
-
mergeMilestoneToMain: () => ({ pushed: false, codeFilesChanged: true }),
|
|
631
792
|
teardownAutoWorktree: () => {},
|
|
632
793
|
createAutoWorktree: () => "/tmp/wt",
|
|
633
794
|
captureIntegrationBranch: () => {},
|
|
@@ -637,7 +798,11 @@ function makeMockDeps(
|
|
|
637
798
|
resolveMilestoneFile: () => null,
|
|
638
799
|
reconcileMergeState: () => "clean",
|
|
639
800
|
preflightCleanRoot: () => ({ stashPushed: false, summary: "" }),
|
|
640
|
-
postflightPopStash: () => {
|
|
801
|
+
postflightPopStash: () => ({
|
|
802
|
+
restored: true,
|
|
803
|
+
needsManualRecovery: false,
|
|
804
|
+
message: "restored",
|
|
805
|
+
}),
|
|
641
806
|
getLedger: () => null,
|
|
642
807
|
getProjectTotals: () => ({ cost: 0 }),
|
|
643
808
|
formatCost: (c: number) => `$${c.toFixed(2)}`,
|
|
@@ -673,21 +838,15 @@ function makeMockDeps(
|
|
|
673
838
|
readFileSync: () => "",
|
|
674
839
|
atomicWriteSync: () => {},
|
|
675
840
|
GitServiceImpl: class {} as any,
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
},
|
|
683
|
-
get lockPath() {
|
|
684
|
-
return "/tmp/project";
|
|
685
|
-
},
|
|
686
|
-
enterMilestone: () => {},
|
|
687
|
-
exitMilestone: () => {},
|
|
688
|
-
mergeAndExit: () => {},
|
|
689
|
-
mergeAndEnterNext: () => {},
|
|
841
|
+
lifecycle: {
|
|
842
|
+
enterMilestone: () => ({ ok: true, mode: "worktree", path: "/tmp/project" }),
|
|
843
|
+
exitMilestone: (_mid: string, opts: { merge: boolean }) => ({
|
|
844
|
+
ok: true,
|
|
845
|
+
merged: opts.merge,
|
|
846
|
+
codeFilesChanged: false,
|
|
847
|
+
}),
|
|
690
848
|
} as any,
|
|
849
|
+
worktreeProjection: new WorktreeStateProjection(),
|
|
691
850
|
postUnitPreVerification: async () => {
|
|
692
851
|
callLog.push("postUnitPreVerification");
|
|
693
852
|
return "continue" as const;
|
|
@@ -732,6 +891,7 @@ function makeLoopSession(overrides?: Partial<Record<string, unknown>>) {
|
|
|
732
891
|
lastBudgetAlertLevel: 0,
|
|
733
892
|
pendingVerificationRetry: null,
|
|
734
893
|
pendingCrashRecovery: null,
|
|
894
|
+
verificationRetryFailureHashes: new Map<string, string>(),
|
|
735
895
|
pendingQuickTasks: [],
|
|
736
896
|
sidecarQueue: [],
|
|
737
897
|
autoModeStartModel: null,
|
|
@@ -805,6 +965,139 @@ test("autoLoop exits on terminal complete state", async (t) => {
|
|
|
805
965
|
);
|
|
806
966
|
});
|
|
807
967
|
|
|
968
|
+
test("autoLoop stops before success notification when postflight stash restore needs recovery", async () => {
|
|
969
|
+
_resetPendingResolve();
|
|
970
|
+
|
|
971
|
+
const notifications: Array<{ msg: string; level: string }> = [];
|
|
972
|
+
const ctx = makeMockCtx();
|
|
973
|
+
ctx.ui.setStatus = () => {};
|
|
974
|
+
ctx.ui.notify = (msg: string, level: string) => {
|
|
975
|
+
notifications.push({ msg, level });
|
|
976
|
+
};
|
|
977
|
+
const pi = makeMockPi();
|
|
978
|
+
const s = makeLoopSession();
|
|
979
|
+
let stopReason = "";
|
|
980
|
+
|
|
981
|
+
const deps = makeMockDeps({
|
|
982
|
+
deriveState: async () => {
|
|
983
|
+
deps.callLog.push("deriveState");
|
|
984
|
+
return {
|
|
985
|
+
phase: "complete",
|
|
986
|
+
activeMilestone: { id: "M001", title: "Test", status: "complete" },
|
|
987
|
+
activeSlice: null,
|
|
988
|
+
activeTask: null,
|
|
989
|
+
registry: [{ id: "M001", status: "complete" }],
|
|
990
|
+
blockers: [],
|
|
991
|
+
} as any;
|
|
992
|
+
},
|
|
993
|
+
preflightCleanRoot: () => ({
|
|
994
|
+
stashPushed: true,
|
|
995
|
+
stashMarker: "gsd-preflight-stash:M001:test",
|
|
996
|
+
summary: "stashed",
|
|
997
|
+
}),
|
|
998
|
+
postflightPopStash: () => ({
|
|
999
|
+
restored: false,
|
|
1000
|
+
needsManualRecovery: true,
|
|
1001
|
+
message: "git stash pop stash@{0} failed after merge of milestone M001",
|
|
1002
|
+
stashRef: "stash@{0}",
|
|
1003
|
+
}),
|
|
1004
|
+
sendDesktopNotification: () => {
|
|
1005
|
+
deps.callLog.push("sendDesktopNotification");
|
|
1006
|
+
},
|
|
1007
|
+
logCmuxEvent: () => {
|
|
1008
|
+
deps.callLog.push("logCmuxEvent");
|
|
1009
|
+
},
|
|
1010
|
+
stopAuto: async (_ctx, _pi, reason) => {
|
|
1011
|
+
deps.callLog.push("stopAuto");
|
|
1012
|
+
stopReason = reason ?? "";
|
|
1013
|
+
},
|
|
1014
|
+
});
|
|
1015
|
+
|
|
1016
|
+
await autoLoop(ctx, pi, s, deps);
|
|
1017
|
+
|
|
1018
|
+
assert.equal(stopReason, "Post-merge stash restore failed for milestone M001");
|
|
1019
|
+
assert.ok(
|
|
1020
|
+
notifications.some(
|
|
1021
|
+
(n) => n.level === "error" && n.msg.includes("Post-merge stash restore failed for milestone M001"),
|
|
1022
|
+
),
|
|
1023
|
+
"failed postflight restore must be surfaced as an error",
|
|
1024
|
+
);
|
|
1025
|
+
assert.ok(
|
|
1026
|
+
!deps.callLog.includes("sendDesktopNotification"),
|
|
1027
|
+
"must not emit milestone success desktop notification after stash restore failure",
|
|
1028
|
+
);
|
|
1029
|
+
assert.ok(
|
|
1030
|
+
!deps.callLog.includes("logCmuxEvent"),
|
|
1031
|
+
"must not emit milestone success cmux event after stash restore failure",
|
|
1032
|
+
);
|
|
1033
|
+
});
|
|
1034
|
+
|
|
1035
|
+
test("autoLoop marks transition merge complete before postflight recovery stop", async () => {
|
|
1036
|
+
_resetPendingResolve();
|
|
1037
|
+
|
|
1038
|
+
const ctx = makeMockCtx();
|
|
1039
|
+
ctx.ui.setStatus = () => {};
|
|
1040
|
+
ctx.ui.notify = () => {};
|
|
1041
|
+
const pi = makeMockPi();
|
|
1042
|
+
const s = makeLoopSession();
|
|
1043
|
+
let mergeCalls = 0;
|
|
1044
|
+
let stopReason = "";
|
|
1045
|
+
|
|
1046
|
+
const deps = makeMockDeps({
|
|
1047
|
+
deriveState: async () => {
|
|
1048
|
+
deps.callLog.push("deriveState");
|
|
1049
|
+
return {
|
|
1050
|
+
phase: "executing",
|
|
1051
|
+
activeMilestone: { id: "M002", title: "Next", status: "active" },
|
|
1052
|
+
activeSlice: null,
|
|
1053
|
+
activeTask: null,
|
|
1054
|
+
registry: [
|
|
1055
|
+
{ id: "M001", title: "Done", status: "complete" },
|
|
1056
|
+
{ id: "M002", title: "Next", status: "active" },
|
|
1057
|
+
],
|
|
1058
|
+
blockers: [],
|
|
1059
|
+
} as any;
|
|
1060
|
+
},
|
|
1061
|
+
preflightCleanRoot: () => ({
|
|
1062
|
+
stashPushed: true,
|
|
1063
|
+
stashMarker: "gsd-preflight-stash:M001:test",
|
|
1064
|
+
summary: "stashed",
|
|
1065
|
+
}),
|
|
1066
|
+
postflightPopStash: () => ({
|
|
1067
|
+
restored: false,
|
|
1068
|
+
needsManualRecovery: true,
|
|
1069
|
+
message: "git stash pop stash@{0} failed after merge of milestone M001",
|
|
1070
|
+
stashRef: "stash@{0}",
|
|
1071
|
+
}),
|
|
1072
|
+
lifecycle: {
|
|
1073
|
+
enterMilestone: () => {
|
|
1074
|
+
assert.fail("must not enter the next milestone after postflight recovery fails");
|
|
1075
|
+
},
|
|
1076
|
+
exitMilestone: (_mid: string, opts: { merge: boolean }) => {
|
|
1077
|
+
if (opts.merge) mergeCalls += 1;
|
|
1078
|
+
return { ok: true, merged: opts.merge, codeFilesChanged: false };
|
|
1079
|
+
},
|
|
1080
|
+
} as any,
|
|
1081
|
+
stopAuto: async (_ctx, _pi, reason) => {
|
|
1082
|
+
deps.callLog.push("stopAuto");
|
|
1083
|
+
stopReason = reason ?? "";
|
|
1084
|
+
if (!s.milestoneMergedInPhases) {
|
|
1085
|
+
deps.lifecycle.exitMilestone(
|
|
1086
|
+
"M001",
|
|
1087
|
+
{ merge: true },
|
|
1088
|
+
{ notify: ctx.ui.notify.bind(ctx.ui) },
|
|
1089
|
+
);
|
|
1090
|
+
}
|
|
1091
|
+
},
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
await autoLoop(ctx, pi, s, deps);
|
|
1095
|
+
|
|
1096
|
+
assert.equal(stopReason, "Post-merge stash restore failed for milestone M001");
|
|
1097
|
+
assert.equal(s.milestoneMergedInPhases, true);
|
|
1098
|
+
assert.equal(mergeCalls, 1, "postflight recovery stop must not re-run an already completed transition merge");
|
|
1099
|
+
});
|
|
1100
|
+
|
|
808
1101
|
test("autoLoop pauses when provider readiness cancels before dispatch", async () => {
|
|
809
1102
|
_resetPendingResolve();
|
|
810
1103
|
|
|
@@ -966,7 +1259,7 @@ test("autoLoop dequeues sidecar item before session-lock break (mid-session, #53
|
|
|
966
1259
|
deps.callLog.push("postUnitPostVerification");
|
|
967
1260
|
s.sidecarQueue.push({
|
|
968
1261
|
kind: "hook" as const,
|
|
969
|
-
unitType: "
|
|
1262
|
+
unitType: "run-uat",
|
|
970
1263
|
unitId: "M001/S01/T01/review",
|
|
971
1264
|
prompt: "review the code",
|
|
972
1265
|
});
|
|
@@ -1108,6 +1401,107 @@ test("autoLoop calls deriveState → resolveDispatch → runUnit in sequence", a
|
|
|
1108
1401
|
);
|
|
1109
1402
|
});
|
|
1110
1403
|
|
|
1404
|
+
test("autoLoop journals post-unit finalize stop after completed unit", async () => {
|
|
1405
|
+
_resetPendingResolve();
|
|
1406
|
+
|
|
1407
|
+
const ctx = makeMockCtx();
|
|
1408
|
+
ctx.ui.setStatus = () => {};
|
|
1409
|
+
ctx.sessionManager = { getSessionFile: () => "/tmp/session.json" };
|
|
1410
|
+
const pi = makeMockPi();
|
|
1411
|
+
const s = makeLoopSession();
|
|
1412
|
+
const journalEvents: Array<{ eventType: string; data?: any }> = [];
|
|
1413
|
+
|
|
1414
|
+
const deps = makeMockDeps({
|
|
1415
|
+
postUnitPreVerification: async () => {
|
|
1416
|
+
deps.callLog.push("postUnitPreVerification");
|
|
1417
|
+
s.lastGitActionFailure = "commit failed";
|
|
1418
|
+
return "dispatched" as const;
|
|
1419
|
+
},
|
|
1420
|
+
emitJournalEvent: (entry: any) => {
|
|
1421
|
+
journalEvents.push(entry);
|
|
1422
|
+
},
|
|
1423
|
+
});
|
|
1424
|
+
|
|
1425
|
+
const loopPromise = autoLoop(ctx, pi, s, deps);
|
|
1426
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
1427
|
+
resolveAgentEnd(makeEvent());
|
|
1428
|
+
await loopPromise;
|
|
1429
|
+
|
|
1430
|
+
assert.ok(
|
|
1431
|
+
deps.callLog.includes("postUnitPreVerification"),
|
|
1432
|
+
"completed units must enter post-unit pre-verification before stopping",
|
|
1433
|
+
);
|
|
1434
|
+
assert.ok(
|
|
1435
|
+
!deps.callLog.includes("runPostUnitVerification"),
|
|
1436
|
+
"git-closeout stop should not run later verification phases",
|
|
1437
|
+
);
|
|
1438
|
+
|
|
1439
|
+
const unitEndIndex = journalEvents.findIndex((e) => e.eventType === "unit-end");
|
|
1440
|
+
const finalizeStartIndex = journalEvents.findIndex((e) => e.eventType === "post-unit-finalize-start");
|
|
1441
|
+
const finalizeEndIndex = journalEvents.findIndex((e) => e.eventType === "post-unit-finalize-end");
|
|
1442
|
+
const iterationEndIndex = journalEvents.findIndex((e) => e.eventType === "iteration-end");
|
|
1443
|
+
|
|
1444
|
+
assert.ok(unitEndIndex >= 0, "unit-end should be journaled after agent completion");
|
|
1445
|
+
assert.ok(finalizeStartIndex > unitEndIndex, "post-unit finalize must start after unit-end");
|
|
1446
|
+
assert.ok(finalizeEndIndex > finalizeStartIndex, "post-unit finalize must journal its stop result");
|
|
1447
|
+
assert.ok(iterationEndIndex > finalizeEndIndex, "iteration-end must be emitted even when finalize stops");
|
|
1448
|
+
|
|
1449
|
+
assert.deepEqual(journalEvents[finalizeEndIndex]!.data, {
|
|
1450
|
+
iteration: 1,
|
|
1451
|
+
unitType: "execute-task",
|
|
1452
|
+
unitId: "M001/S01/T01",
|
|
1453
|
+
status: "stopped",
|
|
1454
|
+
action: "break",
|
|
1455
|
+
reason: "git-closeout-failure",
|
|
1456
|
+
});
|
|
1457
|
+
assert.deepEqual(journalEvents[iterationEndIndex]!.data, {
|
|
1458
|
+
iteration: 1,
|
|
1459
|
+
status: "stopped",
|
|
1460
|
+
reason: "git-closeout-failure",
|
|
1461
|
+
unitType: "execute-task",
|
|
1462
|
+
unitId: "M001/S01/T01",
|
|
1463
|
+
failureClass: "git",
|
|
1464
|
+
});
|
|
1465
|
+
});
|
|
1466
|
+
|
|
1467
|
+
test("autoLoop journals iteration-end when unit phase breaks after cancelled unit", async () => {
|
|
1468
|
+
_resetPendingResolve();
|
|
1469
|
+
|
|
1470
|
+
const ctx = makeMockCtx();
|
|
1471
|
+
ctx.ui.setStatus = () => {};
|
|
1472
|
+
ctx.sessionManager = { getSessionFile: () => "/tmp/session.json" };
|
|
1473
|
+
const pi = makeMockPi();
|
|
1474
|
+
const s = makeLoopSession();
|
|
1475
|
+
const journalEvents: Array<{ eventType: string; data?: any }> = [];
|
|
1476
|
+
|
|
1477
|
+
const deps = makeMockDeps({
|
|
1478
|
+
emitJournalEvent: (entry: any) => {
|
|
1479
|
+
journalEvents.push(entry);
|
|
1480
|
+
},
|
|
1481
|
+
});
|
|
1482
|
+
|
|
1483
|
+
const loopPromise = autoLoop(ctx, pi, s, deps);
|
|
1484
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
1485
|
+
resolveAgentEndCancelled();
|
|
1486
|
+
await loopPromise;
|
|
1487
|
+
|
|
1488
|
+
const unitEndIndex = journalEvents.findIndex(
|
|
1489
|
+
(e) => e.eventType === "unit-end" && e.data?.status === "cancelled",
|
|
1490
|
+
);
|
|
1491
|
+
const iterationEndIndex = journalEvents.findIndex((e) => e.eventType === "iteration-end");
|
|
1492
|
+
|
|
1493
|
+
assert.ok(unitEndIndex >= 0, "cancelled unit should still emit unit-end");
|
|
1494
|
+
assert.ok(iterationEndIndex > unitEndIndex, "unit-phase break must close the iteration after unit-end");
|
|
1495
|
+
assert.deepEqual(journalEvents[iterationEndIndex]!.data, {
|
|
1496
|
+
iteration: 1,
|
|
1497
|
+
status: "stopped",
|
|
1498
|
+
reason: "unit-aborted",
|
|
1499
|
+
unitType: "execute-task",
|
|
1500
|
+
unitId: "M001/S01/T01",
|
|
1501
|
+
failureClass: "execution",
|
|
1502
|
+
});
|
|
1503
|
+
});
|
|
1504
|
+
|
|
1111
1505
|
test("crash lock records session file from AFTER newSession, not before (#1710)", async (t) => {
|
|
1112
1506
|
_resetPendingResolve();
|
|
1113
1507
|
|
|
@@ -1218,86 +1612,152 @@ test("crash lock records session file from AFTER newSession, not before (#1710)"
|
|
|
1218
1612
|
|
|
1219
1613
|
test("autoLoop handles verification retry by continuing loop", async (t) => {
|
|
1220
1614
|
_resetPendingResolve();
|
|
1615
|
+
mock.timers.enable({ apis: ["Date", "setTimeout"], now: 10_000 });
|
|
1221
1616
|
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1617
|
+
try {
|
|
1618
|
+
const ctx = makeMockCtx();
|
|
1619
|
+
ctx.ui.setStatus = () => {};
|
|
1620
|
+
ctx.sessionManager = { getSessionFile: () => "/tmp/session.json" };
|
|
1621
|
+
const pi = makeMockPi();
|
|
1226
1622
|
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1623
|
+
let verifyCallCount = 0;
|
|
1624
|
+
let deriveCallCount = 0;
|
|
1625
|
+
const s = makeLoopSession();
|
|
1230
1626
|
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1627
|
+
// Pre-queued verification actions: each entry provides a side-effect + return value
|
|
1628
|
+
type VerifyAction = { sideEffect?: () => void; response: "retry" | "continue" };
|
|
1629
|
+
const verificationActions: VerifyAction[] = [
|
|
1630
|
+
{
|
|
1631
|
+
sideEffect: () => {
|
|
1632
|
+
// Simulate retry — set pendingVerificationRetry on session
|
|
1633
|
+
s.pendingVerificationRetry = {
|
|
1634
|
+
unitId: "M001/S01/T01",
|
|
1635
|
+
failureContext: "test failed: expected X got Y",
|
|
1636
|
+
attempt: 1,
|
|
1637
|
+
};
|
|
1638
|
+
},
|
|
1639
|
+
response: "retry",
|
|
1242
1640
|
},
|
|
1243
|
-
response: "
|
|
1244
|
-
|
|
1245
|
-
{ response: "continue" },
|
|
1246
|
-
];
|
|
1641
|
+
{ response: "continue" },
|
|
1642
|
+
];
|
|
1247
1643
|
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1644
|
+
const deps = makeMockDeps({
|
|
1645
|
+
deriveState: async () => {
|
|
1646
|
+
deriveCallCount++;
|
|
1647
|
+
deps.callLog.push("deriveState");
|
|
1648
|
+
return {
|
|
1649
|
+
phase: "executing",
|
|
1650
|
+
activeMilestone: { id: "M001", title: "Test", status: "active" },
|
|
1651
|
+
activeSlice: { id: "S01", title: "Slice 1" },
|
|
1652
|
+
activeTask: { id: "T01" },
|
|
1653
|
+
registry: [{ id: "M001", status: "active" }],
|
|
1654
|
+
blockers: [],
|
|
1655
|
+
} as any;
|
|
1656
|
+
},
|
|
1657
|
+
runPostUnitVerification: async () => {
|
|
1658
|
+
const action = verificationActions[verifyCallCount] ?? { response: "continue" as const };
|
|
1659
|
+
verifyCallCount++;
|
|
1660
|
+
deps.callLog.push("runPostUnitVerification");
|
|
1661
|
+
action.sideEffect?.();
|
|
1662
|
+
return action.response;
|
|
1663
|
+
},
|
|
1664
|
+
postUnitPostVerification: async () => {
|
|
1665
|
+
deps.callLog.push("postUnitPostVerification");
|
|
1666
|
+
// After the retry cycle completes, deactivate
|
|
1667
|
+
s.active = false;
|
|
1668
|
+
return "continue" as const;
|
|
1669
|
+
},
|
|
1670
|
+
});
|
|
1275
1671
|
|
|
1276
|
-
|
|
1672
|
+
const loopPromise = autoLoop(ctx, pi, s, deps);
|
|
1277
1673
|
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1674
|
+
// First iteration: runUnit → verification returns "retry" → loop continues
|
|
1675
|
+
await waitForMicrotasks(() => pi.calls.length === 1, "first dispatch");
|
|
1676
|
+
resolveAgentEnd(makeEvent()); // resolve first unit
|
|
1281
1677
|
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1678
|
+
await drainMicrotasks(100);
|
|
1679
|
+
mock.timers.tick(30_000);
|
|
1680
|
+
await waitForMicrotasks(() => pi.calls.length === 2, "retry dispatch");
|
|
1681
|
+
resolveAgentEnd(makeEvent()); // resolve retry unit
|
|
1285
1682
|
|
|
1286
|
-
|
|
1683
|
+
await loopPromise;
|
|
1287
1684
|
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1685
|
+
// Verify deriveState was called twice (two iterations)
|
|
1686
|
+
const deriveCount = deps.callLog.filter((c) => c === "deriveState").length;
|
|
1687
|
+
assert.ok(
|
|
1688
|
+
deriveCount >= 2,
|
|
1689
|
+
`deriveState should be called at least 2 times (got ${deriveCount})`,
|
|
1690
|
+
);
|
|
1294
1691
|
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1692
|
+
// Verify verification was called twice
|
|
1693
|
+
assert.equal(
|
|
1694
|
+
verifyCallCount,
|
|
1695
|
+
2,
|
|
1696
|
+
"verification should have been called twice (once retry, once pass)",
|
|
1697
|
+
);
|
|
1698
|
+
} finally {
|
|
1699
|
+
mock.timers.reset();
|
|
1700
|
+
}
|
|
1701
|
+
});
|
|
1702
|
+
|
|
1703
|
+
test("autoLoop pauses instead of redispatching identical verification failure context", async () => {
|
|
1704
|
+
_resetPendingResolve();
|
|
1705
|
+
mock.timers.enable({ apis: ["Date", "setTimeout"], now: 15_000 });
|
|
1706
|
+
|
|
1707
|
+
try {
|
|
1708
|
+
const ctx = makeMockCtx();
|
|
1709
|
+
ctx.ui.setStatus = () => {};
|
|
1710
|
+
ctx.ui.notify = () => {};
|
|
1711
|
+
ctx.sessionManager = { getSessionFile: () => "/tmp/session.json" };
|
|
1712
|
+
const pi = makeMockPi();
|
|
1713
|
+
const s = makeLoopSession();
|
|
1714
|
+
let verifyCallCount = 0;
|
|
1715
|
+
let pauseCallCount = 0;
|
|
1716
|
+
|
|
1717
|
+
const deps = makeMockDeps({
|
|
1718
|
+
deriveState: async () =>
|
|
1719
|
+
({
|
|
1720
|
+
phase: "executing",
|
|
1721
|
+
activeMilestone: { id: "M001", title: "Test", status: "active" },
|
|
1722
|
+
activeSlice: { id: "S01", title: "Slice 1" },
|
|
1723
|
+
activeTask: { id: "T01" },
|
|
1724
|
+
registry: [{ id: "M001", status: "active" }],
|
|
1725
|
+
blockers: [],
|
|
1726
|
+
}) as any,
|
|
1727
|
+
runPostUnitVerification: async () => {
|
|
1728
|
+
verifyCallCount++;
|
|
1729
|
+
deps.callLog.push("runPostUnitVerification");
|
|
1730
|
+
s.pendingVerificationRetry = {
|
|
1731
|
+
unitId: "M001/S01/T01",
|
|
1732
|
+
failureContext: "test failed: expected X got Y",
|
|
1733
|
+
attempt: verifyCallCount,
|
|
1734
|
+
};
|
|
1735
|
+
return "retry" as const;
|
|
1736
|
+
},
|
|
1737
|
+
pauseAuto: async () => {
|
|
1738
|
+
pauseCallCount++;
|
|
1739
|
+
s.active = false;
|
|
1740
|
+
},
|
|
1741
|
+
});
|
|
1742
|
+
|
|
1743
|
+
const loopPromise = autoLoop(ctx, pi, s, deps);
|
|
1744
|
+
|
|
1745
|
+
await waitForMicrotasks(() => pi.calls.length === 1, "first dispatch");
|
|
1746
|
+
resolveAgentEnd(makeEvent());
|
|
1747
|
+
await drainMicrotasks(100);
|
|
1748
|
+
mock.timers.tick(30_000);
|
|
1749
|
+
|
|
1750
|
+
await waitForMicrotasks(() => pi.calls.length === 2, "retry dispatch");
|
|
1751
|
+
resolveAgentEnd(makeEvent());
|
|
1752
|
+
|
|
1753
|
+
await loopPromise;
|
|
1754
|
+
|
|
1755
|
+
assert.equal(verifyCallCount, 2);
|
|
1756
|
+
assert.equal(pi.calls.length, 2, "duplicate failure should not be redispatched a third time");
|
|
1757
|
+
assert.equal(pauseCallCount, 1, "duplicate failure should pause auto-mode");
|
|
1758
|
+
} finally {
|
|
1759
|
+
mock.timers.reset();
|
|
1760
|
+
}
|
|
1301
1761
|
});
|
|
1302
1762
|
|
|
1303
1763
|
test("autoLoop handles dispatch stop action", async (t) => {
|
|
@@ -1453,7 +1913,7 @@ test("autoLoop drains sidecar queue after postUnitPostVerification enqueues item
|
|
|
1453
1913
|
// First call (main unit): enqueue a sidecar item
|
|
1454
1914
|
s.sidecarQueue.push({
|
|
1455
1915
|
kind: "hook" as const,
|
|
1456
|
-
unitType: "
|
|
1916
|
+
unitType: "run-uat",
|
|
1457
1917
|
unitId: "M001/S01/T01/review",
|
|
1458
1918
|
prompt: "review the code",
|
|
1459
1919
|
});
|
|
@@ -1475,11 +1935,17 @@ test("autoLoop drains sidecar queue after postUnitPostVerification enqueues item
|
|
|
1475
1935
|
const loopPromise = autoLoop(ctx, pi, s, deps);
|
|
1476
1936
|
|
|
1477
1937
|
// Wait for main unit's runUnit to be awaiting
|
|
1478
|
-
|
|
1938
|
+
for (let i = 0; !_hasPendingResolveForTest() && i < 100; i++) {
|
|
1939
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
1940
|
+
}
|
|
1941
|
+
assert.equal(_hasPendingResolveForTest(), true, "main unit should be awaiting agent_end");
|
|
1479
1942
|
resolveAgentEnd(makeEvent()); // resolve main unit
|
|
1480
1943
|
|
|
1481
1944
|
// Wait for the sidecar unit's runUnit to be awaiting
|
|
1482
|
-
|
|
1945
|
+
for (let i = 0; !_hasPendingResolveForTest() && postVerCallCount < 2 && i < 100; i++) {
|
|
1946
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
1947
|
+
}
|
|
1948
|
+
assert.equal(_hasPendingResolveForTest(), true, "sidecar unit should be awaiting agent_end");
|
|
1483
1949
|
resolveAgentEnd(makeEvent()); // resolve sidecar unit
|
|
1484
1950
|
|
|
1485
1951
|
await loopPromise;
|
|
@@ -1680,74 +2146,83 @@ test("stuck detection: window resets recovery when deriveState returns a differe
|
|
|
1680
2146
|
|
|
1681
2147
|
test("stuck detection: does not push to window during verification retry", async () => {
|
|
1682
2148
|
_resetPendingResolve();
|
|
2149
|
+
mock.timers.enable({ apis: ["Date", "setTimeout"], now: 20_000 });
|
|
1683
2150
|
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
2151
|
+
try {
|
|
2152
|
+
const ctx = makeMockCtx();
|
|
2153
|
+
ctx.ui.setStatus = () => {};
|
|
2154
|
+
ctx.ui.notify = () => {};
|
|
2155
|
+
const pi = makeMockPi();
|
|
2156
|
+
const s = makeLoopSession();
|
|
1689
2157
|
|
|
1690
|
-
|
|
1691
|
-
|
|
2158
|
+
let verifyCallCount = 0;
|
|
2159
|
+
let stopReason = "";
|
|
1692
2160
|
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
() =>
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
2161
|
+
// Pre-queued responses: 3 retries then a continue (exit). Failure
|
|
2162
|
+
// contexts differ so this test exercises stuck-window behavior without
|
|
2163
|
+
// tripping duplicate-failure suppression.
|
|
2164
|
+
const verifyActions: Array<() => "retry" | "continue"> = [
|
|
2165
|
+
() => { s.pendingVerificationRetry = { unitId: "M001/S01/T01", failureContext: "test failed: 1", attempt: 1 }; return "retry"; },
|
|
2166
|
+
() => { s.pendingVerificationRetry = { unitId: "M001/S01/T01", failureContext: "test failed: 2", attempt: 2 }; return "retry"; },
|
|
2167
|
+
() => { s.pendingVerificationRetry = { unitId: "M001/S01/T01", failureContext: "test failed: 3", attempt: 3 }; return "retry"; },
|
|
2168
|
+
() => { s.active = false; return "continue"; },
|
|
2169
|
+
];
|
|
1700
2170
|
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
2171
|
+
const deps = makeMockDeps({
|
|
2172
|
+
deriveState: async () =>
|
|
2173
|
+
({
|
|
2174
|
+
phase: "executing",
|
|
2175
|
+
activeMilestone: { id: "M001", title: "Test", status: "active" },
|
|
2176
|
+
activeSlice: { id: "S01", title: "Slice 1" },
|
|
2177
|
+
activeTask: { id: "T01" },
|
|
2178
|
+
registry: [{ id: "M001", status: "active" }],
|
|
2179
|
+
blockers: [],
|
|
2180
|
+
}) as any,
|
|
2181
|
+
resolveDispatch: async () => ({
|
|
2182
|
+
action: "dispatch" as const,
|
|
2183
|
+
unitType: "execute-task",
|
|
2184
|
+
unitId: "M001/S01/T01",
|
|
2185
|
+
prompt: "do the thing",
|
|
2186
|
+
}),
|
|
2187
|
+
runPostUnitVerification: async () => {
|
|
2188
|
+
const action = verifyActions[verifyCallCount] ?? (() => { s.active = false; return "continue" as const; });
|
|
2189
|
+
verifyCallCount++;
|
|
2190
|
+
deps.callLog.push("runPostUnitVerification");
|
|
2191
|
+
return action();
|
|
2192
|
+
},
|
|
2193
|
+
stopAuto: async (_ctx?: any, _pi?: any, reason?: string) => {
|
|
2194
|
+
deps.callLog.push("stopAuto");
|
|
2195
|
+
stopReason = reason ?? "";
|
|
2196
|
+
s.active = false;
|
|
2197
|
+
},
|
|
2198
|
+
});
|
|
1729
2199
|
|
|
1730
|
-
|
|
2200
|
+
const loopPromise = autoLoop(ctx, pi, s, deps);
|
|
1731
2201
|
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
2202
|
+
// Resolve agent_end for 4 iterations (1 initial + 3 retries)
|
|
2203
|
+
for (let i = 1; i <= 4; i++) {
|
|
2204
|
+
await waitForMicrotasks(() => pi.calls.length === i, `dispatch ${i}`);
|
|
2205
|
+
resolveAgentEnd(makeEvent());
|
|
2206
|
+
await drainMicrotasks(100);
|
|
2207
|
+
mock.timers.tick(30_000);
|
|
2208
|
+
}
|
|
1737
2209
|
|
|
1738
|
-
|
|
2210
|
+
await loopPromise;
|
|
1739
2211
|
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
2212
|
+
// Even though same unit was derived 4 times, verification retries should
|
|
2213
|
+
// not push to the sliding window, so stuck detection should not have fired
|
|
2214
|
+
assert.ok(
|
|
2215
|
+
!stopReason.includes("Stuck"),
|
|
2216
|
+
`stuck detection should not fire during verification retries, got: ${stopReason}`,
|
|
2217
|
+
);
|
|
2218
|
+
assert.equal(
|
|
2219
|
+
verifyCallCount,
|
|
2220
|
+
4,
|
|
2221
|
+
"verification should have been called 4 times (1 initial + 3 retries)",
|
|
2222
|
+
);
|
|
2223
|
+
} finally {
|
|
2224
|
+
mock.timers.reset();
|
|
2225
|
+
}
|
|
1751
2226
|
});
|
|
1752
2227
|
|
|
1753
2228
|
// ── detectStuck unit tests ────────────────────────────────────────────────────
|
|
@@ -1898,7 +2373,7 @@ test("autoLoop lifecycle: advances through research → plan → execute → ver
|
|
|
1898
2373
|
{ unitType: "research-slice", unitId: "M001/S01", prompt: "research" },
|
|
1899
2374
|
{ unitType: "plan-slice", unitId: "M001/S01", prompt: "plan" },
|
|
1900
2375
|
{ unitType: "execute-task", unitId: "M001/S01/T01", prompt: "execute" },
|
|
1901
|
-
{ unitType: "
|
|
2376
|
+
{ unitType: "run-uat", unitId: "M001/S01", prompt: "verify" },
|
|
1902
2377
|
{ unitType: "complete-slice", unitId: "M001/S01", prompt: "complete" },
|
|
1903
2378
|
];
|
|
1904
2379
|
|
|
@@ -1968,8 +2443,8 @@ test("autoLoop lifecycle: advances through research → plan → execute → ver
|
|
|
1968
2443
|
`should have dispatched execute-task, got: ${dispatchedUnitTypes.join(", ")}`,
|
|
1969
2444
|
);
|
|
1970
2445
|
assert.ok(
|
|
1971
|
-
dispatchedUnitTypes.includes("
|
|
1972
|
-
`should have dispatched
|
|
2446
|
+
dispatchedUnitTypes.includes("run-uat"),
|
|
2447
|
+
`should have dispatched run-uat, got: ${dispatchedUnitTypes.join(", ")}`,
|
|
1973
2448
|
);
|
|
1974
2449
|
assert.ok(
|
|
1975
2450
|
dispatchedUnitTypes.includes("complete-slice"),
|
|
@@ -2001,7 +2476,7 @@ test("autoLoop lifecycle: advances through research → plan → execute → ver
|
|
|
2001
2476
|
"research-slice",
|
|
2002
2477
|
"plan-slice",
|
|
2003
2478
|
"execute-task",
|
|
2004
|
-
"
|
|
2479
|
+
"run-uat",
|
|
2005
2480
|
"complete-slice",
|
|
2006
2481
|
],
|
|
2007
2482
|
"dispatched unit types should follow the full lifecycle sequence",
|
|
@@ -2073,6 +2548,108 @@ test("resolveAgentEndCancelled with errorContext passes it through to resolved p
|
|
|
2073
2548
|
assert.equal(resolved.errorContext!.isTransient, true);
|
|
2074
2549
|
});
|
|
2075
2550
|
|
|
2551
|
+
test("runUnitPhase pauses ghost completions before closeout and finalize side effects", async (t) => {
|
|
2552
|
+
_resetPendingResolve();
|
|
2553
|
+
|
|
2554
|
+
const basePath = mkdtempSync(join(tmpdir(), "gsd-ghost-completion-"));
|
|
2555
|
+
t.after(() => {
|
|
2556
|
+
_resetPendingResolve();
|
|
2557
|
+
rmSync(basePath, { recursive: true, force: true });
|
|
2558
|
+
});
|
|
2559
|
+
|
|
2560
|
+
let closeoutCalls = 0;
|
|
2561
|
+
let preVerificationCalls = 0;
|
|
2562
|
+
let postVerificationCalls = 0;
|
|
2563
|
+
const journalEvents: any[] = [];
|
|
2564
|
+
const deps = makeMockDeps({
|
|
2565
|
+
closeoutUnit: async () => {
|
|
2566
|
+
closeoutCalls++;
|
|
2567
|
+
},
|
|
2568
|
+
postUnitPreVerification: async () => {
|
|
2569
|
+
preVerificationCalls++;
|
|
2570
|
+
return "continue";
|
|
2571
|
+
},
|
|
2572
|
+
postUnitPostVerification: async () => {
|
|
2573
|
+
postVerificationCalls++;
|
|
2574
|
+
return "continue";
|
|
2575
|
+
},
|
|
2576
|
+
emitJournalEvent: (event: any) => {
|
|
2577
|
+
journalEvents.push(event);
|
|
2578
|
+
},
|
|
2579
|
+
});
|
|
2580
|
+
const ctx = {
|
|
2581
|
+
...makeMockCtx(),
|
|
2582
|
+
ui: {
|
|
2583
|
+
notify: () => {},
|
|
2584
|
+
setStatus: () => {},
|
|
2585
|
+
setWorkingMessage: () => {},
|
|
2586
|
+
},
|
|
2587
|
+
sessionManager: {
|
|
2588
|
+
getEntries: () => [],
|
|
2589
|
+
},
|
|
2590
|
+
modelRegistry: {
|
|
2591
|
+
getProviderAuthMode: () => undefined,
|
|
2592
|
+
isProviderRequestReady: () => true,
|
|
2593
|
+
},
|
|
2594
|
+
} as any;
|
|
2595
|
+
const pi = {
|
|
2596
|
+
...makeMockPi(),
|
|
2597
|
+
sendMessage: () => {
|
|
2598
|
+
queueMicrotask(() => resolveAgentEnd({ messages: [] }));
|
|
2599
|
+
},
|
|
2600
|
+
} as any;
|
|
2601
|
+
const s = makeLoopSession({
|
|
2602
|
+
basePath,
|
|
2603
|
+
canonicalProjectRoot: basePath,
|
|
2604
|
+
originalBasePath: basePath,
|
|
2605
|
+
});
|
|
2606
|
+
let seq = 0;
|
|
2607
|
+
|
|
2608
|
+
const result = await runUnitPhase(
|
|
2609
|
+
{ ctx, pi, s, deps, prefs: undefined, iteration: 1, flowId: "flow-ghost", nextSeq: () => ++seq },
|
|
2610
|
+
{
|
|
2611
|
+
unitType: "execute-task",
|
|
2612
|
+
unitId: "M001/S01/T01",
|
|
2613
|
+
prompt: "do work",
|
|
2614
|
+
finalPrompt: "do work",
|
|
2615
|
+
pauseAfterUatDispatch: false,
|
|
2616
|
+
state: {
|
|
2617
|
+
phase: "executing",
|
|
2618
|
+
activeMilestone: { id: "M001", title: "Milestone" },
|
|
2619
|
+
activeSlice: { id: "S01", title: "Slice" },
|
|
2620
|
+
activeTask: { id: "T01", title: "Task" },
|
|
2621
|
+
registry: [{ id: "M001", title: "Milestone", status: "active" }],
|
|
2622
|
+
recentDecisions: [],
|
|
2623
|
+
blockers: [],
|
|
2624
|
+
nextAction: "",
|
|
2625
|
+
progress: { milestones: { done: 0, total: 1 } },
|
|
2626
|
+
requirements: { active: 0, validated: 0, deferred: 0, outOfScope: 0, blocked: 0, total: 0 },
|
|
2627
|
+
} as any,
|
|
2628
|
+
mid: "M001",
|
|
2629
|
+
midTitle: "Milestone",
|
|
2630
|
+
isRetry: false,
|
|
2631
|
+
previousTier: undefined,
|
|
2632
|
+
},
|
|
2633
|
+
{ recentUnits: [], stuckRecoveryAttempts: 0, consecutiveFinalizeTimeouts: 0 },
|
|
2634
|
+
);
|
|
2635
|
+
|
|
2636
|
+
assert.equal(result.action, "break");
|
|
2637
|
+
assert.equal((result as any).reason, "ghost-completion");
|
|
2638
|
+
assert.equal(deps.callLog.includes("pauseAuto"), true);
|
|
2639
|
+
assert.equal(closeoutCalls, 0);
|
|
2640
|
+
assert.equal(preVerificationCalls, 0);
|
|
2641
|
+
assert.equal(postVerificationCalls, 0);
|
|
2642
|
+
assert.equal(s.currentUnit, null);
|
|
2643
|
+
assert.ok(
|
|
2644
|
+
journalEvents.some((event) =>
|
|
2645
|
+
event.eventType === "unit-end" &&
|
|
2646
|
+
event.data?.status === "cancelled" &&
|
|
2647
|
+
event.data?.errorContext?.message.includes("stale ghost completion")
|
|
2648
|
+
),
|
|
2649
|
+
"ghost completion should emit a cancelled unit-end",
|
|
2650
|
+
);
|
|
2651
|
+
});
|
|
2652
|
+
|
|
2076
2653
|
test("resolveAgentEndCancelled without args produces no errorContext field", async () => {
|
|
2077
2654
|
_resetPendingResolve();
|
|
2078
2655
|
|
|
@@ -2089,64 +2666,110 @@ test("resolveAgentEndCancelled without args produces no errorContext field", asy
|
|
|
2089
2666
|
assert.equal(resolved.errorContext, undefined, "errorContext must not be present when no args passed");
|
|
2090
2667
|
});
|
|
2091
2668
|
|
|
2669
|
+
test("resolveAgentEndCancelled queues cancellation that arrives during session switch", () => {
|
|
2670
|
+
_resetPendingResolve();
|
|
2671
|
+
|
|
2672
|
+
_setSessionSwitchInFlight(true);
|
|
2673
|
+
const resolved = resolveAgentEndCancelled({
|
|
2674
|
+
message: "Claude Code process aborted by user",
|
|
2675
|
+
category: "aborted",
|
|
2676
|
+
isTransient: false,
|
|
2677
|
+
});
|
|
2678
|
+
|
|
2679
|
+
assert.equal(resolved, false);
|
|
2680
|
+
const pending = _consumePendingSwitchCancellation();
|
|
2681
|
+
assert.ok(pending?.errorContext, "queued cancellation should preserve errorContext");
|
|
2682
|
+
assert.equal(pending.errorContext.category, "aborted");
|
|
2683
|
+
assert.equal(pending.errorContext.message, "Claude Code process aborted by user");
|
|
2684
|
+
assert.equal(_consumePendingSwitchCancellation(), null);
|
|
2685
|
+
_resetPendingResolve();
|
|
2686
|
+
});
|
|
2687
|
+
|
|
2688
|
+
test("session-switch abort grace window is short-lived and resettable", () => {
|
|
2689
|
+
_resetPendingResolve();
|
|
2690
|
+
|
|
2691
|
+
_markSessionSwitchAbortGraceWindow(1_000);
|
|
2692
|
+
|
|
2693
|
+
assert.equal(isSessionSwitchAbortGraceActive(Date.now()), true);
|
|
2694
|
+
assert.equal(isSessionSwitchAbortGraceActive(Date.now() + 10_000), false);
|
|
2695
|
+
|
|
2696
|
+
_clearSessionSwitchAbortGraceWindow();
|
|
2697
|
+
assert.equal(isSessionSwitchAbortGraceActive(), false);
|
|
2698
|
+
});
|
|
2699
|
+
|
|
2092
2700
|
// ─── #1571: artifact verification retry ──────────────────────────────────────
|
|
2093
2701
|
|
|
2094
2702
|
test("autoLoop re-iterates when postUnitPreVerification returns retry (#1571)", async () => {
|
|
2095
2703
|
_resetPendingResolve();
|
|
2704
|
+
mock.timers.enable({ apis: ["Date", "setTimeout"], now: 30_000 });
|
|
2096
2705
|
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2706
|
+
try {
|
|
2707
|
+
const ctx = makeMockCtx();
|
|
2708
|
+
ctx.ui.setStatus = () => {};
|
|
2709
|
+
const pi = makeMockPi();
|
|
2710
|
+
const s = makeLoopSession();
|
|
2101
2711
|
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2712
|
+
let preVerifyCallCount = 0;
|
|
2713
|
+
// Pre-queued responses: first call returns "retry", second returns "continue"
|
|
2714
|
+
const preVerifyResponses = ["retry", "continue"] as const;
|
|
2105
2715
|
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2716
|
+
const deps = makeMockDeps({
|
|
2717
|
+
deriveState: async () => {
|
|
2718
|
+
deps.callLog.push("deriveState");
|
|
2719
|
+
return {
|
|
2720
|
+
phase: "executing",
|
|
2721
|
+
activeMilestone: { id: "M001", title: "Test", status: "active" },
|
|
2722
|
+
activeSlice: { id: "S01", title: "Slice 1" },
|
|
2723
|
+
activeTask: { id: "T01" },
|
|
2724
|
+
registry: [{ id: "M001", status: "active" }],
|
|
2725
|
+
blockers: [],
|
|
2726
|
+
} as any;
|
|
2727
|
+
},
|
|
2728
|
+
postUnitPreVerification: async () => {
|
|
2729
|
+
deps.callLog.push("postUnitPreVerification");
|
|
2730
|
+
const response = preVerifyResponses[preVerifyCallCount++] ?? "continue";
|
|
2731
|
+
if (response === "retry") {
|
|
2732
|
+
s.pendingVerificationRetry = {
|
|
2733
|
+
unitId: "M001/S01/T01",
|
|
2734
|
+
failureContext: "missing artifact",
|
|
2735
|
+
attempt: 1,
|
|
2736
|
+
};
|
|
2737
|
+
}
|
|
2738
|
+
return response;
|
|
2739
|
+
},
|
|
2740
|
+
postUnitPostVerification: async () => {
|
|
2741
|
+
deps.callLog.push("postUnitPostVerification");
|
|
2742
|
+
s.active = false;
|
|
2743
|
+
return "continue" as const;
|
|
2744
|
+
},
|
|
2745
|
+
});
|
|
2128
2746
|
|
|
2129
|
-
|
|
2747
|
+
const loopPromise = autoLoop(ctx, pi, s, deps);
|
|
2130
2748
|
|
|
2131
|
-
|
|
2132
|
-
|
|
2749
|
+
await waitForMicrotasks(() => pi.calls.length === 1, "first dispatch");
|
|
2750
|
+
resolveAgentEnd(makeEvent());
|
|
2133
2751
|
|
|
2134
|
-
|
|
2135
|
-
|
|
2752
|
+
await drainMicrotasks(100);
|
|
2753
|
+
mock.timers.tick(30_000);
|
|
2754
|
+
await waitForMicrotasks(() => pi.calls.length === 2, "retry dispatch");
|
|
2755
|
+
resolveAgentEnd(makeEvent());
|
|
2136
2756
|
|
|
2137
|
-
|
|
2757
|
+
await loopPromise;
|
|
2138
2758
|
|
|
2139
|
-
|
|
2759
|
+
assert.equal(preVerifyCallCount, 2, "preVerification should be called twice");
|
|
2140
2760
|
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2761
|
+
const postVerifyCalls = deps.callLog.filter(
|
|
2762
|
+
(c: string) => c === "runPostUnitVerification",
|
|
2763
|
+
);
|
|
2764
|
+
const postPostVerifyCalls = deps.callLog.filter(
|
|
2765
|
+
(c: string) => c === "postUnitPostVerification",
|
|
2766
|
+
);
|
|
2147
2767
|
|
|
2148
|
-
|
|
2149
|
-
|
|
2768
|
+
assert.equal(postVerifyCalls.length, 1, "runPostUnitVerification should only be called once");
|
|
2769
|
+
assert.equal(postPostVerifyCalls.length, 1, "postUnitPostVerification should only be called once");
|
|
2770
|
+
} finally {
|
|
2771
|
+
mock.timers.reset();
|
|
2772
|
+
}
|
|
2150
2773
|
});
|
|
2151
2774
|
|
|
2152
2775
|
// ─── stopAuto unitPromise leak regression (#1799) ────────────────────────────
|
|
@@ -2465,7 +3088,7 @@ test("autoLoop rejects complete-slice with 0 tool calls as context-exhausted (#2
|
|
|
2465
3088
|
|
|
2466
3089
|
// ─── Worktree health check (#1833) ────────────────────────────────────────
|
|
2467
3090
|
|
|
2468
|
-
test("autoLoop stops when
|
|
3091
|
+
test("autoLoop stops when Worktree Safety finds no .git marker for execute-task (#1833)", async (t) => {
|
|
2469
3092
|
_resetPendingResolve();
|
|
2470
3093
|
|
|
2471
3094
|
const ctx = makeMockCtx();
|
|
@@ -2476,7 +3099,16 @@ test("autoLoop stops when worktree has no .git for execute-task (#1833)", async
|
|
|
2476
3099
|
const notifications: string[] = [];
|
|
2477
3100
|
ctx.ui.notify = (msg: string) => { notifications.push(msg); };
|
|
2478
3101
|
|
|
2479
|
-
const
|
|
3102
|
+
const projectRoot = mkdtempSync(join(tmpdir(), "gsd-wt-safety-loop-"));
|
|
3103
|
+
const worktreeRoot = join(projectRoot, ".gsd", "worktrees", "M001");
|
|
3104
|
+
mkdirSync(worktreeRoot, { recursive: true });
|
|
3105
|
+
t.after(() => rmSync(projectRoot, { recursive: true, force: true }));
|
|
3106
|
+
|
|
3107
|
+
const s = makeLoopSession({
|
|
3108
|
+
basePath: worktreeRoot,
|
|
3109
|
+
originalBasePath: projectRoot,
|
|
3110
|
+
canonicalProjectRoot: projectRoot,
|
|
3111
|
+
});
|
|
2480
3112
|
|
|
2481
3113
|
const deps = makeMockDeps({
|
|
2482
3114
|
deriveState: async () => {
|
|
@@ -2490,8 +3122,7 @@ test("autoLoop stops when worktree has no .git for execute-task (#1833)", async
|
|
|
2490
3122
|
blockers: [],
|
|
2491
3123
|
} as any;
|
|
2492
3124
|
},
|
|
2493
|
-
|
|
2494
|
-
existsSync: (p: string) => !p.endsWith(".git"),
|
|
3125
|
+
getIsolationMode: () => "worktree",
|
|
2495
3126
|
});
|
|
2496
3127
|
|
|
2497
3128
|
await autoLoop(ctx, pi, s, deps);
|
|
@@ -2501,11 +3132,283 @@ test("autoLoop stops when worktree has no .git for execute-task (#1833)", async
|
|
|
2501
3132
|
"should stop auto-mode when worktree is invalid",
|
|
2502
3133
|
);
|
|
2503
3134
|
const healthNotification = notifications.find(
|
|
2504
|
-
(n) => n.includes("Worktree
|
|
3135
|
+
(n) => n.includes("Worktree Safety failed") && n.includes("worktree-git-marker-missing"),
|
|
2505
3136
|
);
|
|
2506
3137
|
assert.ok(
|
|
2507
3138
|
healthNotification,
|
|
2508
|
-
"should notify about missing .git
|
|
3139
|
+
"should notify about missing worktree .git marker",
|
|
3140
|
+
);
|
|
3141
|
+
});
|
|
3142
|
+
|
|
3143
|
+
test("dispatch Worktree Safety wins before stuck detection for execute-task without .git", async (t) => {
|
|
3144
|
+
_resetPendingResolve();
|
|
3145
|
+
|
|
3146
|
+
const ctx = makeMockCtx();
|
|
3147
|
+
const pi = makeMockPi();
|
|
3148
|
+
const notifications: string[] = [];
|
|
3149
|
+
ctx.ui.notify = (msg: string) => { notifications.push(msg); };
|
|
3150
|
+
|
|
3151
|
+
const projectRoot = mkdtempSync(join(tmpdir(), "gsd-wt-safety-dispatch-"));
|
|
3152
|
+
const worktreeRoot = join(projectRoot, ".gsd", "worktrees", "M001");
|
|
3153
|
+
mkdirSync(worktreeRoot, { recursive: true });
|
|
3154
|
+
t.after(() => rmSync(projectRoot, { recursive: true, force: true }));
|
|
3155
|
+
|
|
3156
|
+
const s = makeLoopSession({
|
|
3157
|
+
basePath: worktreeRoot,
|
|
3158
|
+
originalBasePath: projectRoot,
|
|
3159
|
+
canonicalProjectRoot: projectRoot,
|
|
3160
|
+
});
|
|
3161
|
+
const deps = makeMockDeps({
|
|
3162
|
+
getIsolationMode: () => "worktree",
|
|
3163
|
+
});
|
|
3164
|
+
const result = await runDispatch(
|
|
3165
|
+
{
|
|
3166
|
+
ctx,
|
|
3167
|
+
pi,
|
|
3168
|
+
s,
|
|
3169
|
+
deps,
|
|
3170
|
+
prefs: undefined,
|
|
3171
|
+
iteration: 1,
|
|
3172
|
+
flowId: "test-flow",
|
|
3173
|
+
nextSeq: () => 1,
|
|
3174
|
+
},
|
|
3175
|
+
{
|
|
3176
|
+
state: {
|
|
3177
|
+
phase: "executing",
|
|
3178
|
+
activeMilestone: { id: "M001", title: "Test", status: "active" },
|
|
3179
|
+
activeSlice: { id: "S01", title: "Slice 1" },
|
|
3180
|
+
activeTask: { id: "T01" },
|
|
3181
|
+
registry: [{ id: "M001", status: "active" }],
|
|
3182
|
+
blockers: [],
|
|
3183
|
+
} as any,
|
|
3184
|
+
mid: "M001",
|
|
3185
|
+
midTitle: "Test",
|
|
3186
|
+
},
|
|
3187
|
+
{
|
|
3188
|
+
recentUnits: [
|
|
3189
|
+
{ key: "execute-task/M001/S01/T01" },
|
|
3190
|
+
{ key: "execute-task/M001/S01/T01" },
|
|
3191
|
+
],
|
|
3192
|
+
stuckRecoveryAttempts: 1,
|
|
3193
|
+
consecutiveFinalizeTimeouts: 0,
|
|
3194
|
+
},
|
|
3195
|
+
);
|
|
3196
|
+
|
|
3197
|
+
assert.equal(result.action, "break");
|
|
3198
|
+
assert.equal(result.reason, "worktree-git-marker-missing");
|
|
3199
|
+
assert.ok(deps.callLog.includes("stopAuto"), "should stop through Worktree Safety");
|
|
3200
|
+
assert.ok(
|
|
3201
|
+
notifications.some((n) => n.includes("Worktree Safety failed") && n.includes("worktree-git-marker-missing")),
|
|
3202
|
+
"should notify about missing worktree .git marker",
|
|
3203
|
+
);
|
|
3204
|
+
assert.ok(
|
|
3205
|
+
!notifications.some((n) => n.includes("Stuck on execute-task")),
|
|
3206
|
+
"stuck-loop message must not mask the worktree health failure",
|
|
3207
|
+
);
|
|
3208
|
+
});
|
|
3209
|
+
|
|
3210
|
+
test("dispatch Worktree Safety stops unknown unit types with missing Tool Contract", async (t) => {
|
|
3211
|
+
_resetPendingResolve();
|
|
3212
|
+
|
|
3213
|
+
const ctx = makeMockCtx();
|
|
3214
|
+
const pi = makeMockPi();
|
|
3215
|
+
const notifications: string[] = [];
|
|
3216
|
+
ctx.ui.notify = (msg: string) => { notifications.push(msg); };
|
|
3217
|
+
|
|
3218
|
+
const projectRoot = mkdtempSync(join(tmpdir(), "gsd-wt-safety-missing-contract-"));
|
|
3219
|
+
const worktreeRoot = join(projectRoot, ".gsd", "worktrees", "M001");
|
|
3220
|
+
mkdirSync(worktreeRoot, { recursive: true });
|
|
3221
|
+
t.after(() => rmSync(projectRoot, { recursive: true, force: true }));
|
|
3222
|
+
|
|
3223
|
+
const s = makeLoopSession({
|
|
3224
|
+
basePath: worktreeRoot,
|
|
3225
|
+
originalBasePath: projectRoot,
|
|
3226
|
+
canonicalProjectRoot: projectRoot,
|
|
3227
|
+
});
|
|
3228
|
+
const deps = makeMockDeps({
|
|
3229
|
+
getIsolationMode: () => "worktree",
|
|
3230
|
+
resolveDispatch: async () => {
|
|
3231
|
+
deps.callLog.push("resolveDispatch");
|
|
3232
|
+
return {
|
|
3233
|
+
action: "dispatch" as const,
|
|
3234
|
+
unitType: "new-source-writing-unit-without-manifest",
|
|
3235
|
+
unitId: "M001/S01/T01",
|
|
3236
|
+
prompt: "do the thing",
|
|
3237
|
+
};
|
|
3238
|
+
},
|
|
3239
|
+
});
|
|
3240
|
+
|
|
3241
|
+
const result = await runDispatch(
|
|
3242
|
+
{
|
|
3243
|
+
ctx,
|
|
3244
|
+
pi,
|
|
3245
|
+
s,
|
|
3246
|
+
deps,
|
|
3247
|
+
prefs: undefined,
|
|
3248
|
+
iteration: 1,
|
|
3249
|
+
flowId: "test-flow",
|
|
3250
|
+
nextSeq: () => 1,
|
|
3251
|
+
},
|
|
3252
|
+
{
|
|
3253
|
+
state: {
|
|
3254
|
+
phase: "executing",
|
|
3255
|
+
activeMilestone: { id: "M001", title: "Test", status: "active" },
|
|
3256
|
+
activeSlice: { id: "S01", title: "Slice 1" },
|
|
3257
|
+
activeTask: { id: "T01" },
|
|
3258
|
+
registry: [{ id: "M001", status: "active" }],
|
|
3259
|
+
blockers: [],
|
|
3260
|
+
} as any,
|
|
3261
|
+
mid: "M001",
|
|
3262
|
+
midTitle: "Test",
|
|
3263
|
+
},
|
|
3264
|
+
{
|
|
3265
|
+
recentUnits: [],
|
|
3266
|
+
stuckRecoveryAttempts: 0,
|
|
3267
|
+
consecutiveFinalizeTimeouts: 0,
|
|
3268
|
+
},
|
|
3269
|
+
);
|
|
3270
|
+
|
|
3271
|
+
assert.equal(result.action, "break");
|
|
3272
|
+
assert.equal(result.reason, "missing-tool-contract");
|
|
3273
|
+
assert.ok(deps.callLog.includes("stopAuto"), "should stop when the Tool Contract is missing");
|
|
3274
|
+
assert.ok(
|
|
3275
|
+
notifications.some((n) => n.includes("missing Tool Contract for new-source-writing-unit-without-manifest")),
|
|
3276
|
+
"should notify with an actionable missing Tool Contract reason",
|
|
3277
|
+
);
|
|
3278
|
+
});
|
|
3279
|
+
|
|
3280
|
+
test("pre-dispatch skip resolves before dispatch health and stuck accounting", async () => {
|
|
3281
|
+
_resetPendingResolve();
|
|
3282
|
+
|
|
3283
|
+
const ctx = makeMockCtx();
|
|
3284
|
+
const pi = makeMockPi();
|
|
3285
|
+
const notifications: string[] = [];
|
|
3286
|
+
ctx.ui.notify = (msg: string) => { notifications.push(msg); };
|
|
3287
|
+
|
|
3288
|
+
const s = makeLoopSession({ basePath: "/tmp/broken-worktree" });
|
|
3289
|
+
const deps = makeMockDeps({
|
|
3290
|
+
existsSync: (p: string) => !p.endsWith(".git"),
|
|
3291
|
+
runPreDispatchHooks: () => ({ firedHooks: ["skip-execute"], action: "skip" }),
|
|
3292
|
+
});
|
|
3293
|
+
const loopState = {
|
|
3294
|
+
recentUnits: [
|
|
3295
|
+
{ key: "execute-task/M001/S01/T01" },
|
|
3296
|
+
{ key: "execute-task/M001/S01/T01" },
|
|
3297
|
+
],
|
|
3298
|
+
stuckRecoveryAttempts: 1,
|
|
3299
|
+
consecutiveFinalizeTimeouts: 0,
|
|
3300
|
+
};
|
|
3301
|
+
|
|
3302
|
+
const result = await runDispatch(
|
|
3303
|
+
{
|
|
3304
|
+
ctx,
|
|
3305
|
+
pi,
|
|
3306
|
+
s,
|
|
3307
|
+
deps,
|
|
3308
|
+
prefs: undefined,
|
|
3309
|
+
iteration: 1,
|
|
3310
|
+
flowId: "test-flow",
|
|
3311
|
+
nextSeq: () => 1,
|
|
3312
|
+
},
|
|
3313
|
+
{
|
|
3314
|
+
state: {
|
|
3315
|
+
phase: "executing",
|
|
3316
|
+
activeMilestone: { id: "M001", title: "Test", status: "active" },
|
|
3317
|
+
activeSlice: { id: "S01", title: "Slice 1" },
|
|
3318
|
+
activeTask: { id: "T01" },
|
|
3319
|
+
registry: [{ id: "M001", status: "active" }],
|
|
3320
|
+
blockers: [],
|
|
3321
|
+
} as any,
|
|
3322
|
+
mid: "M001",
|
|
3323
|
+
midTitle: "Test",
|
|
3324
|
+
},
|
|
3325
|
+
loopState,
|
|
3326
|
+
);
|
|
3327
|
+
|
|
3328
|
+
assert.equal(result.action, "continue");
|
|
3329
|
+
assert.ok(!deps.callLog.includes("stopAuto"), "skip hook should not stop on worktree health");
|
|
3330
|
+
assert.equal(loopState.recentUnits.length, 2, "skip hook should not update stuck accounting");
|
|
3331
|
+
assert.ok(
|
|
3332
|
+
notifications.some((n) => n.includes("Skipping execute-task M001/S01/T01")),
|
|
3333
|
+
"should notify about the skip hook",
|
|
3334
|
+
);
|
|
3335
|
+
assert.ok(
|
|
3336
|
+
!notifications.some((n) => n.includes("Worktree health check failed") || n.includes("Stuck on execute-task")),
|
|
3337
|
+
"health and stuck notifications must not run before skip hook resolution",
|
|
3338
|
+
);
|
|
3339
|
+
});
|
|
3340
|
+
|
|
3341
|
+
test("pre-dispatch replace resolves final unit before dispatch health and stuck accounting", async () => {
|
|
3342
|
+
_resetPendingResolve();
|
|
3343
|
+
|
|
3344
|
+
const ctx = makeMockCtx();
|
|
3345
|
+
const pi = makeMockPi();
|
|
3346
|
+
const notifications: string[] = [];
|
|
3347
|
+
ctx.ui.notify = (msg: string) => { notifications.push(msg); };
|
|
3348
|
+
|
|
3349
|
+
const s = makeLoopSession({ basePath: "/tmp/broken-worktree" });
|
|
3350
|
+
const deps = makeMockDeps({
|
|
3351
|
+
existsSync: (p: string) => !p.endsWith(".git"),
|
|
3352
|
+
runPreDispatchHooks: () => ({
|
|
3353
|
+
firedHooks: ["review"],
|
|
3354
|
+
action: "replace",
|
|
3355
|
+
unitType: "run-uat",
|
|
3356
|
+
prompt: "review before executing",
|
|
3357
|
+
model: "review-model",
|
|
3358
|
+
}),
|
|
3359
|
+
});
|
|
3360
|
+
const loopState = {
|
|
3361
|
+
recentUnits: [
|
|
3362
|
+
{ key: "execute-task/M001/S01/T01" },
|
|
3363
|
+
{ key: "execute-task/M001/S01/T01" },
|
|
3364
|
+
],
|
|
3365
|
+
stuckRecoveryAttempts: 1,
|
|
3366
|
+
consecutiveFinalizeTimeouts: 0,
|
|
3367
|
+
};
|
|
3368
|
+
|
|
3369
|
+
const result = await runDispatch(
|
|
3370
|
+
{
|
|
3371
|
+
ctx,
|
|
3372
|
+
pi,
|
|
3373
|
+
s,
|
|
3374
|
+
deps,
|
|
3375
|
+
prefs: undefined,
|
|
3376
|
+
iteration: 1,
|
|
3377
|
+
flowId: "test-flow",
|
|
3378
|
+
nextSeq: () => 1,
|
|
3379
|
+
},
|
|
3380
|
+
{
|
|
3381
|
+
state: {
|
|
3382
|
+
phase: "executing",
|
|
3383
|
+
activeMilestone: { id: "M001", title: "Test", status: "active" },
|
|
3384
|
+
activeSlice: { id: "S01", title: "Slice 1" },
|
|
3385
|
+
activeTask: { id: "T01" },
|
|
3386
|
+
registry: [{ id: "M001", status: "active" }],
|
|
3387
|
+
blockers: [],
|
|
3388
|
+
} as any,
|
|
3389
|
+
mid: "M001",
|
|
3390
|
+
midTitle: "Test",
|
|
3391
|
+
},
|
|
3392
|
+
loopState,
|
|
3393
|
+
);
|
|
3394
|
+
|
|
3395
|
+
assert.equal(result.action, "next");
|
|
3396
|
+
assert.equal(result.data?.unitType, "run-uat");
|
|
3397
|
+
assert.equal(result.data?.finalPrompt, "review before executing");
|
|
3398
|
+
assert.equal(result.data?.hookModelOverride, "review-model");
|
|
3399
|
+
assert.ok(!deps.callLog.includes("stopAuto"), "replace hook should not stop on execute-task health");
|
|
3400
|
+
assert.deepEqual(
|
|
3401
|
+
loopState.recentUnits.map((u) => u.key),
|
|
3402
|
+
[
|
|
3403
|
+
"execute-task/M001/S01/T01",
|
|
3404
|
+
"execute-task/M001/S01/T01",
|
|
3405
|
+
"run-uat/M001/S01/T01",
|
|
3406
|
+
],
|
|
3407
|
+
"stuck accounting should record the final replaced unit",
|
|
3408
|
+
);
|
|
3409
|
+
assert.ok(
|
|
3410
|
+
!notifications.some((n) => n.includes("Worktree health check failed") || n.includes("Stuck on execute-task")),
|
|
3411
|
+
"health and stuck notifications must use the final replaced unit",
|
|
2509
3412
|
);
|
|
2510
3413
|
});
|
|
2511
3414
|
|
|
@@ -2769,6 +3672,13 @@ test("autoLoop classifies ModelPolicyDispatchBlockedError as blocked, not a retr
|
|
|
2769
3672
|
);
|
|
2770
3673
|
assert.ok(unitEnd, "should emit unit-end with status=blocked");
|
|
2771
3674
|
assert.equal(unitEnd!.data.reason, "model-policy-dispatch-blocked");
|
|
3675
|
+
const unitEndIndex = journalEvents.findIndex(
|
|
3676
|
+
e => e.eventType === "unit-end" && e.data?.status === "blocked",
|
|
3677
|
+
);
|
|
3678
|
+
const iterationEndIndex = journalEvents.findIndex(
|
|
3679
|
+
e => e.eventType === "iteration-end" && e.data?.status === "blocked",
|
|
3680
|
+
);
|
|
3681
|
+
assert.ok(iterationEndIndex > unitEndIndex, "blocked policy iterations must close after unit-end");
|
|
2772
3682
|
|
|
2773
3683
|
// Loop must pause for manual attention, NOT retry until 3-strike hard stop.
|
|
2774
3684
|
assert.equal(pauseAutoCalls, 1, "should pause once on policy block");
|