gsd-pi 2.80.0-dev.3065f7bfa → 2.80.0-dev.488219eb6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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 +29 -0
- package/dist/resources/extensions/github-sync/sync.js +4 -1
- package/dist/resources/extensions/gsd/auto/loop.js +71 -8
- package/dist/resources/extensions/gsd/auto/phases.js +32 -8
- package/dist/resources/extensions/gsd/auto/resolve.js +12 -0
- package/dist/resources/extensions/gsd/auto/run-unit.js +3 -1
- 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-post-unit.js +81 -76
- package/dist/resources/extensions/gsd/auto-start.js +41 -6
- package/dist/resources/extensions/gsd/auto-worktree.js +98 -80
- package/dist/resources/extensions/gsd/auto.js +84 -29
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +62 -13
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +20 -26
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +29 -12
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +6 -9
- package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +4 -7
- package/dist/resources/extensions/gsd/bootstrap/memory-tools.js +6 -10
- package/dist/resources/extensions/gsd/bootstrap/query-tools.js +5 -9
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +1 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +60 -26
- package/dist/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.js +4 -0
- 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/dashboard-overlay.js +1 -1
- package/dist/resources/extensions/gsd/db/unit-dispatches.js +53 -0
- package/dist/resources/extensions/gsd/git-service.js +37 -1
- package/dist/resources/extensions/gsd/guided-flow.js +14 -4
- package/dist/resources/extensions/gsd/init-wizard.js +4 -1
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +6 -4
- 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/tools/complete-milestone.js +6 -7
- package/dist/resources/extensions/gsd/tools/complete-task.js +1 -1
- package/dist/resources/extensions/gsd/worktree-resolver.js +22 -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 +17 -17
- 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 +17 -17
- 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 -0
- package/dist/welcome-screen.js +9 -7
- package/package.json +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/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/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/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/chat-controller-ordering.test.js +3 -1
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -2
- 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/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__/tool-execution.test.js +89 -2
- 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/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 +5 -0
- 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 +153 -8
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.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 +18 -1
- 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 +23 -5
- 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 +19 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js +23 -5
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js.map +1 -1
- package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +3 -1
- package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -1
- 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__/tool-execution.test.ts +119 -2
- 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 +177 -8
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +24 -5
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +17 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +21 -0
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage-safety-guard.test.ts +25 -8
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/claude-code-cli/readiness.ts +25 -7
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +30 -0
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +26 -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/loop.ts +84 -8
- package/src/resources/extensions/gsd/auto/phases.ts +43 -8
- package/src/resources/extensions/gsd/auto/resolve.ts +19 -0
- package/src/resources/extensions/gsd/auto/run-unit.ts +3 -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-post-unit.ts +93 -78
- package/src/resources/extensions/gsd/auto-start.ts +56 -6
- package/src/resources/extensions/gsd/auto-worktree.ts +118 -72
- package/src/resources/extensions/gsd/auto.ts +121 -29
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +76 -12
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +20 -26
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +27 -12
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +7 -9
- package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +4 -7
- package/src/resources/extensions/gsd/bootstrap/memory-tools.ts +6 -10
- package/src/resources/extensions/gsd/bootstrap/query-tools.ts +5 -9
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +1 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +64 -26
- package/src/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.ts +4 -0
- 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/dashboard-overlay.ts +1 -1
- package/src/resources/extensions/gsd/db/unit-dispatches.ts +66 -0
- package/src/resources/extensions/gsd/git-service.ts +41 -2
- package/src/resources/extensions/gsd/guided-flow.ts +19 -4
- package/src/resources/extensions/gsd/init-wizard.ts +5 -1
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +6 -4
- 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/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 +35 -9
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +80 -59
- 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 +76 -18
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +106 -0
- 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-remediate-slice-status.test.ts +32 -30
- 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 +138 -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-warning-noise-regression.test.ts +39 -51
- package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +1 -217
- 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/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/complete-milestone.test.ts +140 -0
- 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/crash-handler-secondary.test.ts +35 -31
- 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 +4 -9
- 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 +58 -40
- package/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts +25 -15
- 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/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/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 +12 -0
- package/src/resources/extensions/gsd/tests/integration/milestone-transition-worktree.test.ts +0 -47
- 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 -59
- package/src/resources/extensions/gsd/tests/lazy-pi-tui-import.test.ts +44 -6
- package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +21 -35
- package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +79 -98
- package/src/resources/extensions/gsd/tests/model-unittype-mapping.test.ts +70 -278
- 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/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 +113 -0
- 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-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-step-ordering.test.ts +70 -125
- 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-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 -90
- package/src/resources/extensions/gsd/tests/resume-dispatch-worktree.test.ts +32 -164
- package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +57 -41
- 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 +61 -1
- package/src/resources/extensions/gsd/tests/show-config-command.test.ts +44 -42
- 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 -97
- 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/status-db-open.test.ts +33 -47
- 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/token-profile.test.ts +84 -309
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +134 -341
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +8 -25
- package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +50 -1
- package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +6 -101
- package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +43 -36
- 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 +2 -57
- 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-main-branch.test.ts +20 -18
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/zero-slice-roadmap-guided.test.ts +19 -13
- 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/worktree-resolver.ts +34 -1
- 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/web/standalone/.next/static/chunks/8336.631939fb583761fa.js +0 -10
- /package/dist/web/standalone/.next/static/{O2WvnXROue6l7nAp_4dhC → G68KoLv5claox_Dyd4pZn}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{O2WvnXROue6l7nAp_4dhC → G68KoLv5claox_Dyd4pZn}/_ssgManifest.js +0 -0
|
@@ -25,11 +25,20 @@ import { execFileSync } from "node:child_process";
|
|
|
25
25
|
* we invoke `cmd /c <command> <args...>` explicitly. On POSIX we don't
|
|
26
26
|
* need a shell at all.
|
|
27
27
|
*/
|
|
28
|
-
function
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
export function buildClaudeSpawnInvocation(
|
|
29
|
+
command: string,
|
|
30
|
+
args: string[],
|
|
31
|
+
platform: NodeJS.Platform = process.platform,
|
|
32
|
+
): { command: string; args: string[] } {
|
|
33
|
+
if (platform === "win32") {
|
|
34
|
+
return { command: "cmd", args: ["/c", command, ...args] };
|
|
31
35
|
}
|
|
32
|
-
return
|
|
36
|
+
return { command, args };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function spawnClaude(command: string, args: string[], opts: { timeout: number; stdio: "pipe" }): Buffer {
|
|
40
|
+
const invocation = buildClaudeSpawnInvocation(command, args);
|
|
41
|
+
return execFileSync(invocation.command, invocation.args, opts);
|
|
33
42
|
}
|
|
34
43
|
|
|
35
44
|
/**
|
|
@@ -38,7 +47,11 @@ function spawnClaude(command: string, args: string[], opts: { timeout: number; s
|
|
|
38
47
|
* Keep the explicit win32 ternary selector for regression coverage (Issue #4424):
|
|
39
48
|
* Node's execFileSync must target `claude.cmd` directly on Windows.
|
|
40
49
|
*/
|
|
41
|
-
|
|
50
|
+
export function getClaudeCommand(platform: NodeJS.Platform = process.platform): string {
|
|
51
|
+
return platform === "win32" ? "claude.cmd" : "claude";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const CLAUDE_COMMAND = getClaudeCommand();
|
|
42
55
|
|
|
43
56
|
/**
|
|
44
57
|
* Windows installs vary: some environments expose `claude.cmd` (npm shim),
|
|
@@ -46,7 +59,12 @@ const CLAUDE_COMMAND = process.platform === "win32" ? "claude.cmd" : "claude";
|
|
|
46
59
|
* (for example Git Bash wrappers). Try all three to avoid false "not
|
|
47
60
|
* installed" results in readiness checks.
|
|
48
61
|
*/
|
|
49
|
-
|
|
62
|
+
export function getClaudeCommandCandidates(platform: NodeJS.Platform = process.platform): string[] {
|
|
63
|
+
const command = getClaudeCommand(platform);
|
|
64
|
+
return platform === "win32" ? [command, "claude.exe", "claude"] : [command];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const CLAUDE_COMMAND_CANDIDATES = getClaudeCommandCandidates();
|
|
50
68
|
|
|
51
69
|
// Keep the version probe snappy — `claude --version` is a quick path.
|
|
52
70
|
const VERSION_TIMEOUT_MS = 5_000;
|
|
@@ -97,7 +115,7 @@ function findWorkingCommand(): string | null {
|
|
|
97
115
|
* back to a text heuristic. Note: the text heuristic only covers English
|
|
98
116
|
* phrasing — the JSON path is the durable signal.
|
|
99
117
|
*/
|
|
100
|
-
function parseAuthStatus(output: string): boolean | null {
|
|
118
|
+
export function parseAuthStatus(output: string): boolean | null {
|
|
101
119
|
const trimmed = output.trim();
|
|
102
120
|
if (!trimmed) return null;
|
|
103
121
|
|
|
@@ -418,6 +418,27 @@ function makeErrorMessage(model: string, errorMsg: string): AssistantMessage {
|
|
|
418
418
|
};
|
|
419
419
|
}
|
|
420
420
|
|
|
421
|
+
export function isClaudeCodeAbortErrorMessage(message: string | undefined | null): boolean {
|
|
422
|
+
if (!message) return false;
|
|
423
|
+
return /\b(?:claude code process aborted by user|request aborted by user|process aborted by user)\b/i.test(message);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function isBareClaudeCodeAbortErrorMessage(message: string | undefined | null): boolean {
|
|
427
|
+
if (!message) return false;
|
|
428
|
+
const normalized = message.trim().replace(/\s+/g, " ").toLowerCase();
|
|
429
|
+
return normalized === "claude code process aborted by user"
|
|
430
|
+
|| normalized === "request aborted by user"
|
|
431
|
+
|| normalized === "process aborted by user";
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
export function resolveClaudeCodeAbortedMessageText(errorMsg: string, lastTextContent: string): string {
|
|
435
|
+
const trimmedError = errorMsg.trim();
|
|
436
|
+
if (trimmedError && !isBareClaudeCodeAbortErrorMessage(trimmedError)) {
|
|
437
|
+
return trimmedError;
|
|
438
|
+
}
|
|
439
|
+
return lastTextContent;
|
|
440
|
+
}
|
|
441
|
+
|
|
421
442
|
/**
|
|
422
443
|
* Generator exhaustion without a terminal result means the SDK stream was
|
|
423
444
|
* interrupted mid-turn. Surface it as an error so downstream recovery logic
|
|
@@ -1840,6 +1861,15 @@ async function pumpSdkMessages(
|
|
|
1840
1861
|
stream.push({ type: "error", reason: "error", error: fallback });
|
|
1841
1862
|
} catch (err) {
|
|
1842
1863
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
1864
|
+
if (options?.signal?.aborted || isClaudeCodeAbortErrorMessage(errorMsg)) {
|
|
1865
|
+
const abortedText = resolveClaudeCodeAbortedMessageText(errorMsg, lastTextContent);
|
|
1866
|
+
stream.push({
|
|
1867
|
+
type: "error",
|
|
1868
|
+
reason: "aborted",
|
|
1869
|
+
error: makeAbortedMessage(modelId, abortedText),
|
|
1870
|
+
});
|
|
1871
|
+
return;
|
|
1872
|
+
}
|
|
1843
1873
|
stream.push({
|
|
1844
1874
|
type: "error",
|
|
1845
1875
|
reason: "error",
|
|
@@ -5,6 +5,8 @@ import { join, resolve } from "node:path";
|
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import {
|
|
7
7
|
makeStreamExhaustedErrorMessage,
|
|
8
|
+
isClaudeCodeAbortErrorMessage,
|
|
9
|
+
resolveClaudeCodeAbortedMessageText,
|
|
8
10
|
getResultErrorMessage,
|
|
9
11
|
makeAbortedMessage,
|
|
10
12
|
mergePendingToolCalls,
|
|
@@ -1212,6 +1214,12 @@ describe("stream-adapter — MCP elicitation bridge", () => {
|
|
|
1212
1214
|
// ---------------------------------------------------------------------------
|
|
1213
1215
|
|
|
1214
1216
|
describe("stream-adapter — abort classification (F2)", () => {
|
|
1217
|
+
test("recognizes Claude Code SDK abort exceptions", () => {
|
|
1218
|
+
assert.equal(isClaudeCodeAbortErrorMessage("Claude Code process aborted by user"), true);
|
|
1219
|
+
assert.equal(isClaudeCodeAbortErrorMessage("Request aborted by user"), true);
|
|
1220
|
+
assert.equal(isClaudeCodeAbortErrorMessage("rate limit exceeded"), false);
|
|
1221
|
+
});
|
|
1222
|
+
|
|
1215
1223
|
test("makeAbortedMessage sets stopReason to 'aborted', not 'error'", () => {
|
|
1216
1224
|
const message = makeAbortedMessage("claude-sonnet-4-6", "");
|
|
1217
1225
|
assert.equal(message.stopReason, "aborted");
|
|
@@ -1229,6 +1237,24 @@ describe("stream-adapter — abort classification (F2)", () => {
|
|
|
1229
1237
|
assert.notEqual(aborted.stopReason, exhausted.stopReason);
|
|
1230
1238
|
assert.equal(exhausted.errorMessage, "stream_exhausted_without_result");
|
|
1231
1239
|
});
|
|
1240
|
+
|
|
1241
|
+
test("abort catch preserves SDK diagnostic text instead of partial output", () => {
|
|
1242
|
+
const text = resolveClaudeCodeAbortedMessageText(
|
|
1243
|
+
"Request aborted by user\nAPI Error: 529 overloaded",
|
|
1244
|
+
"partial mid-stream text",
|
|
1245
|
+
);
|
|
1246
|
+
|
|
1247
|
+
assert.equal(text, "Request aborted by user\nAPI Error: 529 overloaded");
|
|
1248
|
+
});
|
|
1249
|
+
|
|
1250
|
+
test("abort catch falls back to partial output for bare abort markers", () => {
|
|
1251
|
+
const text = resolveClaudeCodeAbortedMessageText(
|
|
1252
|
+
"Request aborted by user",
|
|
1253
|
+
"partial mid-stream text",
|
|
1254
|
+
);
|
|
1255
|
+
|
|
1256
|
+
assert.equal(text, "partial mid-stream text");
|
|
1257
|
+
});
|
|
1232
1258
|
});
|
|
1233
1259
|
|
|
1234
1260
|
// ---------------------------------------------------------------------------
|
|
@@ -122,6 +122,13 @@ export async function runGitHubSync(
|
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
export function shouldCreateSlicePrForSyncEvent(
|
|
126
|
+
unitType: string,
|
|
127
|
+
config: Pick<GitHubSyncConfig, "slice_prs">,
|
|
128
|
+
): boolean {
|
|
129
|
+
return unitType === "complete-slice" && config.slice_prs !== false;
|
|
130
|
+
}
|
|
131
|
+
|
|
125
132
|
// ─── Per-Event Sync Functions ───────────────────────────────────────────────
|
|
126
133
|
|
|
127
134
|
async function syncMilestonePlan(
|
|
@@ -406,7 +413,7 @@ async function syncSliceComplete(
|
|
|
406
413
|
sliceRecord = getSliceRecord(mapping, mid, sid);
|
|
407
414
|
}
|
|
408
415
|
if (!sliceRecord || sliceRecord.state === "closed") return;
|
|
409
|
-
if (!sliceRecord.prNumber && config
|
|
416
|
+
if (!sliceRecord.prNumber && shouldCreateSlicePrForSyncEvent("complete-slice", config)) {
|
|
410
417
|
await ensureSlicePullRequest(basePath, mapping, mid, sid);
|
|
411
418
|
sliceRecord = getSliceRecord(mapping, mid, sid);
|
|
412
419
|
if (!sliceRecord || !sliceRecord.prNumber) return;
|
|
@@ -1,26 +1,14 @@
|
|
|
1
1
|
import test from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
-
import {
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
|
|
6
|
-
const source = readFileSync(join(import.meta.dirname, "..", "sync.ts"), "utf8");
|
|
3
|
+
import { shouldCreateSlicePrForSyncEvent } from "../sync.ts";
|
|
7
4
|
|
|
8
5
|
test("slice plan sync records issues without creating a draft PR", () => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
assert.ok(planStart >= 0 && prHelperStart > planStart, "slice plan and PR helper functions are present");
|
|
12
|
-
|
|
13
|
-
const planBody = source.slice(planStart, prHelperStart);
|
|
14
|
-
assert.match(planBody, /prNumber:\s*0/);
|
|
15
|
-
assert.doesNotMatch(planBody, /ghCreatePR\(/);
|
|
16
|
-
assert.doesNotMatch(planBody, /ghPushBranch\(/);
|
|
6
|
+
assert.equal(shouldCreateSlicePrForSyncEvent("plan-slice", { slice_prs: true }), false);
|
|
7
|
+
assert.equal(shouldCreateSlicePrForSyncEvent("research-slice", { slice_prs: true }), false);
|
|
17
8
|
});
|
|
18
9
|
|
|
19
10
|
test("slice completion creates the PR only after slice work is complete", () => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
assert.
|
|
23
|
-
|
|
24
|
-
const completeBody = source.slice(completeStart, milestoneStart);
|
|
25
|
-
assert.match(completeBody, /ensureSlicePullRequest\(basePath, mapping, mid, sid\)/);
|
|
11
|
+
assert.equal(shouldCreateSlicePrForSyncEvent("complete-slice", { slice_prs: true }), true);
|
|
12
|
+
assert.equal(shouldCreateSlicePrForSyncEvent("complete-slice", {}), true);
|
|
13
|
+
assert.equal(shouldCreateSlicePrForSyncEvent("complete-slice", { slice_prs: false }), false);
|
|
26
14
|
});
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
getRecentForUnit as getRecentDispatchesForUnit,
|
|
39
39
|
getRecentUnitKeysForProjectRoot,
|
|
40
40
|
} from "../db/unit-dispatches.js";
|
|
41
|
-
import { refreshMilestoneLease } from "../db/milestone-leases.js";
|
|
41
|
+
import { claimMilestoneLease, refreshMilestoneLease } from "../db/milestone-leases.js";
|
|
42
42
|
import { heartbeatAutoWorker } from "../db/auto-workers.js";
|
|
43
43
|
import { getRuntimeKv, setRuntimeKv } from "../db/runtime-kv.js";
|
|
44
44
|
import { resolveUokFlags } from "../uok/flags.js";
|
|
@@ -70,7 +70,7 @@ import {
|
|
|
70
70
|
} from "./workflow-dispatch-ledger.js";
|
|
71
71
|
import { emitOpenUnitEndForUnit } from "../crash-recovery.js";
|
|
72
72
|
import { writeUnitRuntimeRecord } from "../unit-runtime.js";
|
|
73
|
-
import { openDispatchClaim } from "./workflow-dispatch-claim.js";
|
|
73
|
+
import { ensureDispatchLease, openDispatchClaim } from "./workflow-dispatch-claim.js";
|
|
74
74
|
import { completeWorkflowIteration } from "./workflow-iteration-completion.js";
|
|
75
75
|
import { createWorkflowJournalReporter } from "./workflow-journal-reporter.js";
|
|
76
76
|
import { createWorkflowPhaseReporter } from "./workflow-phase-reporter.js";
|
|
@@ -165,6 +165,29 @@ function logDispatchClaimFailed(err: unknown): void {
|
|
|
165
165
|
});
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
function logDispatchLeaseRecovered(details: {
|
|
169
|
+
milestoneId: string;
|
|
170
|
+
workerId: string;
|
|
171
|
+
token: number;
|
|
172
|
+
recovered: boolean;
|
|
173
|
+
}): void {
|
|
174
|
+
debugLog("autoLoop", {
|
|
175
|
+
phase: details.recovered ? "dispatch-lease-recovered" : "dispatch-lease-acquired",
|
|
176
|
+
...details,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function logDispatchLeaseRecoveryFailed(details: {
|
|
181
|
+
milestoneId?: string;
|
|
182
|
+
workerId?: string;
|
|
183
|
+
reason: string;
|
|
184
|
+
}): void {
|
|
185
|
+
debugLog("autoLoop", {
|
|
186
|
+
phase: "dispatch-lease-recovery-failed",
|
|
187
|
+
...details,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
168
191
|
function logCustomVerifyRetryLoadFailure(err: unknown): void {
|
|
169
192
|
debugLog("autoLoop", {
|
|
170
193
|
phase: "load-custom-verify-retries-failed",
|
|
@@ -274,6 +297,10 @@ export async function autoLoop(
|
|
|
274
297
|
phase: "heartbeat-failed",
|
|
275
298
|
error: err instanceof Error ? err.message : String(err),
|
|
276
299
|
}),
|
|
300
|
+
logLeaseRefreshMiss: details => debugLog("autoLoop", {
|
|
301
|
+
phase: "lease-refresh-missed",
|
|
302
|
+
...details,
|
|
303
|
+
}),
|
|
277
304
|
});
|
|
278
305
|
|
|
279
306
|
// ── Journal: per-iteration flow grouping ──
|
|
@@ -703,25 +730,74 @@ export async function autoLoop(
|
|
|
703
730
|
|
|
704
731
|
// Phase B: claim a unit_dispatches row before invoking the unit. The
|
|
705
732
|
// partial unique index idx_unit_dispatches_active_per_unit prevents
|
|
706
|
-
// a second worker from claiming the same unit concurrently.
|
|
707
|
-
//
|
|
708
|
-
//
|
|
709
|
-
//
|
|
710
|
-
const
|
|
733
|
+
// a second worker from claiming the same unit concurrently. When this
|
|
734
|
+
// process has a worker identity, make the milestone lease explicit before
|
|
735
|
+
// claiming so a step-mode handoff cannot leave us running with a stale
|
|
736
|
+
// in-memory token and no backing lease row.
|
|
737
|
+
const leaseBeforeClaim = ensureDispatchLease(s, iterData.mid, {
|
|
738
|
+
claimMilestoneLease,
|
|
739
|
+
logLeaseRecovered: logDispatchLeaseRecovered,
|
|
740
|
+
logLeaseRecoveryFailed: logDispatchLeaseRecoveryFailed,
|
|
741
|
+
});
|
|
742
|
+
if (leaseBeforeClaim.kind === "blocked" || leaseBeforeClaim.kind === "failed") {
|
|
743
|
+
const msg = `Lost milestone lease for ${iterData.mid ?? "unknown"} before dispatching ${iterData.unitType} ${iterData.unitId}: ${leaseBeforeClaim.reason}`;
|
|
744
|
+
ctx.ui.notify(msg, "error");
|
|
745
|
+
finishTurn("stopped", "execution", msg);
|
|
746
|
+
await deps.stopAuto(ctx, pi, msg);
|
|
747
|
+
break;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
let dispatchClaim = openDispatchClaim(s, flowId, turnId, iterData, {
|
|
711
751
|
getRecentDispatchesForUnit,
|
|
712
752
|
recordDispatchClaim,
|
|
713
753
|
markDispatchRunning,
|
|
714
754
|
logClaimRejected: logDispatchClaimRejected,
|
|
715
755
|
logClaimFailed: logDispatchClaimFailed,
|
|
716
756
|
});
|
|
717
|
-
|
|
757
|
+
let dispatchDecision = decideDispatchClaim(
|
|
718
758
|
dispatchClaim.kind === "opened"
|
|
719
759
|
? { kind: "opened", dispatchId: dispatchClaim.dispatchId }
|
|
720
760
|
: dispatchClaim.kind === "skip"
|
|
721
761
|
? { kind: "skip", reason: dispatchClaim.reason }
|
|
722
762
|
: { kind: "degraded" },
|
|
723
763
|
);
|
|
764
|
+
if (dispatchDecision.action === "skip" && dispatchDecision.reason === "stale-lease") {
|
|
765
|
+
const leaseRecovery = ensureDispatchLease(s, iterData.mid, {
|
|
766
|
+
claimMilestoneLease,
|
|
767
|
+
logLeaseRecovered: logDispatchLeaseRecovered,
|
|
768
|
+
logLeaseRecoveryFailed: logDispatchLeaseRecoveryFailed,
|
|
769
|
+
}, { forceReclaim: true });
|
|
770
|
+
if (leaseRecovery.kind === "ready") {
|
|
771
|
+
dispatchClaim = openDispatchClaim(s, flowId, turnId, iterData, {
|
|
772
|
+
getRecentDispatchesForUnit,
|
|
773
|
+
recordDispatchClaim,
|
|
774
|
+
markDispatchRunning,
|
|
775
|
+
logClaimRejected: logDispatchClaimRejected,
|
|
776
|
+
logClaimFailed: logDispatchClaimFailed,
|
|
777
|
+
});
|
|
778
|
+
dispatchDecision = decideDispatchClaim(
|
|
779
|
+
dispatchClaim.kind === "opened"
|
|
780
|
+
? { kind: "opened", dispatchId: dispatchClaim.dispatchId }
|
|
781
|
+
: dispatchClaim.kind === "skip"
|
|
782
|
+
? { kind: "skip", reason: dispatchClaim.reason }
|
|
783
|
+
: { kind: "degraded" },
|
|
784
|
+
);
|
|
785
|
+
} else {
|
|
786
|
+
const msg = `Lost milestone lease for ${iterData.mid ?? "unknown"} while claiming ${iterData.unitType} ${iterData.unitId}: ${leaseRecovery.reason}`;
|
|
787
|
+
ctx.ui.notify(msg, "error");
|
|
788
|
+
finishTurn("stopped", "execution", msg);
|
|
789
|
+
await deps.stopAuto(ctx, pi, msg);
|
|
790
|
+
break;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
724
793
|
if (dispatchDecision.action === "skip") {
|
|
794
|
+
if (dispatchDecision.reason === "stale-lease") {
|
|
795
|
+
const msg = `Lost milestone lease for ${iterData.mid ?? "unknown"} while claiming ${iterData.unitType} ${iterData.unitId}; dispatch claim still failed after recovery.`;
|
|
796
|
+
ctx.ui.notify(msg, "error");
|
|
797
|
+
finishTurn("stopped", "execution", msg);
|
|
798
|
+
await deps.stopAuto(ctx, pi, msg);
|
|
799
|
+
break;
|
|
800
|
+
}
|
|
725
801
|
finishTurn("skipped", "execution", dispatchDecision.reason);
|
|
726
802
|
continue;
|
|
727
803
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Project/App: GSD-2
|
|
2
|
+
// File Purpose: Auto-loop pipeline phases, merge closeout, and finalize handling.
|
|
1
3
|
/**
|
|
2
4
|
* auto/phases.ts — Pipeline phases for the auto-loop.
|
|
3
5
|
*
|
|
@@ -126,10 +128,23 @@ const PLAN_V2_GATE_PHASES: ReadonlySet<Phase> = new Set([
|
|
|
126
128
|
"completing-milestone",
|
|
127
129
|
]);
|
|
128
130
|
|
|
129
|
-
function shouldRunPlanV2Gate(phase: Phase): boolean {
|
|
131
|
+
export function shouldRunPlanV2Gate(phase: Phase): boolean {
|
|
130
132
|
return PLAN_V2_GATE_PHASES.has(phase);
|
|
131
133
|
}
|
|
132
134
|
|
|
135
|
+
export function _shouldProceedWithInvalidRepoClassificationForTest(
|
|
136
|
+
reason: string | undefined,
|
|
137
|
+
hasGit: boolean,
|
|
138
|
+
): boolean {
|
|
139
|
+
return reason === "missing .git" && hasGit;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function _resolveCurrentUnitStartedAtForTest(
|
|
143
|
+
currentUnit: { startedAt: number } | null | undefined,
|
|
144
|
+
): number | undefined {
|
|
145
|
+
return currentUnit?.startedAt;
|
|
146
|
+
}
|
|
147
|
+
|
|
133
148
|
/**
|
|
134
149
|
* Generate and write an HTML milestone report snapshot.
|
|
135
150
|
* Extracted from the milestone-transition block in autoLoop.
|
|
@@ -332,6 +347,21 @@ export async function _runMilestoneMergeWithStashRestore(
|
|
|
332
347
|
return null;
|
|
333
348
|
}
|
|
334
349
|
|
|
350
|
+
export async function _runMilestoneMergeOnceWithStashRestore(
|
|
351
|
+
ic: IterationContext,
|
|
352
|
+
milestoneId: string,
|
|
353
|
+
): Promise<{ action: "break"; reason: string } | null> {
|
|
354
|
+
if (ic.s.milestoneMergedInPhases) {
|
|
355
|
+
debugLog("autoLoop", {
|
|
356
|
+
phase: "milestone-merge-skip",
|
|
357
|
+
reason: "already-merged-in-phases",
|
|
358
|
+
milestoneId,
|
|
359
|
+
});
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
return _runMilestoneMergeWithStashRestore(ic, milestoneId);
|
|
363
|
+
}
|
|
364
|
+
|
|
335
365
|
async function emitCancelledUnitEnd(
|
|
336
366
|
ic: IterationContext,
|
|
337
367
|
unitType: string,
|
|
@@ -771,7 +801,7 @@ export async function runPreDispatch(
|
|
|
771
801
|
// Worktree lifecycle on milestone transition — merge current, enter next.
|
|
772
802
|
// #2909 / #5538-followup: preflight stash + always-on postflight pop.
|
|
773
803
|
{
|
|
774
|
-
const stop = await
|
|
804
|
+
const stop = await _runMilestoneMergeOnceWithStashRestore(ic, s.currentMilestoneId!);
|
|
775
805
|
if (stop) return stop;
|
|
776
806
|
}
|
|
777
807
|
|
|
@@ -853,7 +883,7 @@ export async function runPreDispatch(
|
|
|
853
883
|
// All milestones complete — merge milestone branch before stopping.
|
|
854
884
|
if (s.currentMilestoneId) {
|
|
855
885
|
// #2909 / #5538-followup: preflight stash + always-on postflight pop.
|
|
856
|
-
const stop = await
|
|
886
|
+
const stop = await _runMilestoneMergeOnceWithStashRestore(ic, s.currentMilestoneId);
|
|
857
887
|
if (stop) return stop;
|
|
858
888
|
// PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
|
|
859
889
|
}
|
|
@@ -948,7 +978,7 @@ export async function runPreDispatch(
|
|
|
948
978
|
// Milestone merge on complete (before closeout so branch state is clean).
|
|
949
979
|
if (s.currentMilestoneId) {
|
|
950
980
|
// #2909 / #5538-followup: preflight stash + always-on postflight pop.
|
|
951
|
-
const stop = await
|
|
981
|
+
const stop = await _runMilestoneMergeOnceWithStashRestore(ic, s.currentMilestoneId);
|
|
952
982
|
if (stop) return stop;
|
|
953
983
|
// PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
|
|
954
984
|
}
|
|
@@ -1553,7 +1583,7 @@ export async function runUnitPhase(
|
|
|
1553
1583
|
if (projectClassification.kind === "invalid-repo") {
|
|
1554
1584
|
const msg = `Worktree health check failed: ${s.basePath} classified as invalid-repo (${projectClassification.reason}) — refusing to dispatch ${unitType} ${unitId}`;
|
|
1555
1585
|
debugLog("runUnitPhase", { phase: "worktree-health-invalid-repo", basePath: s.basePath, classification: projectClassification });
|
|
1556
|
-
if (projectClassification.reason
|
|
1586
|
+
if (_shouldProceedWithInvalidRepoClassificationForTest(projectClassification.reason, hasGit)) {
|
|
1557
1587
|
ctx.ui.notify(
|
|
1558
1588
|
`Warning: ${s.basePath} project classification could not confirm .git; assuming it has no project content yet — proceeding as greenfield project because worktree health reported .git present`,
|
|
1559
1589
|
"warning",
|
|
@@ -2068,7 +2098,7 @@ export async function runUnitPhase(
|
|
|
2068
2098
|
const currentLedger = deps.getLedger() as { units: Array<{ type: string; id: string; startedAt: number; toolCalls: number }> } | null;
|
|
2069
2099
|
if (currentLedger?.units) {
|
|
2070
2100
|
const lastUnit = [...currentLedger.units].reverse().find(
|
|
2071
|
-
(u: { type: string; id: string; startedAt: number; toolCalls: number }) => u.type === unitType && u.id === unitId && u.startedAt === s.currentUnit
|
|
2101
|
+
(u: { type: string; id: string; startedAt: number; toolCalls: number }) => u.type === unitType && u.id === unitId && u.startedAt === _resolveCurrentUnitStartedAtForTest(s.currentUnit),
|
|
2072
2102
|
);
|
|
2073
2103
|
if (lastUnit && lastUnit.toolCalls === 0) {
|
|
2074
2104
|
if (USER_DRIVEN_DEEP_UNITS.has(unitType) && isAwaitingUserInput(s.lastUnitAgentEndMessages ?? undefined)) {
|
|
@@ -2090,7 +2120,7 @@ export async function runUnitPhase(
|
|
|
2090
2120
|
);
|
|
2091
2121
|
// Fall through to next iteration where dispatch will re-derive
|
|
2092
2122
|
// and re-dispatch this unit.
|
|
2093
|
-
return { action: "next", data: { unitStartedAt: s.currentUnit
|
|
2123
|
+
return { action: "next", data: { unitStartedAt: _resolveCurrentUnitStartedAtForTest(s.currentUnit), requestDispatchedAt: unitResult.requestDispatchedAt } };
|
|
2094
2124
|
}
|
|
2095
2125
|
}
|
|
2096
2126
|
}
|
|
@@ -2155,7 +2185,7 @@ export async function runUnitPhase(
|
|
|
2155
2185
|
s.checkpointSha = null;
|
|
2156
2186
|
}
|
|
2157
2187
|
|
|
2158
|
-
return { action: "next", data: { unitStartedAt: s.currentUnit
|
|
2188
|
+
return { action: "next", data: { unitStartedAt: _resolveCurrentUnitStartedAtForTest(s.currentUnit), requestDispatchedAt: unitResult.requestDispatchedAt } };
|
|
2159
2189
|
}
|
|
2160
2190
|
|
|
2161
2191
|
// ─── runFinalize ──────────────────────────────────────────────────────────────
|
|
@@ -2335,6 +2365,11 @@ export async function runFinalize(
|
|
|
2335
2365
|
return { action: "break", reason: "step-wizard" };
|
|
2336
2366
|
}
|
|
2337
2367
|
|
|
2368
|
+
if (preUnitSnapshot?.type === "complete-milestone" && s.currentMilestoneId) {
|
|
2369
|
+
const stop = await _runMilestoneMergeOnceWithStashRestore(ic, s.currentMilestoneId);
|
|
2370
|
+
if (stop) return stop;
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2338
2373
|
// Both pre and post verification completed without timeout — reset counter
|
|
2339
2374
|
loopState.consecutiveFinalizeTimeouts = 0;
|
|
2340
2375
|
if (preUnitSnapshot) {
|
|
@@ -23,6 +23,9 @@ import { bumpTurnGeneration } from "./turn-epoch.js";
|
|
|
23
23
|
let _currentResolve: ((result: UnitResult) => void) | null = null;
|
|
24
24
|
let _sessionSwitchInFlight = false;
|
|
25
25
|
let _pendingSwitchCancellation: { errorContext?: ErrorContext } | null = null;
|
|
26
|
+
let _sessionSwitchAbortGraceUntil = 0;
|
|
27
|
+
|
|
28
|
+
const DEFAULT_SESSION_SWITCH_ABORT_GRACE_MS = 2_000;
|
|
26
29
|
|
|
27
30
|
// ─── Setters (needed for cross-module mutation) ─────────────────────────────
|
|
28
31
|
|
|
@@ -34,6 +37,21 @@ export function _setSessionSwitchInFlight(v: boolean): void {
|
|
|
34
37
|
_sessionSwitchInFlight = v;
|
|
35
38
|
}
|
|
36
39
|
|
|
40
|
+
export function _markSessionSwitchAbortGraceWindow(durationMs = DEFAULT_SESSION_SWITCH_ABORT_GRACE_MS): void {
|
|
41
|
+
_sessionSwitchAbortGraceUntil = Math.max(
|
|
42
|
+
_sessionSwitchAbortGraceUntil,
|
|
43
|
+
Date.now() + durationMs,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function _clearSessionSwitchAbortGraceWindow(): void {
|
|
48
|
+
_sessionSwitchAbortGraceUntil = 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function isSessionSwitchAbortGraceActive(now = Date.now()): boolean {
|
|
52
|
+
return now < _sessionSwitchAbortGraceUntil;
|
|
53
|
+
}
|
|
54
|
+
|
|
37
55
|
export function _clearCurrentResolve(): void {
|
|
38
56
|
_currentResolve = null;
|
|
39
57
|
}
|
|
@@ -142,6 +160,7 @@ export function _resetPendingResolve(): void {
|
|
|
142
160
|
_currentResolve = null;
|
|
143
161
|
_sessionSwitchInFlight = false;
|
|
144
162
|
_pendingSwitchCancellation = null;
|
|
163
|
+
_sessionSwitchAbortGraceUntil = 0;
|
|
145
164
|
}
|
|
146
165
|
|
|
147
166
|
export function _hasPendingResolveForTest(): boolean {
|
|
@@ -14,6 +14,7 @@ import type { UnitResult } from "./types.js";
|
|
|
14
14
|
import {
|
|
15
15
|
_clearCurrentResolve,
|
|
16
16
|
_consumePendingSwitchCancellation,
|
|
17
|
+
_markSessionSwitchAbortGraceWindow,
|
|
17
18
|
_setCurrentResolve,
|
|
18
19
|
_setSessionSwitchInFlight,
|
|
19
20
|
} from "./resolve.js";
|
|
@@ -65,6 +66,7 @@ export async function runUnit(
|
|
|
65
66
|
workspaceRoot: s.basePath,
|
|
66
67
|
}).finally(() => {
|
|
67
68
|
if (sessionSwitchGeneration === mySessionSwitchGeneration) {
|
|
69
|
+
_markSessionSwitchAbortGraceWindow();
|
|
68
70
|
_setSessionSwitchInFlight(false);
|
|
69
71
|
}
|
|
70
72
|
});
|
|
@@ -128,6 +130,7 @@ export async function runUnit(
|
|
|
128
130
|
// ── Create the agent_end promise (per-unit one-shot) ──
|
|
129
131
|
// This happens after newSession completes so session-switch agent_end events
|
|
130
132
|
// from the previous session cannot resolve the new unit.
|
|
133
|
+
_markSessionSwitchAbortGraceWindow();
|
|
131
134
|
_setSessionSwitchInFlight(false);
|
|
132
135
|
const unitPromise = new Promise<UnitResult>((resolve) => {
|
|
133
136
|
_setCurrentResolve(resolve);
|
|
@@ -9,6 +9,16 @@ export type DispatchClaimOutcome =
|
|
|
9
9
|
| { kind: "skip"; reason: "already-active" | "stale-lease"; existingId?: number; existingWorker?: string }
|
|
10
10
|
| { kind: "degraded" };
|
|
11
11
|
|
|
12
|
+
export type DispatchLeaseOutcome =
|
|
13
|
+
| { kind: "ready"; token: number; recovered: boolean }
|
|
14
|
+
| { kind: "degraded"; reason: "missing-worker" | "missing-milestone" }
|
|
15
|
+
| { kind: "blocked"; reason: string }
|
|
16
|
+
| { kind: "failed"; reason: string };
|
|
17
|
+
|
|
18
|
+
type ClaimMilestoneLeaseResult =
|
|
19
|
+
| { ok: true; token: number; expiresAt: string }
|
|
20
|
+
| { ok: false; error: "held_by"; byWorker: string; expiresAt: string };
|
|
21
|
+
|
|
12
22
|
interface RecentDispatch {
|
|
13
23
|
attempt_n?: number | null;
|
|
14
24
|
}
|
|
@@ -44,6 +54,58 @@ export interface OpenDispatchClaimDeps {
|
|
|
44
54
|
logClaimFailed: (err: unknown) => void;
|
|
45
55
|
}
|
|
46
56
|
|
|
57
|
+
export interface EnsureDispatchLeaseDeps {
|
|
58
|
+
claimMilestoneLease: (workerId: string, milestoneId: string) => ClaimMilestoneLeaseResult;
|
|
59
|
+
logLeaseRecovered: (details: {
|
|
60
|
+
milestoneId: string;
|
|
61
|
+
workerId: string;
|
|
62
|
+
token: number;
|
|
63
|
+
recovered: boolean;
|
|
64
|
+
}) => void;
|
|
65
|
+
logLeaseRecoveryFailed: (details: {
|
|
66
|
+
milestoneId?: string;
|
|
67
|
+
workerId?: string;
|
|
68
|
+
reason: string;
|
|
69
|
+
}) => void;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function ensureDispatchLease(
|
|
73
|
+
s: AutoSession,
|
|
74
|
+
milestoneId: string | undefined,
|
|
75
|
+
deps: EnsureDispatchLeaseDeps,
|
|
76
|
+
opts: { forceReclaim?: boolean } = {},
|
|
77
|
+
): DispatchLeaseOutcome {
|
|
78
|
+
if (!s.workerId) return { kind: "degraded", reason: "missing-worker" };
|
|
79
|
+
if (!milestoneId) return { kind: "degraded", reason: "missing-milestone" };
|
|
80
|
+
if (!opts.forceReclaim && typeof s.milestoneLeaseToken === "number") {
|
|
81
|
+
return { kind: "ready", token: s.milestoneLeaseToken, recovered: false };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
s.milestoneLeaseToken = null;
|
|
85
|
+
try {
|
|
86
|
+
const claim = deps.claimMilestoneLease(s.workerId, milestoneId);
|
|
87
|
+
if (!claim.ok) {
|
|
88
|
+
const reason = `Milestone ${milestoneId} is held by worker ${claim.byWorker} until ${claim.expiresAt}.`;
|
|
89
|
+
deps.logLeaseRecoveryFailed({ milestoneId, workerId: s.workerId, reason });
|
|
90
|
+
return { kind: "blocked", reason };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
s.currentMilestoneId = milestoneId;
|
|
94
|
+
s.milestoneLeaseToken = claim.token;
|
|
95
|
+
deps.logLeaseRecovered({
|
|
96
|
+
milestoneId,
|
|
97
|
+
workerId: s.workerId,
|
|
98
|
+
token: claim.token,
|
|
99
|
+
recovered: opts.forceReclaim === true,
|
|
100
|
+
});
|
|
101
|
+
return { kind: "ready", token: claim.token, recovered: opts.forceReclaim === true };
|
|
102
|
+
} catch (err) {
|
|
103
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
104
|
+
deps.logLeaseRecoveryFailed({ milestoneId, workerId: s.workerId, reason });
|
|
105
|
+
return { kind: "failed", reason };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
47
109
|
export function openDispatchClaim(
|
|
48
110
|
s: AutoSession,
|
|
49
111
|
flowId: string,
|
|
@@ -51,7 +113,7 @@ export function openDispatchClaim(
|
|
|
51
113
|
iterData: IterationData,
|
|
52
114
|
deps: OpenDispatchClaimDeps,
|
|
53
115
|
): DispatchClaimOutcome {
|
|
54
|
-
if (!s.workerId || s.milestoneLeaseToken
|
|
116
|
+
if (!s.workerId || typeof s.milestoneLeaseToken !== "number") return { kind: "degraded" };
|
|
55
117
|
const mid = iterData.mid;
|
|
56
118
|
if (!mid) return { kind: "degraded" };
|
|
57
119
|
|
|
@@ -15,6 +15,11 @@ export interface MaintainWorkerHeartbeatDeps {
|
|
|
15
15
|
fencingToken: number,
|
|
16
16
|
) => boolean;
|
|
17
17
|
logHeartbeatFailure: (err: unknown) => void;
|
|
18
|
+
logLeaseRefreshMiss?: (details: {
|
|
19
|
+
workerId: string;
|
|
20
|
+
milestoneId: string;
|
|
21
|
+
fencingToken: number;
|
|
22
|
+
}) => void;
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
export function maintainWorkerHeartbeat(
|
|
@@ -26,11 +31,19 @@ export function maintainWorkerHeartbeat(
|
|
|
26
31
|
try {
|
|
27
32
|
deps.heartbeatAutoWorker(session.workerId);
|
|
28
33
|
if (session.currentMilestoneId && session.milestoneLeaseToken) {
|
|
29
|
-
deps.refreshMilestoneLease(
|
|
34
|
+
const refreshed = deps.refreshMilestoneLease(
|
|
30
35
|
session.workerId,
|
|
31
36
|
session.currentMilestoneId,
|
|
32
37
|
session.milestoneLeaseToken,
|
|
33
38
|
);
|
|
39
|
+
if (!refreshed) {
|
|
40
|
+
deps.logLeaseRefreshMiss?.({
|
|
41
|
+
workerId: session.workerId,
|
|
42
|
+
milestoneId: session.currentMilestoneId,
|
|
43
|
+
fencingToken: session.milestoneLeaseToken,
|
|
44
|
+
});
|
|
45
|
+
session.milestoneLeaseToken = null;
|
|
46
|
+
}
|
|
34
47
|
}
|
|
35
48
|
} catch (err) {
|
|
36
49
|
deps.logHeartbeatFailure(err);
|