@opengsd/gsd-pi 1.2.0-dev.9ad8ae33 → 1.2.0-dev.a6376d75
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/cli-model-override.d.ts +15 -0
- package/dist/cli-model-override.js +21 -0
- package/dist/cli.js +1 -18
- package/dist/loader.js +6 -4
- package/dist/register-agent-bundles.d.ts +11 -2
- package/dist/register-agent-bundles.js +18 -4
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/ask-user-questions.js +3 -2
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +447 -215
- package/dist/resources/extensions/claude-code-cli/turn-assembler.js +33 -1
- package/dist/resources/extensions/gsd/auto/closeout.js +215 -0
- package/dist/resources/extensions/gsd/auto/dispatch-history.js +21 -6
- package/dist/resources/extensions/gsd/auto/dispatch.js +365 -0
- package/dist/resources/extensions/gsd/auto/finalize.js +347 -0
- package/dist/resources/extensions/gsd/auto/loop.js +4 -1
- package/dist/resources/extensions/gsd/auto/milestone-lease-reclaim.js +56 -0
- package/dist/resources/extensions/gsd/auto/orchestrator.js +85 -15
- package/dist/resources/extensions/gsd/auto/phase-helpers.js +146 -0
- package/dist/resources/extensions/gsd/auto/phases.js +17 -2372
- package/dist/resources/extensions/gsd/auto/pre-dispatch.js +534 -0
- package/dist/resources/extensions/gsd/auto/unit-phase.js +694 -0
- package/dist/resources/extensions/gsd/auto/workflow-unit-dispatch.js +1 -1
- package/dist/resources/extensions/gsd/auto/worktree-safety-phase.js +125 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +1 -1
- package/dist/resources/extensions/gsd/auto.js +15 -1
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +37 -7
- package/dist/resources/extensions/gsd/commands-mcp-status.js +2 -2
- package/dist/resources/extensions/gsd/commands-workflow-templates.js +9 -2
- package/dist/resources/extensions/gsd/db/queries.js +30 -0
- package/dist/resources/extensions/gsd/doctor-environment.js +256 -125
- package/dist/resources/extensions/gsd/guided-flow.js +88 -2
- package/dist/resources/extensions/gsd/health-widget.js +87 -28
- package/dist/resources/extensions/gsd/mcp-bridge.js +10 -0
- package/dist/resources/extensions/gsd/milestone-settlement.js +2 -2
- package/dist/resources/extensions/gsd/notifications.js +12 -7
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -1
- package/dist/resources/extensions/gsd/prompts/run-uat.md +2 -0
- package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -1
- package/dist/resources/extensions/gsd/skill-activation.js +3 -6
- package/dist/resources/extensions/gsd/state.js +6 -2
- package/dist/resources/extensions/gsd/tool-surface-readiness.js +83 -31
- package/dist/resources/extensions/gsd/tools/complete-task.js +62 -0
- package/dist/resources/extensions/gsd/unit-context-composer.js +1 -1
- package/dist/resources/extensions/gsd/unit-registry.js +34 -4
- package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +2 -0
- package/dist/resources/extensions/gsd/workflow-mcp-readiness-cache.js +105 -0
- package/dist/resources/extensions/gsd/worktree-safety.js +28 -26
- package/dist/resources/extensions/mcp-client/manager.js +6 -1
- package/dist/runtime-checks.d.ts +10 -0
- package/dist/runtime-checks.js +27 -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 +8 -8
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +2 -2
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/dist/sdk.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/sdk.js +6 -4
- package/packages/gsd-agent-core/dist/sdk.js.map +1 -1
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js +10 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +8 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +50 -6
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts +2 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +34 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +12 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js +4 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/README.md +12 -3
- package/packages/mcp-server/dist/cli-runner.d.ts +40 -0
- package/packages/mcp-server/dist/cli-runner.d.ts.map +1 -0
- package/packages/mcp-server/dist/cli-runner.js +137 -0
- package/packages/mcp-server/dist/cli-runner.js.map +1 -0
- package/packages/mcp-server/dist/cli.js +2 -58
- package/packages/mcp-server/dist/cli.js.map +1 -1
- package/packages/mcp-server/dist/pid-registry.d.ts +46 -0
- package/packages/mcp-server/dist/pid-registry.d.ts.map +1 -0
- package/packages/mcp-server/dist/pid-registry.js +452 -0
- package/packages/mcp-server/dist/pid-registry.js.map +1 -0
- package/packages/mcp-server/dist/probe-mode.d.ts +4 -0
- package/packages/mcp-server/dist/probe-mode.d.ts.map +1 -0
- package/packages/mcp-server/dist/probe-mode.js +10 -0
- package/packages/mcp-server/dist/probe-mode.js.map +1 -0
- package/packages/mcp-server/dist/stdio-watchdog.d.ts +8 -0
- package/packages/mcp-server/dist/stdio-watchdog.d.ts.map +1 -0
- package/packages/mcp-server/dist/stdio-watchdog.js +40 -0
- package/packages/mcp-server/dist/stdio-watchdog.js.map +1 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +62 -43
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +5 -5
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +43 -2
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -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 +11 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme.js +45 -17
- package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/dist/index.d.ts +1 -1
- package/packages/pi-tui/dist/index.d.ts.map +1 -1
- package/packages/pi-tui/dist/index.js +1 -1
- package/packages/pi-tui/dist/index.js.map +1 -1
- package/packages/pi-tui/dist/terminal-image.d.ts +33 -0
- package/packages/pi-tui/dist/terminal-image.d.ts.map +1 -1
- package/packages/pi-tui/dist/terminal-image.js +54 -2
- package/packages/pi-tui/dist/terminal-image.js.map +1 -1
- package/packages/pi-tui/dist/tui.d.ts +8 -0
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +63 -18
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/dist/utils.d.ts.map +1 -1
- package/packages/pi-tui/dist/utils.js +110 -36
- package/packages/pi-tui/dist/utils.js.map +1 -1
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/dist/theme/theme.d.ts.map +1 -1
- package/pkg/dist/theme/theme.js +45 -17
- package/pkg/dist/theme/theme.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/ask-user-questions.ts +7 -2
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +531 -226
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +672 -7
- package/src/resources/extensions/claude-code-cli/turn-assembler.ts +38 -1
- package/src/resources/extensions/gsd/auto/closeout.ts +309 -0
- package/src/resources/extensions/gsd/auto/dispatch-history.ts +22 -6
- package/src/resources/extensions/gsd/auto/dispatch.ts +449 -0
- package/src/resources/extensions/gsd/auto/finalize.ts +445 -0
- package/src/resources/extensions/gsd/auto/loop.ts +4 -1
- package/src/resources/extensions/gsd/auto/milestone-lease-reclaim.ts +74 -0
- package/src/resources/extensions/gsd/auto/orchestrator.ts +95 -15
- package/src/resources/extensions/gsd/auto/phase-helpers.ts +199 -0
- package/src/resources/extensions/gsd/auto/phases.ts +58 -3061
- package/src/resources/extensions/gsd/auto/pre-dispatch.ts +704 -0
- package/src/resources/extensions/gsd/auto/unit-phase.ts +910 -0
- package/src/resources/extensions/gsd/auto/workflow-unit-dispatch.ts +1 -1
- package/src/resources/extensions/gsd/auto/worktree-safety-phase.ts +149 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +1 -1
- package/src/resources/extensions/gsd/auto.ts +20 -1
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +56 -6
- package/src/resources/extensions/gsd/commands-mcp-status.ts +2 -2
- package/src/resources/extensions/gsd/commands-workflow-templates.ts +11 -4
- package/src/resources/extensions/gsd/db/queries.ts +29 -0
- package/src/resources/extensions/gsd/doctor-environment.ts +267 -142
- package/src/resources/extensions/gsd/guided-flow.ts +128 -2
- package/src/resources/extensions/gsd/health-widget.ts +91 -27
- package/src/resources/extensions/gsd/mcp-bridge.ts +39 -0
- package/src/resources/extensions/gsd/milestone-settlement.ts +2 -2
- package/src/resources/extensions/gsd/notifications.ts +13 -6
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/execute-task.md +2 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +2 -0
- package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -1
- package/src/resources/extensions/gsd/skill-activation.ts +3 -6
- package/src/resources/extensions/gsd/state.ts +7 -1
- package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-blocked-remediation-message.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +206 -22
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +76 -12
- package/src/resources/extensions/gsd/tests/auto-pause-double-entry-guard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +77 -1
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/auto-unit-closeout.test.ts +169 -1
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +141 -5
- package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/dispatch-history.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/dist-redirect.mjs +8 -0
- package/src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts +117 -91
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +113 -0
- package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/integration/doctor-environment-async.test.ts +104 -0
- package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +47 -16
- package/src/resources/extensions/gsd/tests/mcp-readiness-preflight.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +6 -5
- package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/milestone-report-path.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/milestone-settlement.test.ts +92 -0
- package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/notifications.test.ts +64 -9
- package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +5 -0
- package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/phases-terminal-complete-idempotent.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/plan-gate-failed-doctor-heal-hint.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +10 -2
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -4
- package/src/resources/extensions/gsd/tests/remote-notification-from-desktop.test.ts +31 -81
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +20 -17
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +7 -3
- package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/tool-surface-readiness.test.ts +184 -10
- package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/workflow-mcp-readiness-cache.test.ts +119 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +65 -2
- package/src/resources/extensions/gsd/tests/workflow-phase-contract-matrix.test.ts +332 -0
- package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +92 -0
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-project-root-degrade.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-safety-phase.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +72 -0
- package/src/resources/extensions/gsd/tool-surface-readiness.ts +126 -19
- package/src/resources/extensions/gsd/tools/complete-task.ts +87 -0
- package/src/resources/extensions/gsd/unit-context-composer.ts +1 -1
- package/src/resources/extensions/gsd/unit-registry.ts +34 -4
- package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +2 -0
- package/src/resources/extensions/gsd/workflow-mcp-readiness-cache.ts +150 -0
- package/src/resources/extensions/gsd/worktree-safety.ts +41 -39
- package/src/resources/extensions/mcp-client/manager.ts +7 -1
- /package/dist/web/standalone/.next/static/{FBNo5cT_chy7YNoAQsU3o → xyMkEaICFHJoa98VgJyzY}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{FBNo5cT_chy7YNoAQsU3o → xyMkEaICFHJoa98VgJyzY}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Auto-loop finalize phase — post-unit verification and UAT pause.
|
|
3
|
+
|
|
4
|
+
import type { SidecarItem } from "./session.js";
|
|
5
|
+
import {
|
|
6
|
+
type PostUnitContext,
|
|
7
|
+
type PreVerificationOpts,
|
|
8
|
+
} from "../auto-post-unit.js";
|
|
9
|
+
import { clearCurrentPhase } from "../../shared/gsd-phase-state.js";
|
|
10
|
+
import { withTimeout, FINALIZE_PRE_TIMEOUT_MS, FINALIZE_POST_TIMEOUT_MS } from "./finalize-timeout.js";
|
|
11
|
+
import { writeUnitRuntimeRecord } from "../unit-runtime.js";
|
|
12
|
+
import { buildManualValidationGuidance } from "../worktree-manager.js";
|
|
13
|
+
import { relSliceFile } from "../paths.js";
|
|
14
|
+
import {
|
|
15
|
+
detectRootWriteLeak,
|
|
16
|
+
formatRootWriteLeakMessage,
|
|
17
|
+
} from "../root-write-leak-guard.js";
|
|
18
|
+
import {
|
|
19
|
+
logWarning,
|
|
20
|
+
drainLogs,
|
|
21
|
+
drainAndSummarize,
|
|
22
|
+
formatForNotification,
|
|
23
|
+
hasAnyIssues,
|
|
24
|
+
} from "../workflow-logger.js";
|
|
25
|
+
import { debugLog } from "../debug-logger.js";
|
|
26
|
+
import { buildPhaseHandoffOutcome, setAutoOutcomeWidget } from "../auto-dashboard.js";
|
|
27
|
+
import {
|
|
28
|
+
applyVerificationRetryPolicy,
|
|
29
|
+
rememberRetryDispatch,
|
|
30
|
+
_resolveCurrentUnitStartedAtForTest,
|
|
31
|
+
isIsolatedWorktreeSession,
|
|
32
|
+
} from "./phase-helpers.js";
|
|
33
|
+
import { _runMilestoneMergeOnceWithStashRestore } from "./closeout.js";
|
|
34
|
+
import type { IterationContext, IterationData, LoopState, PhaseResult } from "./types.js";
|
|
35
|
+
import { MAX_FINALIZE_TIMEOUTS } from "./types.js";
|
|
36
|
+
|
|
37
|
+
export async function failClosedOnFinalizeTimeout(
|
|
38
|
+
ic: IterationContext,
|
|
39
|
+
iterData: IterationData,
|
|
40
|
+
loopState: LoopState,
|
|
41
|
+
stage: "pre" | "post",
|
|
42
|
+
startedAt: number,
|
|
43
|
+
): Promise<PhaseResult> {
|
|
44
|
+
const { ctx, pi, s, deps } = ic;
|
|
45
|
+
const now = Date.now();
|
|
46
|
+
const unitType = iterData.unitType;
|
|
47
|
+
const unitId = iterData.unitId;
|
|
48
|
+
const timeoutMs = stage === "pre" ? FINALIZE_PRE_TIMEOUT_MS : FINALIZE_POST_TIMEOUT_MS;
|
|
49
|
+
const progressKind = stage === "pre" ? "finalize-pre-timeout" : "finalize-post-timeout";
|
|
50
|
+
|
|
51
|
+
writeUnitRuntimeRecord(s.basePath, unitType, unitId, startedAt, {
|
|
52
|
+
phase: "finalize-timeout",
|
|
53
|
+
timeoutAt: now,
|
|
54
|
+
lastProgressAt: now,
|
|
55
|
+
lastProgressKind: progressKind,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
deps.emitJournalEvent({
|
|
59
|
+
ts: new Date(now).toISOString(),
|
|
60
|
+
flowId: ic.flowId,
|
|
61
|
+
seq: ic.nextSeq(),
|
|
62
|
+
eventType: "unit-end",
|
|
63
|
+
data: {
|
|
64
|
+
unitType,
|
|
65
|
+
unitId,
|
|
66
|
+
status: "timed-out-finalize",
|
|
67
|
+
artifactVerified: false,
|
|
68
|
+
finalizeStage: stage,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
loopState.consecutiveFinalizeTimeouts++;
|
|
73
|
+
debugLog("autoLoop", {
|
|
74
|
+
phase: progressKind,
|
|
75
|
+
iteration: ic.iteration,
|
|
76
|
+
unitType,
|
|
77
|
+
unitId,
|
|
78
|
+
consecutiveTimeouts: loopState.consecutiveFinalizeTimeouts,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
ctx.ui.notify(
|
|
82
|
+
`${stage === "pre" ? "postUnitPreVerification" : "postUnitPostVerification"} timed out after ${timeoutMs / 1000}s for ${unitType} ${unitId} (${loopState.consecutiveFinalizeTimeouts}/${MAX_FINALIZE_TIMEOUTS}) — pausing auto-mode for recovery.`,
|
|
83
|
+
"warning",
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
await deps.pauseAuto(ctx, pi);
|
|
87
|
+
s.clearCurrentUnit();
|
|
88
|
+
clearCurrentPhase();
|
|
89
|
+
drainLogs();
|
|
90
|
+
return { action: "break", reason: progressKind };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Phase 5: Post-unit finalize — pre/post verification, UAT pause, step-wizard.
|
|
95
|
+
* Returns break/continue/next to control the outer loop.
|
|
96
|
+
*/
|
|
97
|
+
export async function runFinalize(
|
|
98
|
+
ic: IterationContext,
|
|
99
|
+
iterData: IterationData,
|
|
100
|
+
loopState: LoopState,
|
|
101
|
+
sidecarItem?: SidecarItem,
|
|
102
|
+
): Promise<PhaseResult> {
|
|
103
|
+
const { ctx, pi, s, deps } = ic;
|
|
104
|
+
const { pauseAfterUatDispatch } = iterData;
|
|
105
|
+
|
|
106
|
+
debugLog("autoLoop", { phase: "finalize", iteration: ic.iteration });
|
|
107
|
+
|
|
108
|
+
// Clear unit timeout (unit completed)
|
|
109
|
+
deps.clearUnitTimeout();
|
|
110
|
+
|
|
111
|
+
// Post-unit context for pre/post verification
|
|
112
|
+
const postUnitCtx: PostUnitContext = {
|
|
113
|
+
s,
|
|
114
|
+
ctx,
|
|
115
|
+
pi,
|
|
116
|
+
buildSnapshotOpts: deps.buildSnapshotOpts,
|
|
117
|
+
lockBase: deps.lockBase,
|
|
118
|
+
stopAuto: deps.stopAuto,
|
|
119
|
+
pauseAuto: deps.pauseAuto,
|
|
120
|
+
updateProgressWidget: deps.updateProgressWidget,
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Pre-verification processing (commit, doctor, state rebuild, etc.)
|
|
124
|
+
// Timeout guard: if postUnitPreVerification hangs (e.g., safety harness
|
|
125
|
+
// deadlock, browser teardown hang, worktree sync stall), force-continue
|
|
126
|
+
// after timeout so the auto-loop is not permanently frozen (#3757).
|
|
127
|
+
//
|
|
128
|
+
// On timeout, null out s.currentUnit so the timed-out task's late async
|
|
129
|
+
// mutations are harmless — postUnitPreVerification guards all side effects
|
|
130
|
+
// behind `if (s.currentUnit)`. The next iteration sets a fresh currentUnit.
|
|
131
|
+
// Sidecar items use lightweight pre-verification opts
|
|
132
|
+
const preVerificationOpts: PreVerificationOpts = sidecarItem
|
|
133
|
+
? sidecarItem.kind === "hook"
|
|
134
|
+
? { skipSettleDelay: true, skipWorktreeSync: true, agentEndMessages: s.lastUnitAgentEndMessages ?? undefined }
|
|
135
|
+
: { skipSettleDelay: true, agentEndMessages: s.lastUnitAgentEndMessages ?? undefined }
|
|
136
|
+
: { agentEndMessages: s.lastUnitAgentEndMessages ?? undefined };
|
|
137
|
+
const preUnitSnapshot = s.currentUnit
|
|
138
|
+
? { type: s.currentUnit.type, id: s.currentUnit.id, startedAt: s.currentUnit.startedAt }
|
|
139
|
+
: null;
|
|
140
|
+
const clearFinalizingUnit = () => {
|
|
141
|
+
if (
|
|
142
|
+
preUnitSnapshot &&
|
|
143
|
+
s.currentUnit?.type === preUnitSnapshot.type &&
|
|
144
|
+
s.currentUnit?.id === preUnitSnapshot.id &&
|
|
145
|
+
s.currentUnit?.startedAt === preUnitSnapshot.startedAt
|
|
146
|
+
) {
|
|
147
|
+
s.clearCurrentUnit();
|
|
148
|
+
}
|
|
149
|
+
s.rootWriteBaseline = null;
|
|
150
|
+
};
|
|
151
|
+
clearCurrentPhase();
|
|
152
|
+
const preResultGuard = await withTimeout(
|
|
153
|
+
deps.postUnitPreVerification(postUnitCtx, preVerificationOpts),
|
|
154
|
+
FINALIZE_PRE_TIMEOUT_MS,
|
|
155
|
+
"postUnitPreVerification",
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
if (preResultGuard.timedOut) {
|
|
159
|
+
return failClosedOnFinalizeTimeout(
|
|
160
|
+
ic,
|
|
161
|
+
iterData,
|
|
162
|
+
loopState,
|
|
163
|
+
"pre",
|
|
164
|
+
preUnitSnapshot?.startedAt ?? Date.now(),
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const preResult = preResultGuard.value;
|
|
169
|
+
if (preResult === "dispatched") {
|
|
170
|
+
const dispatchedReason = s.lastGitActionFailure
|
|
171
|
+
? "git-closeout-failure"
|
|
172
|
+
: "pre-verification-dispatched";
|
|
173
|
+
debugLog("autoLoop", {
|
|
174
|
+
phase: "exit",
|
|
175
|
+
reason: dispatchedReason,
|
|
176
|
+
gitError: s.lastGitActionFailure ?? undefined,
|
|
177
|
+
});
|
|
178
|
+
clearFinalizingUnit();
|
|
179
|
+
return { action: "break", reason: dispatchedReason };
|
|
180
|
+
}
|
|
181
|
+
if (preResult === "retry") {
|
|
182
|
+
if (sidecarItem) {
|
|
183
|
+
// Sidecar artifact retries are skipped — just continue
|
|
184
|
+
debugLog("autoLoop", { phase: "sidecar-artifact-retry-skipped", iteration: ic.iteration });
|
|
185
|
+
} else {
|
|
186
|
+
// s.pendingVerificationRetry was set by postUnitPreVerification.
|
|
187
|
+
// Emit a dedicated journal event so forensics can distinguish bounded
|
|
188
|
+
// verification retries from genuine stuck-loop dispatch repetitions (#4540).
|
|
189
|
+
const retryInfo = s.pendingVerificationRetry;
|
|
190
|
+
deps.emitJournalEvent({
|
|
191
|
+
ts: new Date().toISOString(),
|
|
192
|
+
flowId: ic.flowId,
|
|
193
|
+
seq: ic.nextSeq(),
|
|
194
|
+
eventType: "artifact-verification-retry",
|
|
195
|
+
data: {
|
|
196
|
+
unitType: preUnitSnapshot?.type,
|
|
197
|
+
unitId: retryInfo?.unitId,
|
|
198
|
+
attempt: retryInfo?.attempt,
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
const retryPolicyResult = await applyVerificationRetryPolicy(
|
|
202
|
+
ic,
|
|
203
|
+
preUnitSnapshot?.type,
|
|
204
|
+
"artifact-verification-retry",
|
|
205
|
+
);
|
|
206
|
+
if (retryPolicyResult) {
|
|
207
|
+
clearFinalizingUnit();
|
|
208
|
+
return retryPolicyResult;
|
|
209
|
+
}
|
|
210
|
+
// Continue the loop — next iteration will inject the retry context into the prompt.
|
|
211
|
+
rememberRetryDispatch(s, preUnitSnapshot, iterData);
|
|
212
|
+
debugLog("autoLoop", { phase: "artifact-verification-retry", iteration: ic.iteration });
|
|
213
|
+
clearFinalizingUnit();
|
|
214
|
+
return { action: "continue" };
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (pauseAfterUatDispatch) {
|
|
219
|
+
const pauseMid = iterData.mid;
|
|
220
|
+
const pauseSliceId = pauseMid && iterData.unitId.startsWith(`${pauseMid}/`)
|
|
221
|
+
? iterData.unitId.slice(pauseMid.length + 1)
|
|
222
|
+
: undefined;
|
|
223
|
+
const guidance = pauseMid
|
|
224
|
+
? buildManualValidationGuidance(s.basePath, pauseMid, {
|
|
225
|
+
uatPath: pauseSliceId
|
|
226
|
+
? relSliceFile(s.basePath, pauseMid, pauseSliceId, "UAT")
|
|
227
|
+
: undefined,
|
|
228
|
+
})
|
|
229
|
+
: null;
|
|
230
|
+
const pauseMessage = guidance
|
|
231
|
+
? `UAT requires human execution. Auto-mode will pause after this unit writes the result file.\n\n${guidance}`
|
|
232
|
+
: "UAT requires human execution. Auto-mode will pause after this unit writes the result file.";
|
|
233
|
+
ctx.ui.notify(pauseMessage, "info");
|
|
234
|
+
await deps.pauseAuto(ctx, pi);
|
|
235
|
+
debugLog("autoLoop", { phase: "exit", reason: "uat-pause" });
|
|
236
|
+
clearFinalizingUnit();
|
|
237
|
+
return { action: "break", reason: "uat-pause" };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Verification gate
|
|
241
|
+
// Hook sidecar items skip verification entirely.
|
|
242
|
+
// Non-hook sidecar items run verification but skip retries (just continue).
|
|
243
|
+
const skipVerification = sidecarItem?.kind === "hook";
|
|
244
|
+
if (!skipVerification) {
|
|
245
|
+
const verificationResult = await deps.runPostUnitVerification(
|
|
246
|
+
{ s, ctx, pi },
|
|
247
|
+
deps.pauseAuto,
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
if (verificationResult === "pause") {
|
|
251
|
+
debugLog("autoLoop", { phase: "exit", reason: "verification-pause" });
|
|
252
|
+
clearFinalizingUnit();
|
|
253
|
+
return { action: "break", reason: "verification-pause" };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (verificationResult === "retry") {
|
|
257
|
+
if (sidecarItem) {
|
|
258
|
+
// Sidecar verification retries are skipped — just continue
|
|
259
|
+
debugLog("autoLoop", { phase: "sidecar-verification-retry-skipped", iteration: ic.iteration });
|
|
260
|
+
} else {
|
|
261
|
+
// s.pendingVerificationRetry was set by runPostUnitVerification.
|
|
262
|
+
const retryPolicyResult = await applyVerificationRetryPolicy(
|
|
263
|
+
ic,
|
|
264
|
+
iterData.unitType,
|
|
265
|
+
"verification-retry",
|
|
266
|
+
);
|
|
267
|
+
if (retryPolicyResult) {
|
|
268
|
+
clearFinalizingUnit();
|
|
269
|
+
return retryPolicyResult;
|
|
270
|
+
}
|
|
271
|
+
// Continue the loop — next iteration will inject the retry context into the prompt.
|
|
272
|
+
rememberRetryDispatch(s, preUnitSnapshot, iterData);
|
|
273
|
+
debugLog("autoLoop", { phase: "verification-retry", iteration: ic.iteration });
|
|
274
|
+
clearFinalizingUnit();
|
|
275
|
+
return { action: "continue" };
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Post-verification processing (DB dual-write, hooks, triage, quick-tasks)
|
|
281
|
+
// Timeout guard: if postUnitPostVerification hangs (e.g., module import
|
|
282
|
+
// deadlock, SQLite transaction hang), force-continue after timeout so the
|
|
283
|
+
// auto-loop is not permanently frozen (#2344).
|
|
284
|
+
const postResultGuard = await withTimeout(
|
|
285
|
+
deps.postUnitPostVerification(postUnitCtx),
|
|
286
|
+
FINALIZE_POST_TIMEOUT_MS,
|
|
287
|
+
"postUnitPostVerification",
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
if (postResultGuard.timedOut) {
|
|
291
|
+
return failClosedOnFinalizeTimeout(
|
|
292
|
+
ic,
|
|
293
|
+
iterData,
|
|
294
|
+
loopState,
|
|
295
|
+
"post",
|
|
296
|
+
preUnitSnapshot?.startedAt ?? Date.now(),
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const postResult = postResultGuard.value;
|
|
301
|
+
|
|
302
|
+
if (postResult === "retry") {
|
|
303
|
+
if (sidecarItem) {
|
|
304
|
+
debugLog("autoLoop", { phase: "sidecar-pre-execution-retry-skipped", iteration: ic.iteration });
|
|
305
|
+
} else {
|
|
306
|
+
const retryInfo = s.pendingVerificationRetry;
|
|
307
|
+
deps.emitJournalEvent({
|
|
308
|
+
ts: new Date().toISOString(),
|
|
309
|
+
flowId: ic.flowId,
|
|
310
|
+
seq: ic.nextSeq(),
|
|
311
|
+
eventType: "pre-execution-retry",
|
|
312
|
+
data: {
|
|
313
|
+
unitType: preUnitSnapshot?.type,
|
|
314
|
+
unitId: retryInfo?.unitId,
|
|
315
|
+
attempt: retryInfo?.attempt,
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
const retryPolicyResult = await applyVerificationRetryPolicy(
|
|
319
|
+
ic,
|
|
320
|
+
preUnitSnapshot?.type,
|
|
321
|
+
"pre-execution-retry",
|
|
322
|
+
);
|
|
323
|
+
if (retryPolicyResult) {
|
|
324
|
+
clearFinalizingUnit();
|
|
325
|
+
return retryPolicyResult;
|
|
326
|
+
}
|
|
327
|
+
rememberRetryDispatch(s, preUnitSnapshot, iterData);
|
|
328
|
+
debugLog("autoLoop", {
|
|
329
|
+
phase: "pre-execution-retry",
|
|
330
|
+
iteration: ic.iteration,
|
|
331
|
+
unitType: preUnitSnapshot?.type,
|
|
332
|
+
unitId: retryInfo?.unitId,
|
|
333
|
+
attempt: retryInfo?.attempt,
|
|
334
|
+
});
|
|
335
|
+
clearFinalizingUnit();
|
|
336
|
+
return { action: "continue" };
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (postResult === "stopped") {
|
|
341
|
+
debugLog("autoLoop", {
|
|
342
|
+
phase: "exit",
|
|
343
|
+
reason: "post-verification-stopped",
|
|
344
|
+
});
|
|
345
|
+
clearFinalizingUnit();
|
|
346
|
+
return { action: "break", reason: "post-verification-stopped" };
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (postResult === "step-wizard") {
|
|
350
|
+
// Step mode — exit the loop (caller handles wizard)
|
|
351
|
+
debugLog("autoLoop", { phase: "exit", reason: "step-wizard" });
|
|
352
|
+
clearFinalizingUnit();
|
|
353
|
+
return { action: "break", reason: "step-wizard" };
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (preUnitSnapshot && isIsolatedWorktreeSession(s)) {
|
|
357
|
+
const leak = detectRootWriteLeak({
|
|
358
|
+
rootPath: s.originalBasePath,
|
|
359
|
+
worktreePath: s.basePath,
|
|
360
|
+
unitType: preUnitSnapshot.type,
|
|
361
|
+
unitId: preUnitSnapshot.id,
|
|
362
|
+
before: s.rootWriteBaseline,
|
|
363
|
+
});
|
|
364
|
+
s.rootWriteBaseline = null;
|
|
365
|
+
if (leak) {
|
|
366
|
+
const message = formatRootWriteLeakMessage(leak);
|
|
367
|
+
debugLog("autoLoop", {
|
|
368
|
+
phase: "root-write-leak",
|
|
369
|
+
unitType: preUnitSnapshot.type,
|
|
370
|
+
unitId: preUnitSnapshot.id,
|
|
371
|
+
rootPath: leak.rootPath,
|
|
372
|
+
worktreePath: leak.worktreePath,
|
|
373
|
+
files: leak.files.map((file) => ({ path: file.path, status: file.status })),
|
|
374
|
+
});
|
|
375
|
+
ctx.ui.notify(message, "error");
|
|
376
|
+
await deps.stopAuto(ctx, pi, "Root-write leak during isolated auto-mode", {
|
|
377
|
+
preserveCompletedMilestoneBranch: true,
|
|
378
|
+
});
|
|
379
|
+
clearFinalizingUnit();
|
|
380
|
+
return { action: "break", reason: "root-write-leak" };
|
|
381
|
+
}
|
|
382
|
+
} else {
|
|
383
|
+
s.rootWriteBaseline = null;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (preUnitSnapshot?.type === "complete-milestone" && s.currentMilestoneId) {
|
|
387
|
+
const stop = await _runMilestoneMergeOnceWithStashRestore(ic, s.currentMilestoneId, {
|
|
388
|
+
preserveCloseoutTranscript: true,
|
|
389
|
+
});
|
|
390
|
+
if (stop) {
|
|
391
|
+
clearFinalizingUnit();
|
|
392
|
+
return stop;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Both pre and post verification completed without timeout — reset counter
|
|
397
|
+
loopState.consecutiveFinalizeTimeouts = 0;
|
|
398
|
+
if (preUnitSnapshot) {
|
|
399
|
+
writeUnitRuntimeRecord(s.basePath, preUnitSnapshot.type, preUnitSnapshot.id, preUnitSnapshot.startedAt, {
|
|
400
|
+
phase: "finalized",
|
|
401
|
+
lastProgressAt: Date.now(),
|
|
402
|
+
lastProgressKind: "finalize-success",
|
|
403
|
+
});
|
|
404
|
+
if (
|
|
405
|
+
!preUnitSnapshot.type.startsWith("hook/") &&
|
|
406
|
+
preUnitSnapshot.type !== "custom-step" &&
|
|
407
|
+
preUnitSnapshot.type !== "complete-milestone"
|
|
408
|
+
) {
|
|
409
|
+
setAutoOutcomeWidget(ctx, {
|
|
410
|
+
...buildPhaseHandoffOutcome({
|
|
411
|
+
unitType: preUnitSnapshot.type,
|
|
412
|
+
unitId: preUnitSnapshot.id,
|
|
413
|
+
agentEndMessages: s.lastUnitAgentEndMessages,
|
|
414
|
+
}),
|
|
415
|
+
startedAt: s.autoStartTime,
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
clearFinalizingUnit();
|
|
420
|
+
// Surface accumulated workflow-logger issues for this unit to the user.
|
|
421
|
+
// Warnings/errors logged during the unit are buffered in the logger and
|
|
422
|
+
// drained here so the user sees a single consolidated post-unit alert.
|
|
423
|
+
if (hasAnyIssues()) {
|
|
424
|
+
const { logs } = drainAndSummarize();
|
|
425
|
+
if (logs.length > 0) {
|
|
426
|
+
const severity = logs.some((e) => e.severity === "error") ? "error" : "warning";
|
|
427
|
+
ctx.ui.notify(formatForNotification(logs), severity);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (preUnitSnapshot?.type === "complete-milestone" && s.currentMilestoneId) {
|
|
432
|
+
// cleanupAfterLoopExit skips gsd-progress when preserveCompletionSurface is true, so clear stale controls here.
|
|
433
|
+
ctx.ui.setStatus?.("gsd-step", undefined);
|
|
434
|
+
ctx.ui.setWidget?.("gsd-progress", undefined);
|
|
435
|
+
await deps.stopAuto(ctx, pi, `Milestone ${s.currentMilestoneId} complete`, {
|
|
436
|
+
completionWidget: {
|
|
437
|
+
milestoneId: s.currentMilestoneId,
|
|
438
|
+
milestoneTitle: iterData.midTitle,
|
|
439
|
+
},
|
|
440
|
+
});
|
|
441
|
+
return { action: "break", reason: "milestone-complete" };
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return { action: "next", data: undefined as void };
|
|
445
|
+
}
|
|
@@ -25,7 +25,9 @@ import {
|
|
|
25
25
|
type IterationData,
|
|
26
26
|
} from "./types.js";
|
|
27
27
|
import { _clearCurrentResolve } from "./resolve.js";
|
|
28
|
-
import { runGuards
|
|
28
|
+
import { runGuards } from "./phases.js";
|
|
29
|
+
import { runFinalize } from "./finalize.js";
|
|
30
|
+
import { resetSessionTimeoutState } from "./unit-phase.js";
|
|
29
31
|
import { STUCK_WINDOW_SIZE } from "./dispatch-history.js";
|
|
30
32
|
import { debugLog } from "../debug-logger.js";
|
|
31
33
|
import { isInfrastructureError, isTransientCooldownError, getCooldownRetryAfterMs, COOLDOWN_FALLBACK_WAIT_MS, MAX_COOLDOWN_RETRIES } from "./infra-errors.js";
|
|
@@ -385,6 +387,7 @@ export async function autoLoop(
|
|
|
385
387
|
options?: AutoLoopOptions,
|
|
386
388
|
): Promise<void> {
|
|
387
389
|
debugLog("autoLoop", { phase: "enter" });
|
|
390
|
+
resetSessionTimeoutState();
|
|
388
391
|
let iteration = 0;
|
|
389
392
|
const dispatchContract = options?.dispatchContract ?? "legacy-direct";
|
|
390
393
|
const unitDispatchDeps = createExecutionGraphUnitDispatchDeps();
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Recover missing milestone lease state for resumed isolated workers.
|
|
3
|
+
|
|
4
|
+
import { debugLog } from "../debug-logger.js";
|
|
5
|
+
import { claimMilestoneLease } from "../db/milestone-leases.js";
|
|
6
|
+
import type { getIsolationMode } from "../preferences.js";
|
|
7
|
+
|
|
8
|
+
type IsolationMode = ReturnType<typeof getIsolationMode>;
|
|
9
|
+
|
|
10
|
+
export interface MilestoneLeaseSession {
|
|
11
|
+
workerId: string | null;
|
|
12
|
+
currentMilestoneId: string | null;
|
|
13
|
+
milestoneLeaseToken: number | null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function hasHeldMilestoneLease(
|
|
17
|
+
session: MilestoneLeaseSession,
|
|
18
|
+
milestoneId: string | null | undefined,
|
|
19
|
+
): boolean {
|
|
20
|
+
return (
|
|
21
|
+
Boolean(milestoneId) &&
|
|
22
|
+
session.currentMilestoneId === milestoneId &&
|
|
23
|
+
typeof session.milestoneLeaseToken === "number"
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function reclaimMissingMilestoneLease(
|
|
28
|
+
session: MilestoneLeaseSession,
|
|
29
|
+
milestoneId: string | null | undefined,
|
|
30
|
+
isolationMode: IsolationMode,
|
|
31
|
+
phase: string,
|
|
32
|
+
): void {
|
|
33
|
+
if (isolationMode === "none") return;
|
|
34
|
+
if (!session.workerId || !milestoneId) return;
|
|
35
|
+
if (hasHeldMilestoneLease(session, milestoneId)) return;
|
|
36
|
+
// Note: we intentionally do NOT bail just because the session already holds a
|
|
37
|
+
// lease for a *different* milestone. When dispatch advances to a new
|
|
38
|
+
// milestone the session's stale `currentMilestoneId`/token are for the prior
|
|
39
|
+
// one, and the active milestone's lease must still be claimed — otherwise
|
|
40
|
+
// worktree safety sees `held: false` and fails dispatch (#760). Claiming is
|
|
41
|
+
// safe: claimMilestoneLease refuses to steal a lease another worker holds.
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const claim = claimMilestoneLease(session.workerId, milestoneId);
|
|
45
|
+
if (claim.ok) {
|
|
46
|
+
session.currentMilestoneId = milestoneId;
|
|
47
|
+
session.milestoneLeaseToken = claim.token;
|
|
48
|
+
debugLog("worktreeSafety", {
|
|
49
|
+
phase: "lease-reclaimed",
|
|
50
|
+
source: phase,
|
|
51
|
+
milestoneId,
|
|
52
|
+
workerId: session.workerId,
|
|
53
|
+
token: claim.token,
|
|
54
|
+
});
|
|
55
|
+
} else {
|
|
56
|
+
debugLog("worktreeSafety", {
|
|
57
|
+
phase: "lease-reclaim-blocked",
|
|
58
|
+
source: phase,
|
|
59
|
+
milestoneId,
|
|
60
|
+
workerId: session.workerId,
|
|
61
|
+
holderWorkerId: claim.byWorker,
|
|
62
|
+
expiresAt: claim.expiresAt,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
} catch (err) {
|
|
66
|
+
debugLog("worktreeSafety", {
|
|
67
|
+
phase: "lease-reclaim-failed",
|
|
68
|
+
source: phase,
|
|
69
|
+
milestoneId,
|
|
70
|
+
workerId: session.workerId,
|
|
71
|
+
error: err instanceof Error ? err.message : String(err),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|