@ricsam/r5d-worker 0.0.59 → 0.0.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cjs/main.cjs +303 -381
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/workspace-convergence.cjs +42 -106
- package/dist/cjs/workspace-manifest-admission.cjs +4 -10
- package/dist/cjs/workspace-sync.cjs +2 -8
- package/dist/mjs/main.mjs +310 -396
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/workspace-convergence.mjs +37 -99
- package/dist/mjs/workspace-manifest-admission.mjs +3 -8
- package/dist/mjs/workspace-sync.mjs +2 -8
- package/dist/types/workspace-convergence.d.ts +15 -35
- package/dist/types/workspace-incident-state.d.ts +1 -1
- package/dist/types/workspace-manifest-admission.d.ts +6 -9
- package/dist/types/workspace-sync.d.ts +1 -1
- package/package.json +1 -1
package/dist/mjs/main.mjs
CHANGED
|
@@ -13,27 +13,19 @@ import {
|
|
|
13
13
|
} from "./heartbeat.mjs";
|
|
14
14
|
import { terminateProcessTree } from "./process-tree.mjs";
|
|
15
15
|
import { openWorkerPortForwardRelay } from "./port-forward-client.mjs";
|
|
16
|
-
import {
|
|
17
|
-
applyWorkspaceIncidentUpdate,
|
|
18
|
-
releasePendingWorkspaceHead,
|
|
19
|
-
WorkspaceIncidentOrderingFence
|
|
20
|
-
} from "./workspace-incident-state.mjs";
|
|
16
|
+
import { applyWorkspaceIncidentUpdate, releasePendingWorkspaceHead, WorkspaceIncidentOrderingFence } from "./workspace-incident-state.mjs";
|
|
21
17
|
import { acquireWorkspaceCommandMutation, runWorkspaceCommand } from "./workspace-command-sync-policy.mjs";
|
|
22
18
|
import {
|
|
23
|
-
|
|
19
|
+
WORKSPACE_PUBLICATION_QUIET_MS,
|
|
24
20
|
WORKSPACE_IDLE_SAFETY_SCAN_MS,
|
|
25
|
-
|
|
21
|
+
ExplicitWorkspacePublicationQueue,
|
|
26
22
|
WorkspaceManifestRevisionFence,
|
|
27
23
|
processWorkspaceConvergenceState,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
workspaceCheckpointTelemetry
|
|
24
|
+
waitForWorkspacePublicationOnShutdown,
|
|
25
|
+
workspacePublicationIncidentAction,
|
|
26
|
+
workspacePublicationTelemetry
|
|
32
27
|
} from "./workspace-convergence.mjs";
|
|
33
|
-
import {
|
|
34
|
-
acquireWorkspaceManifestVisibleLease,
|
|
35
|
-
inspectWorkspaceManifestAdmission
|
|
36
|
-
} from "./workspace-manifest-admission.mjs";
|
|
28
|
+
import { inspectWorkspaceManifestFilesystem } from "./workspace-manifest-admission.mjs";
|
|
37
29
|
import {
|
|
38
30
|
isRetryableWorkerServerStatus,
|
|
39
31
|
superviseWorkerRuntime,
|
|
@@ -64,7 +56,6 @@ const cancelledProcessRuns = /* @__PURE__ */ new Set();
|
|
|
64
56
|
const activePtys = /* @__PURE__ */ new Map();
|
|
65
57
|
let currentWorkerSocket = null;
|
|
66
58
|
const workspaceSyncSingleFlight = new WorkspaceSyncSingleFlight();
|
|
67
|
-
const workspaceFilesystemWriters = processWorkspaceFilesystemWriterGate();
|
|
68
59
|
let githubCredential = null;
|
|
69
60
|
let visibleGitIdentity = null;
|
|
70
61
|
function defaultConfigPath() {
|
|
@@ -786,25 +777,13 @@ function fallbackInternalRemoteUrl(baseUrl, projectId) {
|
|
|
786
777
|
}
|
|
787
778
|
function visibleCheckoutRemoteFor(input) {
|
|
788
779
|
const canonicalCheckout = input.manifest?.canonicalCheckouts?.find((checkout) => checkout.branchName === input.branchName);
|
|
789
|
-
const isCanonicalManifestBranch = Boolean(canonicalCheckout);
|
|
790
|
-
const usesCanonicalRemote = isCanonicalManifestBranch || !input.manifest?.repoHttpUrl;
|
|
791
|
-
if (usesCanonicalRemote) {
|
|
792
|
-
return {
|
|
793
|
-
remoteUrl: fallbackInternalRemoteUrl(input.baseUrl, input.projectId),
|
|
794
|
-
authHeader: `Authorization: Bearer ${input.token}`,
|
|
795
|
-
persistAuth: false,
|
|
796
|
-
reconcileExistingOrigin: isCanonicalManifestBranch,
|
|
797
|
-
requireRemoteBranch: isCanonicalManifestBranch,
|
|
798
|
-
requiredBaseCommit: canonicalCheckout?.scaffoldCommitHash ?? null
|
|
799
|
-
};
|
|
800
|
-
}
|
|
801
780
|
return {
|
|
802
|
-
remoteUrl:
|
|
803
|
-
authHeader: input.
|
|
804
|
-
persistAuth:
|
|
805
|
-
reconcileExistingOrigin:
|
|
806
|
-
requireRemoteBranch:
|
|
807
|
-
requiredBaseCommit: null
|
|
781
|
+
remoteUrl: fallbackInternalRemoteUrl(input.baseUrl, input.projectId),
|
|
782
|
+
authHeader: `Authorization: Bearer ${input.token}`,
|
|
783
|
+
persistAuth: false,
|
|
784
|
+
reconcileExistingOrigin: true,
|
|
785
|
+
requireRemoteBranch: true,
|
|
786
|
+
requiredBaseCommit: canonicalCheckout?.scaffoldCommitHash ?? null
|
|
808
787
|
};
|
|
809
788
|
}
|
|
810
789
|
function gitExtraHeaderUrlForRemote(remoteUrl) {
|
|
@@ -1025,9 +1004,11 @@ function checkoutVisibleBranch(input) {
|
|
|
1025
1004
|
}
|
|
1026
1005
|
function migrateExistingCheckoutToRequiredRemote(input) {
|
|
1027
1006
|
if (originRemoteUrl(input.branchPath) === input.remoteUrl) return;
|
|
1028
|
-
|
|
1007
|
+
const dirtyWorkingTree = hasWorktreeChanges(input.branchPath);
|
|
1008
|
+
if (dirtyWorkingTree && input.requiredBaseCommit) {
|
|
1029
1009
|
throw new Error(`Cannot migrate dirty checkout for ${input.branchName} to its canonical remote`);
|
|
1030
1010
|
}
|
|
1011
|
+
const preserveWorkingTree = dirtyWorkingTree;
|
|
1031
1012
|
runGit(
|
|
1032
1013
|
visibleCheckoutFetchArgs("--no-tags", input.remoteUrl, `+refs/heads/${input.branchName}:refs/remotes/origin/${input.branchName}`),
|
|
1033
1014
|
{
|
|
@@ -1042,11 +1023,12 @@ function migrateExistingCheckoutToRequiredRemote(input) {
|
|
|
1042
1023
|
if (input.requiredBaseCommit && (!tryGit(["cat-file", "-e", `${input.requiredBaseCommit}^{commit}`], { cwd: input.branchPath }) || !tryGit(["merge-base", "--is-ancestor", input.requiredBaseCommit, canonicalRemoteRef], { cwd: input.branchPath }))) {
|
|
1043
1024
|
throw new Error(`Canonical branch ${input.branchName} is not derived from required scaffold ${input.requiredBaseCommit}`);
|
|
1044
1025
|
}
|
|
1026
|
+
ensureOriginRemote(input.branchPath, input.remoteUrl);
|
|
1027
|
+
if (preserveWorkingTree) return;
|
|
1045
1028
|
if (tryGit(["rev-parse", "--verify", "HEAD"], { cwd: input.branchPath })) {
|
|
1046
1029
|
const previousHead = runGit(["rev-parse", "HEAD"], { cwd: input.branchPath });
|
|
1047
1030
|
runGit(["update-ref", `refs/r5d/pre-canonical-checkout/${previousHead}`, previousHead], { cwd: input.branchPath });
|
|
1048
1031
|
}
|
|
1049
|
-
ensureOriginRemote(input.branchPath, input.remoteUrl);
|
|
1050
1032
|
runGit(["checkout", "--no-recurse-submodules", "-B", input.branchName, `origin/${input.branchName}`], {
|
|
1051
1033
|
cwd: input.branchPath
|
|
1052
1034
|
});
|
|
@@ -1139,7 +1121,7 @@ function ensureVisibleGitCheckout(input) {
|
|
|
1139
1121
|
configureVisibleGitIdentity(branchPath, visibleGitIdentity, runGit);
|
|
1140
1122
|
return branchPath;
|
|
1141
1123
|
}
|
|
1142
|
-
function
|
|
1124
|
+
function ensureBranchWorktree(input) {
|
|
1143
1125
|
const defaultBranch = input.manifest?.defaultBranch || "main";
|
|
1144
1126
|
const remote = visibleCheckoutRemoteFor(input);
|
|
1145
1127
|
return {
|
|
@@ -1172,7 +1154,7 @@ function resolveWorkerSessionTarget(input) {
|
|
|
1172
1154
|
}
|
|
1173
1155
|
const manifest = input.manifestByProjectId.get(input.target.projectId);
|
|
1174
1156
|
const projectRoot = projectRootFor(input.projectsRoot, input.target.projectId, input.manifestByProjectId);
|
|
1175
|
-
const
|
|
1157
|
+
const worktree = ensureBranchWorktree({
|
|
1176
1158
|
projectId: input.target.projectId,
|
|
1177
1159
|
baseUrl: input.baseUrl,
|
|
1178
1160
|
token: input.token,
|
|
@@ -1181,7 +1163,7 @@ function resolveWorkerSessionTarget(input) {
|
|
|
1181
1163
|
branchName: input.target.branchName,
|
|
1182
1164
|
manifest
|
|
1183
1165
|
});
|
|
1184
|
-
return { target: input.target, rootPath:
|
|
1166
|
+
return { target: input.target, rootPath: worktree.branchPath, manifest };
|
|
1185
1167
|
}
|
|
1186
1168
|
function workspaceSyncCheckoutTargets(input) {
|
|
1187
1169
|
const selected = /* @__PURE__ */ new Map();
|
|
@@ -2386,15 +2368,15 @@ async function startWorker(options) {
|
|
|
2386
2368
|
const workspaceConvergence = processWorkspaceConvergenceState(path.join(syncRoot, "workspace-convergence.json"));
|
|
2387
2369
|
const workspaceManifestRevision = new WorkspaceManifestRevisionFence();
|
|
2388
2370
|
let workspaceReady = false;
|
|
2389
|
-
let
|
|
2371
|
+
let workspacePublicationTimer;
|
|
2390
2372
|
let workspaceSafetyScan;
|
|
2391
2373
|
let workspaceManifestHydrationTimer;
|
|
2392
2374
|
let workspaceManifestHydrationInFlight = false;
|
|
2393
2375
|
let workspaceSafetyScanInFlight = false;
|
|
2394
2376
|
let workspaceHeadConvergenceInFlight = false;
|
|
2395
2377
|
let pendingWorkspaceHead;
|
|
2396
|
-
let
|
|
2397
|
-
const
|
|
2378
|
+
let pendingPublication;
|
|
2379
|
+
const explicitWorkspacePublications = new ExplicitWorkspacePublicationQueue();
|
|
2398
2380
|
let terminalReplayTimer;
|
|
2399
2381
|
let workspaceSyncRequestsInFlight = 0;
|
|
2400
2382
|
let sessionArtifactSyncRequestsInFlight = 0;
|
|
@@ -2441,121 +2423,110 @@ async function startWorker(options) {
|
|
|
2441
2423
|
})
|
|
2442
2424
|
);
|
|
2443
2425
|
} catch (error) {
|
|
2444
|
-
process.stderr.write(
|
|
2445
|
-
|
|
2446
|
-
`
|
|
2447
|
-
);
|
|
2426
|
+
process.stderr.write(`[r5d-worker] failed to send workspace readiness: ${error instanceof Error ? error.message : String(error)}
|
|
2427
|
+
`);
|
|
2448
2428
|
}
|
|
2449
2429
|
};
|
|
2450
|
-
const workerHasActiveWriter = () => activeProcesses.size > 0 || activePtys.size > 0 || cliUpdateInProgress;
|
|
2451
2430
|
const targetMayMutateVisibleWorkspace = (target) => target.type === "project" || target.rootProfile === "visible_projects";
|
|
2452
|
-
const
|
|
2431
|
+
const armWorkspacePublication = (trigger, options2 = { delayMs: WORKSPACE_PUBLICATION_QUIET_MS }) => {
|
|
2453
2432
|
workspaceConvergence.schedule();
|
|
2454
2433
|
if (currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN) return;
|
|
2455
2434
|
sendWorkspaceReady();
|
|
2456
|
-
if (options2.requestId && !
|
|
2435
|
+
if (options2.requestId && !explicitWorkspacePublications.schedule({ requestId: options2.requestId, trigger }, pendingPublication?.requestId)) {
|
|
2457
2436
|
return;
|
|
2458
2437
|
}
|
|
2459
|
-
if (!options2.requestId &&
|
|
2460
|
-
if (
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2438
|
+
if (!options2.requestId && explicitWorkspacePublications.hasScheduled()) return;
|
|
2439
|
+
if (workspacePublicationTimer) clearTimeout(workspacePublicationTimer);
|
|
2440
|
+
workspacePublicationTimer = setTimeout(
|
|
2441
|
+
() => {
|
|
2442
|
+
workspacePublicationTimer = void 0;
|
|
2443
|
+
if (options2.requestId) explicitWorkspacePublications.consumeScheduled(options2.requestId);
|
|
2444
|
+
if (currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN) return;
|
|
2445
|
+
if (!workspaceReady || !workspaceRemoteUrl) {
|
|
2446
|
+
armWorkspacePublication(trigger, { delayMs: 1e3, ...options2.requestId ? { requestId: options2.requestId } : {} });
|
|
2447
|
+
return;
|
|
2448
|
+
}
|
|
2449
|
+
if (activeWorkspaceIncidentId) {
|
|
2450
|
+
workspaceConvergence.block();
|
|
2451
|
+
sendWorkspaceReady();
|
|
2452
|
+
if (workspacePublicationIncidentAction(options2.requestId) === "defer_automatic") return;
|
|
2453
|
+
if (pendingPublication) {
|
|
2454
|
+
explicitWorkspacePublications.enqueue({ requestId: options2.requestId, trigger }, pendingPublication.requestId);
|
|
2455
|
+
return;
|
|
2456
|
+
}
|
|
2457
|
+
const snapshot2 = workspaceConvergence.snapshot();
|
|
2458
|
+
pendingPublication = {
|
|
2459
|
+
requestId: options2.requestId,
|
|
2460
|
+
trigger,
|
|
2461
|
+
dirtyGeneration: snapshot2.dirtyGeneration,
|
|
2462
|
+
explicit: true,
|
|
2463
|
+
workInFlight: false,
|
|
2464
|
+
requestedAt: Date.now()
|
|
2465
|
+
};
|
|
2466
|
+
try {
|
|
2467
|
+
ws.send(
|
|
2468
|
+
JSON.stringify({
|
|
2469
|
+
type: "workspace_publication_request",
|
|
2470
|
+
requestId: options2.requestId,
|
|
2471
|
+
trigger,
|
|
2472
|
+
dirtyGeneration: snapshot2.dirtyGeneration,
|
|
2473
|
+
localHead: snapshot2.localHead,
|
|
2474
|
+
desiredCanonicalHead: snapshot2.desiredCanonicalHead
|
|
2475
|
+
})
|
|
2476
|
+
);
|
|
2477
|
+
} catch (error) {
|
|
2478
|
+
pendingPublication = void 0;
|
|
2479
|
+
workspaceConvergence.defer();
|
|
2480
|
+
process.stderr.write(
|
|
2481
|
+
`[r5d-worker] failed to request blocked workspace publication authority: ${error instanceof Error ? error.message : String(error)}
|
|
2482
|
+
`
|
|
2483
|
+
);
|
|
2484
|
+
armWorkspacePublication(trigger, { delayMs: 1e3, requestId: options2.requestId });
|
|
2485
|
+
}
|
|
2475
2486
|
return;
|
|
2476
2487
|
}
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2488
|
+
if (pendingPublication) {
|
|
2489
|
+
armWorkspacePublication(trigger, { delayMs: 1e3, ...options2.requestId ? { requestId: options2.requestId } : {} });
|
|
2490
|
+
return;
|
|
2491
|
+
}
|
|
2492
|
+
const snapshot = workspaceConvergence.snapshot();
|
|
2493
|
+
const requestId = options2.requestId ?? crypto.randomUUID();
|
|
2494
|
+
pendingPublication = {
|
|
2495
|
+
requestId,
|
|
2480
2496
|
trigger,
|
|
2481
|
-
dirtyGeneration:
|
|
2482
|
-
explicit:
|
|
2483
|
-
admissionHeld: false,
|
|
2497
|
+
dirtyGeneration: snapshot.dirtyGeneration,
|
|
2498
|
+
explicit: options2.requestId !== void 0,
|
|
2484
2499
|
workInFlight: false,
|
|
2485
|
-
requestedAt: Date.now()
|
|
2486
|
-
releaseCheckpointAdmission: () => {
|
|
2487
|
-
}
|
|
2500
|
+
requestedAt: Date.now()
|
|
2488
2501
|
};
|
|
2489
2502
|
try {
|
|
2490
2503
|
ws.send(
|
|
2491
2504
|
JSON.stringify({
|
|
2492
|
-
type: "
|
|
2493
|
-
requestId
|
|
2505
|
+
type: "workspace_publication_request",
|
|
2506
|
+
requestId,
|
|
2494
2507
|
trigger,
|
|
2495
|
-
dirtyGeneration:
|
|
2496
|
-
localHead:
|
|
2497
|
-
desiredCanonicalHead:
|
|
2508
|
+
dirtyGeneration: snapshot.dirtyGeneration,
|
|
2509
|
+
localHead: snapshot.localHead,
|
|
2510
|
+
desiredCanonicalHead: snapshot.desiredCanonicalHead
|
|
2498
2511
|
})
|
|
2499
2512
|
);
|
|
2500
2513
|
} catch (error) {
|
|
2501
|
-
|
|
2514
|
+
pendingPublication = void 0;
|
|
2502
2515
|
workspaceConvergence.defer();
|
|
2503
2516
|
process.stderr.write(
|
|
2504
|
-
`[r5d-worker] failed to request
|
|
2517
|
+
`[r5d-worker] failed to request workspace publication: ${error instanceof Error ? error.message : String(error)}
|
|
2505
2518
|
`
|
|
2506
2519
|
);
|
|
2507
|
-
|
|
2520
|
+
armWorkspacePublication(trigger, { delayMs: 1e3, ...options2.requestId ? { requestId: options2.requestId } : {} });
|
|
2508
2521
|
}
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
return;
|
|
2514
|
-
}
|
|
2515
|
-
const releaseCheckpointAdmission = workspaceFilesystemWriters.tryAcquireCheckpoint();
|
|
2516
|
-
if (!releaseCheckpointAdmission) {
|
|
2517
|
-
armWorkspaceCheckpoint(trigger, { delayMs: 1e3, ...options2.requestId ? { requestId: options2.requestId } : {} });
|
|
2518
|
-
return;
|
|
2519
|
-
}
|
|
2520
|
-
const snapshot = workspaceConvergence.snapshot();
|
|
2521
|
-
const requestId = options2.requestId ?? crypto.randomUUID();
|
|
2522
|
-
pendingCheckpoint = {
|
|
2523
|
-
requestId,
|
|
2524
|
-
trigger,
|
|
2525
|
-
dirtyGeneration: snapshot.dirtyGeneration,
|
|
2526
|
-
explicit: options2.requestId !== void 0,
|
|
2527
|
-
admissionHeld: true,
|
|
2528
|
-
workInFlight: false,
|
|
2529
|
-
requestedAt: Date.now(),
|
|
2530
|
-
releaseCheckpointAdmission
|
|
2531
|
-
};
|
|
2532
|
-
try {
|
|
2533
|
-
ws.send(
|
|
2534
|
-
JSON.stringify({
|
|
2535
|
-
type: "workspace_checkpoint_request",
|
|
2536
|
-
requestId,
|
|
2537
|
-
trigger,
|
|
2538
|
-
dirtyGeneration: snapshot.dirtyGeneration,
|
|
2539
|
-
localHead: snapshot.localHead,
|
|
2540
|
-
desiredCanonicalHead: snapshot.desiredCanonicalHead
|
|
2541
|
-
})
|
|
2542
|
-
);
|
|
2543
|
-
} catch (error) {
|
|
2544
|
-
releaseCheckpointAdmission();
|
|
2545
|
-
pendingCheckpoint = void 0;
|
|
2546
|
-
workspaceConvergence.defer();
|
|
2547
|
-
process.stderr.write(
|
|
2548
|
-
`[r5d-worker] failed to request workspace checkpoint: ${error instanceof Error ? error.message : String(error)}
|
|
2549
|
-
`
|
|
2550
|
-
);
|
|
2551
|
-
armWorkspaceCheckpoint(trigger, { delayMs: 1e3, ...options2.requestId ? { requestId: options2.requestId } : {} });
|
|
2552
|
-
}
|
|
2553
|
-
}, Math.max(0, options2.delayMs));
|
|
2554
|
-
workspaceCheckpointTimer.unref();
|
|
2522
|
+
},
|
|
2523
|
+
Math.max(0, options2.delayMs)
|
|
2524
|
+
);
|
|
2525
|
+
workspacePublicationTimer.unref();
|
|
2555
2526
|
};
|
|
2556
2527
|
const markWorkspaceDirty = (trigger, force = false) => {
|
|
2557
2528
|
workspaceConvergence.markDirty();
|
|
2558
|
-
|
|
2529
|
+
armWorkspacePublication(trigger, { delayMs: force ? 0 : WORKSPACE_PUBLICATION_QUIET_MS });
|
|
2559
2530
|
};
|
|
2560
2531
|
const convergeAvailableWorkspaceHead = async () => {
|
|
2561
2532
|
if (pendingWorkspaceHead === void 0 || !workspaceReady || !workspaceRemoteUrl || activeWorkspaceIncidentId || workspaceHeadConvergenceInFlight) {
|
|
@@ -2565,12 +2536,9 @@ async function startWorker(options) {
|
|
|
2565
2536
|
pendingWorkspaceHead = void 0;
|
|
2566
2537
|
workspaceConvergence.observeCanonicalHead(advertisedHead);
|
|
2567
2538
|
workspaceHeadConvergenceInFlight = true;
|
|
2568
|
-
let releaseVisibleHydration = null;
|
|
2569
2539
|
try {
|
|
2570
2540
|
const snapshot = workspaceConvergence.snapshot();
|
|
2571
|
-
const
|
|
2572
|
-
releaseVisibleHydration = mayHydrateVisible ? workspaceFilesystemWriters.tryAcquireCheckpoint() : null;
|
|
2573
|
-
const hydrateVisible = releaseVisibleHydration !== null;
|
|
2541
|
+
const hydrateVisible = !pendingPublication && snapshot.dirtyGeneration === snapshot.publishedGeneration;
|
|
2574
2542
|
const converged = await workspaceSyncSingleFlight.runExclusive(
|
|
2575
2543
|
() => convergeWorkspaceHead(workspaceSyncInput({ type: "inbound_head", detail: advertisedHead ?? "unborn" }), {
|
|
2576
2544
|
hydrateVisible
|
|
@@ -2579,7 +2547,7 @@ async function startWorker(options) {
|
|
|
2579
2547
|
workspaceConvergence.observeCanonicalHead(converged.canonicalHead);
|
|
2580
2548
|
if (converged.hydrated) workspaceConvergence.setLocalHead(converged.localHead);
|
|
2581
2549
|
if (hydrateVisible && converged.localChanges) {
|
|
2582
|
-
markWorkspaceDirty({ type: "periodic", detail: "inbound overlap retained for next
|
|
2550
|
+
markWorkspaceDirty({ type: "periodic", detail: "inbound overlap retained for next publication" });
|
|
2583
2551
|
}
|
|
2584
2552
|
sendWorkspaceReady();
|
|
2585
2553
|
} catch (error) {
|
|
@@ -2589,7 +2557,6 @@ async function startWorker(options) {
|
|
|
2589
2557
|
`
|
|
2590
2558
|
);
|
|
2591
2559
|
} finally {
|
|
2592
|
-
releaseVisibleHydration?.();
|
|
2593
2560
|
workspaceHeadConvergenceInFlight = false;
|
|
2594
2561
|
heartbeatBusyGrace = grantWorkerHeartbeatBusyGrace(lastServerHeartbeatAt, heartbeatBusyGrace);
|
|
2595
2562
|
if (pendingWorkspaceHead !== void 0) {
|
|
@@ -2610,7 +2577,7 @@ async function startWorker(options) {
|
|
|
2610
2577
|
workspaceShadowRoot,
|
|
2611
2578
|
manifestByProjectId
|
|
2612
2579
|
});
|
|
2613
|
-
const
|
|
2580
|
+
const ensureVisibleWorktrees = (targets, options2 = {}) => {
|
|
2614
2581
|
const created = [];
|
|
2615
2582
|
for (const target of targets) {
|
|
2616
2583
|
const manifest = manifestByProjectId.get(target.projectId);
|
|
@@ -2639,7 +2606,7 @@ async function startWorker(options) {
|
|
|
2639
2606
|
reconcileExistingOrigin: remote.reconcileExistingOrigin,
|
|
2640
2607
|
requireRemoteBranch: remote.requireRemoteBranch,
|
|
2641
2608
|
requiredBaseCommit: remote.requiredBaseCommit,
|
|
2642
|
-
allowRequiredRemoteMigration: !(options2.strictCanonicalRevalidation && remote.
|
|
2609
|
+
allowRequiredRemoteMigration: !(options2.strictCanonicalRevalidation && remote.requiredBaseCommit),
|
|
2643
2610
|
clearPersistentAuth: !remote.persistAuth
|
|
2644
2611
|
});
|
|
2645
2612
|
if (!existed) created.push(target);
|
|
@@ -2650,46 +2617,43 @@ async function startWorker(options) {
|
|
|
2650
2617
|
if (workspaceManifestHydrationTimer || workspaceManifestHydrationInFlight || pendingManifestCheckouts.size === 0 || currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN) {
|
|
2651
2618
|
return;
|
|
2652
2619
|
}
|
|
2653
|
-
workspaceManifestHydrationTimer = setTimeout(
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
if (pendingManifestCheckouts.size
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
}
|
|
2661
|
-
const releaseHydration = workspaceFilesystemWriters.tryAcquireCheckpoint();
|
|
2662
|
-
if (!releaseHydration) {
|
|
2663
|
-
schedulePendingManifestCheckoutHydration(1e3);
|
|
2664
|
-
return;
|
|
2665
|
-
}
|
|
2666
|
-
workspaceManifestHydrationInFlight = true;
|
|
2667
|
-
void workspaceSyncSingleFlight.runExclusive(async () => {
|
|
2668
|
-
if (!workspaceReady || activeWorkspaceIncidentId || currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN) {
|
|
2620
|
+
workspaceManifestHydrationTimer = setTimeout(
|
|
2621
|
+
() => {
|
|
2622
|
+
workspaceManifestHydrationTimer = void 0;
|
|
2623
|
+
if (!workspaceReady || !workspaceRemoteUrl || activeWorkspaceIncidentId || pendingPublication || pendingManifestCheckouts.size === 0 || currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN) {
|
|
2624
|
+
if (pendingManifestCheckouts.size > 0 && currentWorkerSocket === ws && ws.readyState === WebSocket.OPEN) {
|
|
2625
|
+
schedulePendingManifestCheckoutHydration(1e3);
|
|
2626
|
+
}
|
|
2669
2627
|
return;
|
|
2670
2628
|
}
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
if (!manifest || !manifest.branches.includes(target.branchName)) continue;
|
|
2676
|
-
const branchPath = path.join(projectRootFor(projectsRoot, target.projectId, manifestByProjectId), target.branchName);
|
|
2677
|
-
if (hasNormalVisibleGitDir(branchPath)) {
|
|
2678
|
-
pendingManifestCheckouts.delete(manifestCheckoutKey(target.projectId, target.branchName));
|
|
2629
|
+
workspaceManifestHydrationInFlight = true;
|
|
2630
|
+
void workspaceSyncSingleFlight.runExclusive(async () => {
|
|
2631
|
+
if (!workspaceReady || activeWorkspaceIncidentId || currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN) {
|
|
2632
|
+
return;
|
|
2679
2633
|
}
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2634
|
+
const targets = [...pendingManifestCheckouts.values()];
|
|
2635
|
+
ensureVisibleWorktrees(targets, { strictCanonicalRevalidation: true });
|
|
2636
|
+
for (const target of targets) {
|
|
2637
|
+
const manifest = manifestByProjectId.get(target.projectId);
|
|
2638
|
+
if (!manifest || !manifest.branches.includes(target.branchName)) continue;
|
|
2639
|
+
const branchPath = path.join(projectRootFor(projectsRoot, target.projectId, manifestByProjectId), target.branchName);
|
|
2640
|
+
if (hasNormalVisibleGitDir(branchPath)) {
|
|
2641
|
+
pendingManifestCheckouts.delete(manifestCheckoutKey(target.projectId, target.branchName));
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
}).then(() => sendWorkspaceReady()).catch((error) => {
|
|
2645
|
+
process.stderr.write(
|
|
2646
|
+
`[r5d-worker] pending manifest checkout hydration failed: ${error instanceof Error ? error.message : String(error)}
|
|
2684
2647
|
`
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
}
|
|
2692
|
-
|
|
2648
|
+
);
|
|
2649
|
+
}).finally(() => {
|
|
2650
|
+
workspaceManifestHydrationInFlight = false;
|
|
2651
|
+
heartbeatBusyGrace = grantWorkerHeartbeatBusyGrace(lastServerHeartbeatAt, heartbeatBusyGrace);
|
|
2652
|
+
if (pendingManifestCheckouts.size > 0) schedulePendingManifestCheckoutHydration(1e3);
|
|
2653
|
+
});
|
|
2654
|
+
},
|
|
2655
|
+
Math.max(0, delayMs)
|
|
2656
|
+
);
|
|
2693
2657
|
workspaceManifestHydrationTimer.unref();
|
|
2694
2658
|
};
|
|
2695
2659
|
const runRequestedWorkspaceSync = async (authority, trigger, overrides = {}) => {
|
|
@@ -2723,7 +2687,7 @@ async function startWorker(options) {
|
|
|
2723
2687
|
});
|
|
2724
2688
|
}
|
|
2725
2689
|
pendingTargets = shouldEnsureCheckouts ? scopedPendingTargets : [];
|
|
2726
|
-
const createdCheckouts =
|
|
2690
|
+
const createdCheckouts = ensureVisibleWorktrees(checkoutTargets, {
|
|
2727
2691
|
strictCanonicalRevalidation: true
|
|
2728
2692
|
});
|
|
2729
2693
|
const newVisibleCheckoutByKey = /* @__PURE__ */ new Map();
|
|
@@ -2805,11 +2769,11 @@ async function startWorker(options) {
|
|
|
2805
2769
|
const handleGracefulShutdown = () => {
|
|
2806
2770
|
if (shutdownAfterClose) return;
|
|
2807
2771
|
shutdownAfterClose = true;
|
|
2808
|
-
void
|
|
2772
|
+
void waitForWorkspacePublicationOnShutdown({
|
|
2809
2773
|
snapshot: () => workspaceConvergence.snapshot(),
|
|
2810
|
-
|
|
2774
|
+
forcePublication: () => armWorkspacePublication({ type: "manual", detail: "graceful worker shutdown" }, { delayMs: 0 })
|
|
2811
2775
|
}).then((outcome) => {
|
|
2812
|
-
process.stdout.write(`[r5d-worker] graceful workspace
|
|
2776
|
+
process.stdout.write(`[r5d-worker] graceful workspace publication ${outcome}
|
|
2813
2777
|
`);
|
|
2814
2778
|
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
2815
2779
|
ws.close(1e3, "Worker shutting down");
|
|
@@ -2880,105 +2844,84 @@ async function startWorker(options) {
|
|
|
2880
2844
|
if (message.type === "workspace_manifest") {
|
|
2881
2845
|
const revisionToken = workspaceManifestRevision.begin(message.revision);
|
|
2882
2846
|
workspaceReady = false;
|
|
2883
|
-
const
|
|
2847
|
+
const manifestFilesystem = inspectWorkspaceManifestFilesystem({
|
|
2884
2848
|
projectsRoot,
|
|
2885
2849
|
workspaceShadowRoot,
|
|
2886
2850
|
projects: message.projects
|
|
2887
2851
|
});
|
|
2888
|
-
const
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
);
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
hydrateVisible: firstBootstrap && !activeWorkspaceIncidentId
|
|
2930
|
-
}
|
|
2931
|
-
);
|
|
2932
|
-
if (!workspaceManifestRevision.isCurrent(revisionToken)) return false;
|
|
2933
|
-
workspaceConvergence.setLocalHead(bootstrapped.localHead);
|
|
2934
|
-
workspaceConvergence.observeCanonicalHead(bootstrapped.canonicalHead);
|
|
2935
|
-
for (const checkout of currentCheckouts) {
|
|
2936
|
-
const manifest = manifestByProjectId.get(checkout.projectId);
|
|
2937
|
-
if (!manifest) continue;
|
|
2938
|
-
const branchPath = path.join(projectRootFor(projectsRoot, checkout.projectId, manifestByProjectId), checkout.branchName);
|
|
2939
|
-
if (hasNormalVisibleGitDir(branchPath)) {
|
|
2940
|
-
pendingManifestCheckouts.delete(manifestCheckoutKey(checkout.projectId, checkout.branchName));
|
|
2941
|
-
}
|
|
2852
|
+
const completed = await workspaceSyncSingleFlight.runExclusive(async () => {
|
|
2853
|
+
if (!workspaceManifestRevision.isCurrent(revisionToken)) return false;
|
|
2854
|
+
const firstBootstrap = manifestFilesystem.firstBootstrap;
|
|
2855
|
+
configureGitHubAuth(message.githubCredential);
|
|
2856
|
+
visibleGitIdentity = message.gitIdentity;
|
|
2857
|
+
workspaceRemoteUrl = message.workspaceRemoteUrl;
|
|
2858
|
+
const migratedProjectIds = migrateLegacyProjectRoots(projectsRoot, message.projects);
|
|
2859
|
+
manifestByProjectId.clear();
|
|
2860
|
+
for (const project of message.projects) manifestByProjectId.set(project.projectId, project);
|
|
2861
|
+
const currentCheckouts = allManifestCheckouts();
|
|
2862
|
+
const currentCheckoutKeys = new Set(
|
|
2863
|
+
currentCheckouts.map(({ projectId, branchName }) => manifestCheckoutKey(projectId, branchName))
|
|
2864
|
+
);
|
|
2865
|
+
for (const key of pendingManifestCheckouts.keys()) {
|
|
2866
|
+
if (!currentCheckoutKeys.has(key)) pendingManifestCheckouts.delete(key);
|
|
2867
|
+
}
|
|
2868
|
+
for (const checkout of currentCheckouts) {
|
|
2869
|
+
const key = manifestCheckoutKey(checkout.projectId, checkout.branchName);
|
|
2870
|
+
const branchPath = path.join(projectRootFor(projectsRoot, checkout.projectId, manifestByProjectId), checkout.branchName);
|
|
2871
|
+
if (!hasNormalVisibleGitDir(branchPath)) pendingManifestCheckouts.set(key, checkout);
|
|
2872
|
+
}
|
|
2873
|
+
if (!workspaceManifestRevision.isCurrent(revisionToken)) return false;
|
|
2874
|
+
const created = firstBootstrap || migratedProjectIds.length > 0 ? ensureVisibleWorktrees(currentCheckouts, { strictCanonicalRevalidation: true }) : [];
|
|
2875
|
+
if (!workspaceManifestRevision.isCurrent(revisionToken)) return false;
|
|
2876
|
+
const bootstrapped = await convergeWorkspaceHead(workspaceSyncInput({ type: "connect" }, { newVisibleCheckouts: created }), {
|
|
2877
|
+
// An existing checkout becomes routable after a fetch. Its
|
|
2878
|
+
// visible tree converges asynchronously; first bootstrap alone
|
|
2879
|
+
// must materialize the canonical snapshot before readiness.
|
|
2880
|
+
// Re-check the incident immediately before convergence so a
|
|
2881
|
+
// concurrently delivered fence forces fetch-only behavior.
|
|
2882
|
+
hydrateVisible: firstBootstrap && !activeWorkspaceIncidentId
|
|
2883
|
+
});
|
|
2884
|
+
if (!workspaceManifestRevision.isCurrent(revisionToken)) return false;
|
|
2885
|
+
workspaceConvergence.setLocalHead(bootstrapped.localHead);
|
|
2886
|
+
workspaceConvergence.observeCanonicalHead(bootstrapped.canonicalHead);
|
|
2887
|
+
for (const checkout of currentCheckouts) {
|
|
2888
|
+
const manifest = manifestByProjectId.get(checkout.projectId);
|
|
2889
|
+
if (!manifest) continue;
|
|
2890
|
+
const branchPath = path.join(projectRootFor(projectsRoot, checkout.projectId, manifestByProjectId), checkout.branchName);
|
|
2891
|
+
if (hasNormalVisibleGitDir(branchPath)) {
|
|
2892
|
+
pendingManifestCheckouts.delete(manifestCheckoutKey(checkout.projectId, checkout.branchName));
|
|
2942
2893
|
}
|
|
2943
|
-
|
|
2944
|
-
|
|
2894
|
+
}
|
|
2895
|
+
process.stdout.write(
|
|
2896
|
+
`[r5d-worker] workspace manifest: ${message.projects.length} projects${migratedProjectIds.length > 0 ? `; migrated ${migratedProjectIds.length} project checkout root(s)` : ""}
|
|
2945
2897
|
`
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
} finally {
|
|
2950
|
-
releaseManifestHydration?.();
|
|
2951
|
-
}
|
|
2898
|
+
);
|
|
2899
|
+
return true;
|
|
2900
|
+
});
|
|
2952
2901
|
if (!completed || !workspaceManifestRevision.complete(revisionToken)) return;
|
|
2953
2902
|
workspaceReady = true;
|
|
2954
2903
|
sendWorkspaceReady();
|
|
2955
2904
|
schedulePendingManifestCheckoutHydration();
|
|
2956
2905
|
if (!workspaceSafetyScan) {
|
|
2957
2906
|
workspaceSafetyScan = setInterval(() => {
|
|
2958
|
-
if (!workspaceReady || !workspaceRemoteUrl || activeWorkspaceIncidentId ||
|
|
2907
|
+
if (!workspaceReady || !workspaceRemoteUrl || activeWorkspaceIncidentId || pendingPublication || workspaceSafetyScanInFlight) {
|
|
2959
2908
|
return;
|
|
2960
2909
|
}
|
|
2961
2910
|
workspaceSafetyScanInFlight = true;
|
|
2962
2911
|
void (async () => {
|
|
2963
2912
|
const before = workspaceConvergence.snapshot();
|
|
2964
|
-
const
|
|
2965
|
-
const
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
if (converged.hydrated) workspaceConvergence.setLocalHead(converged.localHead);
|
|
2975
|
-
if (converged.localChanges && hydrateVisible) {
|
|
2976
|
-
markWorkspaceDirty({ type: "periodic", detail: "idle safety scan" });
|
|
2977
|
-
}
|
|
2978
|
-
sendWorkspaceReady();
|
|
2979
|
-
} finally {
|
|
2980
|
-
releaseVisibleHydration?.();
|
|
2913
|
+
const hydrateVisible = before.dirtyGeneration === before.publishedGeneration;
|
|
2914
|
+
const converged = await workspaceSyncSingleFlight.runExclusive(
|
|
2915
|
+
() => convergeWorkspaceHead(workspaceSyncInput({ type: "periodic", detail: "idle convergence scan" }), {
|
|
2916
|
+
hydrateVisible
|
|
2917
|
+
})
|
|
2918
|
+
);
|
|
2919
|
+
workspaceConvergence.observeCanonicalHead(converged.canonicalHead);
|
|
2920
|
+
if (converged.hydrated) workspaceConvergence.setLocalHead(converged.localHead);
|
|
2921
|
+
if (converged.localChanges && hydrateVisible) {
|
|
2922
|
+
markWorkspaceDirty({ type: "periodic", detail: "idle safety scan" });
|
|
2981
2923
|
}
|
|
2924
|
+
sendWorkspaceReady();
|
|
2982
2925
|
})().catch((error) => {
|
|
2983
2926
|
process.stderr.write(
|
|
2984
2927
|
`[r5d-worker] idle workspace safety scan failed: ${error instanceof Error ? error.message : String(error)}
|
|
@@ -2994,7 +2937,7 @@ async function startWorker(options) {
|
|
|
2994
2937
|
pendingWorkspaceHead = workspaceConvergence.snapshot().desiredCanonicalHead;
|
|
2995
2938
|
void convergeAvailableWorkspaceHead();
|
|
2996
2939
|
if (workspaceConvergence.snapshot().dirtyGeneration > workspaceConvergence.snapshot().publishedGeneration) {
|
|
2997
|
-
|
|
2940
|
+
armWorkspacePublication({ type: "periodic", detail: "resume dirty generation after reconnect" }, { delayMs: 0 });
|
|
2998
2941
|
}
|
|
2999
2942
|
return;
|
|
3000
2943
|
}
|
|
@@ -3025,59 +2968,52 @@ async function startWorker(options) {
|
|
|
3025
2968
|
void convergeAvailableWorkspaceHead();
|
|
3026
2969
|
return;
|
|
3027
2970
|
}
|
|
3028
|
-
if (message.type === "
|
|
3029
|
-
|
|
2971
|
+
if (message.type === "workspace_publication_now") {
|
|
2972
|
+
armWorkspacePublication(message.trigger, { delayMs: 0, requestId: message.requestId });
|
|
3030
2973
|
return;
|
|
3031
2974
|
}
|
|
3032
|
-
if (message.type === "
|
|
3033
|
-
const
|
|
3034
|
-
if (!
|
|
3035
|
-
process.stderr.write(`[r5d-worker] ignoring
|
|
2975
|
+
if (message.type === "workspace_publication_authority") {
|
|
2976
|
+
const publication = pendingPublication;
|
|
2977
|
+
if (!publication || publication.requestId !== message.requestId) {
|
|
2978
|
+
process.stderr.write(`[r5d-worker] ignoring publication authority for unknown request ${message.requestId}
|
|
3036
2979
|
`);
|
|
3037
2980
|
return;
|
|
3038
2981
|
}
|
|
3039
2982
|
if (message.status !== "granted") {
|
|
3040
|
-
|
|
3041
|
-
pendingCheckpoint = void 0;
|
|
2983
|
+
pendingPublication = void 0;
|
|
3042
2984
|
workspaceConvergence.observeCanonicalHead(message.canonicalHead);
|
|
3043
2985
|
const applyBlockedResponse = message.status === "blocked" && workspaceIncidentOrdering.permitsBlockedResponse(message.incidentId);
|
|
3044
2986
|
if (applyBlockedResponse) {
|
|
3045
2987
|
workspaceConvergence.block();
|
|
3046
2988
|
if (message.incidentId) activeWorkspaceIncidentId = message.incidentId;
|
|
3047
2989
|
} else {
|
|
3048
|
-
workspaceConvergence.
|
|
2990
|
+
workspaceConvergence.resetTransientPublicationState();
|
|
3049
2991
|
workspaceConvergence.observeIncidentState(Boolean(activeWorkspaceIncidentId));
|
|
3050
2992
|
if (message.status === "busy") {
|
|
3051
|
-
|
|
2993
|
+
armWorkspacePublication(publication.trigger, {
|
|
3052
2994
|
delayMs: message.retryAfterMs ?? 1e3,
|
|
3053
|
-
requestId:
|
|
2995
|
+
requestId: publication.requestId
|
|
3054
2996
|
});
|
|
3055
2997
|
} else if (!activeWorkspaceIncidentId) {
|
|
3056
|
-
const explicit =
|
|
2998
|
+
const explicit = explicitWorkspacePublications.next();
|
|
3057
2999
|
if (explicit) {
|
|
3058
|
-
|
|
3000
|
+
armWorkspacePublication(explicit.trigger, { delayMs: 0, requestId: explicit.requestId });
|
|
3059
3001
|
} else if (workspaceConvergence.snapshot().dirtyGeneration > workspaceConvergence.snapshot().publishedGeneration) {
|
|
3060
|
-
|
|
3002
|
+
armWorkspacePublication(publication.trigger, { delayMs: 0 });
|
|
3061
3003
|
}
|
|
3062
3004
|
}
|
|
3063
3005
|
}
|
|
3064
3006
|
sendWorkspaceReady();
|
|
3065
3007
|
return;
|
|
3066
3008
|
}
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
if (
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
checkpoint.workInFlight = false;
|
|
3076
|
-
workspaceConvergence.resetTransientCheckpointState();
|
|
3077
|
-
return;
|
|
3078
|
-
}
|
|
3079
|
-
checkpoint.admissionHeld = true;
|
|
3080
|
-
checkpoint.releaseCheckpointAdmission = releaseCheckpointAdmission;
|
|
3009
|
+
publication.attemptId = message.attemptId;
|
|
3010
|
+
publication.authorityToken = message.authorityToken;
|
|
3011
|
+
publication.workInFlight = true;
|
|
3012
|
+
pendingPublication = publication;
|
|
3013
|
+
if (currentWorkerSocket !== ws) {
|
|
3014
|
+
publication.workInFlight = false;
|
|
3015
|
+
workspaceConvergence.resetTransientPublicationState();
|
|
3016
|
+
return;
|
|
3081
3017
|
}
|
|
3082
3018
|
workspaceConvergence.beginPreparing();
|
|
3083
3019
|
sendWorkspaceReady();
|
|
@@ -3089,11 +3025,11 @@ async function startWorker(options) {
|
|
|
3089
3025
|
result = await workspaceSyncSingleFlight.runExclusive(async () => {
|
|
3090
3026
|
prepareStartedAt = Date.now();
|
|
3091
3027
|
const pendingTargets = [...pendingManifestCheckouts.values()];
|
|
3092
|
-
const createdCheckouts =
|
|
3028
|
+
const createdCheckouts = ensureVisibleWorktrees(pendingTargets, { strictCanonicalRevalidation: true });
|
|
3093
3029
|
synchronizeStartedAt = Date.now();
|
|
3094
3030
|
synchronizeStarted = true;
|
|
3095
3031
|
const prepared = await synchronizeWorkspace(
|
|
3096
|
-
workspaceSyncInput(
|
|
3032
|
+
workspaceSyncInput(publication.trigger, {
|
|
3097
3033
|
attemptId: message.attemptId,
|
|
3098
3034
|
quarantineRef: message.quarantineRef,
|
|
3099
3035
|
expectedCanonicalHead: message.expectedHead,
|
|
@@ -3111,7 +3047,7 @@ async function startWorker(options) {
|
|
|
3111
3047
|
type: "workspace_sync",
|
|
3112
3048
|
attemptId: message.attemptId,
|
|
3113
3049
|
workerLabel: label,
|
|
3114
|
-
trigger:
|
|
3050
|
+
trigger: publication.trigger,
|
|
3115
3051
|
outcome: "failed",
|
|
3116
3052
|
startingHead: message.expectedHead,
|
|
3117
3053
|
expectedHead: message.expectedHead,
|
|
@@ -3128,9 +3064,9 @@ async function startWorker(options) {
|
|
|
3128
3064
|
const finishedAt = Date.now();
|
|
3129
3065
|
result = {
|
|
3130
3066
|
...result,
|
|
3131
|
-
dirtyGeneration:
|
|
3132
|
-
telemetry:
|
|
3133
|
-
requestedAt:
|
|
3067
|
+
dirtyGeneration: publication.dirtyGeneration,
|
|
3068
|
+
telemetry: workspacePublicationTelemetry({
|
|
3069
|
+
requestedAt: publication.requestedAt,
|
|
3134
3070
|
prepareStartedAt,
|
|
3135
3071
|
synchronizeStartedAt,
|
|
3136
3072
|
finishedAt
|
|
@@ -3141,36 +3077,35 @@ async function startWorker(options) {
|
|
|
3141
3077
|
try {
|
|
3142
3078
|
ws.send(
|
|
3143
3079
|
JSON.stringify({
|
|
3144
|
-
type: "
|
|
3145
|
-
requestId:
|
|
3080
|
+
type: "workspace_publication_result",
|
|
3081
|
+
requestId: publication.requestId,
|
|
3146
3082
|
attemptId: message.attemptId,
|
|
3147
3083
|
authorityToken: message.authorityToken,
|
|
3148
|
-
dirtyGeneration:
|
|
3084
|
+
dirtyGeneration: publication.dirtyGeneration,
|
|
3149
3085
|
result
|
|
3150
3086
|
})
|
|
3151
3087
|
);
|
|
3152
3088
|
} catch (error) {
|
|
3153
|
-
workspaceConvergence.
|
|
3089
|
+
workspaceConvergence.resetTransientPublicationState();
|
|
3154
3090
|
throw error;
|
|
3155
3091
|
} finally {
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
if (currentWorkerSocket !== ws) workspaceConvergence.resetTransientCheckpointState();
|
|
3092
|
+
publication.workInFlight = false;
|
|
3093
|
+
if (currentWorkerSocket !== ws) workspaceConvergence.resetTransientPublicationState();
|
|
3159
3094
|
}
|
|
3160
3095
|
return;
|
|
3161
3096
|
}
|
|
3162
|
-
if (message.type === "
|
|
3163
|
-
const
|
|
3164
|
-
if (!
|
|
3165
|
-
process.stderr.write(`[r5d-worker] ignoring completion for unknown
|
|
3097
|
+
if (message.type === "workspace_publication_completed") {
|
|
3098
|
+
const publication = pendingPublication;
|
|
3099
|
+
if (!publication || publication.requestId !== message.requestId || publication.attemptId !== message.attemptId) {
|
|
3100
|
+
process.stderr.write(`[r5d-worker] ignoring completion for unknown publication ${message.requestId}
|
|
3166
3101
|
`);
|
|
3167
3102
|
return;
|
|
3168
3103
|
}
|
|
3169
|
-
|
|
3104
|
+
pendingPublication = void 0;
|
|
3170
3105
|
workspaceConvergence.observeCanonicalHead(message.canonicalHead);
|
|
3171
3106
|
if (message.status === "published" || message.status === "no_change") {
|
|
3172
3107
|
workspaceConvergence.complete({
|
|
3173
|
-
generation: Math.min(message.publishedGeneration ??
|
|
3108
|
+
generation: Math.min(message.publishedGeneration ?? publication.dirtyGeneration, publication.dirtyGeneration),
|
|
3174
3109
|
canonicalHead: message.canonicalHead,
|
|
3175
3110
|
published: true
|
|
3176
3111
|
});
|
|
@@ -3178,18 +3113,18 @@ async function startWorker(options) {
|
|
|
3178
3113
|
workspaceConvergence.block();
|
|
3179
3114
|
if (message.incidentId) activeWorkspaceIncidentId = message.incidentId;
|
|
3180
3115
|
} else {
|
|
3181
|
-
workspaceConvergence.
|
|
3116
|
+
workspaceConvergence.resetTransientPublicationState();
|
|
3182
3117
|
workspaceConvergence.observeIncidentState(Boolean(activeWorkspaceIncidentId));
|
|
3183
3118
|
}
|
|
3184
3119
|
sendWorkspaceReady();
|
|
3185
|
-
const explicit = message.status === "retry" &&
|
|
3120
|
+
const explicit = message.status === "retry" && publication.explicit ? { requestId: publication.requestId, trigger: publication.trigger } : explicitWorkspacePublications.next();
|
|
3186
3121
|
if (explicit) {
|
|
3187
|
-
|
|
3122
|
+
armWorkspacePublication(explicit.trigger, {
|
|
3188
3123
|
delayMs: message.retryAfterMs ?? 0,
|
|
3189
3124
|
requestId: explicit.requestId
|
|
3190
3125
|
});
|
|
3191
3126
|
} else if (message.status === "retry" || message.status === "failed" || workspaceConvergence.snapshot().dirtyGeneration > workspaceConvergence.snapshot().publishedGeneration) {
|
|
3192
|
-
|
|
3127
|
+
armWorkspacePublication(publication.trigger, { delayMs: message.retryAfterMs ?? 1e3 });
|
|
3193
3128
|
}
|
|
3194
3129
|
if (message.canonicalHead !== workspaceConvergence.snapshot().localHead) {
|
|
3195
3130
|
pendingWorkspaceHead = message.canonicalHead;
|
|
@@ -3237,11 +3172,11 @@ async function startWorker(options) {
|
|
|
3237
3172
|
pendingWorkspaceHead = workspaceConvergence.snapshot().desiredCanonicalHead;
|
|
3238
3173
|
void convergeAvailableWorkspaceHead();
|
|
3239
3174
|
}
|
|
3240
|
-
const explicit = !activeWorkspaceIncidentId ?
|
|
3175
|
+
const explicit = !activeWorkspaceIncidentId ? explicitWorkspacePublications.next() : void 0;
|
|
3241
3176
|
if (explicit) {
|
|
3242
|
-
|
|
3177
|
+
armWorkspacePublication(explicit.trigger, { delayMs: 0, requestId: explicit.requestId });
|
|
3243
3178
|
} else if (!activeWorkspaceIncidentId && workspaceConvergence.snapshot().dirtyGeneration > workspaceConvergence.snapshot().publishedGeneration) {
|
|
3244
|
-
|
|
3179
|
+
armWorkspacePublication({ type: "periodic", detail: "resume after workspace incident" }, { delayMs: 0 });
|
|
3245
3180
|
}
|
|
3246
3181
|
if (!activeWorkspaceIncidentId) schedulePendingManifestCheckoutHydration();
|
|
3247
3182
|
sendWorkspaceReady();
|
|
@@ -3342,10 +3277,8 @@ async function startWorker(options) {
|
|
|
3342
3277
|
});
|
|
3343
3278
|
return;
|
|
3344
3279
|
}
|
|
3345
|
-
const releaseWorkspaceWriter = targetMayMutateVisibleWorkspace(message.target) ? await workspaceFilesystemWriters.acquireWriter() : void 0;
|
|
3346
3280
|
const releaseWorkspaceMutation = await acquireWorkspaceCommandMutation(message.target, workspaceSyncSingleFlight);
|
|
3347
3281
|
let mutationLeaseTransferred = false;
|
|
3348
|
-
let writerLeaseTransferred = false;
|
|
3349
3282
|
try {
|
|
3350
3283
|
const resolvedTarget = resolveMessageTarget(message.target);
|
|
3351
3284
|
process.stdout.write(`[r5d-worker] pty ${message.ptyId}: ${describeWorkerSessionTarget(message.target)}
|
|
@@ -3358,16 +3291,11 @@ async function startWorker(options) {
|
|
|
3358
3291
|
...releaseWorkspaceMutation ? { releaseWorkspaceMutation } : {},
|
|
3359
3292
|
...targetMayMutateVisibleWorkspace(message.target) ? {
|
|
3360
3293
|
onTerminal: () => {
|
|
3361
|
-
markWorkspaceDirty(
|
|
3362
|
-
{ type: "process_terminal", detail: `pty ${message.ptyId} completed` },
|
|
3363
|
-
true
|
|
3364
|
-
);
|
|
3365
|
-
releaseWorkspaceWriter?.();
|
|
3294
|
+
markWorkspaceDirty({ type: "process_terminal", detail: `pty ${message.ptyId} completed` }, true);
|
|
3366
3295
|
}
|
|
3367
3296
|
} : {}
|
|
3368
3297
|
});
|
|
3369
3298
|
mutationLeaseTransferred = releaseWorkspaceMutation !== void 0;
|
|
3370
|
-
writerLeaseTransferred = releaseWorkspaceWriter !== void 0;
|
|
3371
3299
|
} catch (error) {
|
|
3372
3300
|
sendWorkerMessage(ws, {
|
|
3373
3301
|
type: "pty_error",
|
|
@@ -3377,7 +3305,6 @@ async function startWorker(options) {
|
|
|
3377
3305
|
});
|
|
3378
3306
|
} finally {
|
|
3379
3307
|
if (!mutationLeaseTransferred) releaseWorkspaceMutation?.();
|
|
3380
|
-
if (!writerLeaseTransferred) releaseWorkspaceWriter?.();
|
|
3381
3308
|
}
|
|
3382
3309
|
return;
|
|
3383
3310
|
}
|
|
@@ -3397,7 +3324,7 @@ async function startWorker(options) {
|
|
|
3397
3324
|
const activePty = activePtys.get(message.ptyId);
|
|
3398
3325
|
closePty(message);
|
|
3399
3326
|
if (activePty && targetMayMutateVisibleWorkspace(activePty.target)) {
|
|
3400
|
-
|
|
3327
|
+
armWorkspacePublication({ type: "process_terminal", detail: `pty ${message.ptyId} closed` }, { delayMs: 0 });
|
|
3401
3328
|
}
|
|
3402
3329
|
return;
|
|
3403
3330
|
}
|
|
@@ -3412,36 +3339,31 @@ async function startWorker(options) {
|
|
|
3412
3339
|
});
|
|
3413
3340
|
return;
|
|
3414
3341
|
}
|
|
3415
|
-
|
|
3342
|
+
let result;
|
|
3416
3343
|
try {
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
const resolvedTarget = resolveMessageTarget(message.target);
|
|
3421
|
-
process.stdout.write(`[r5d-worker] exec ${message.runId}: ${message.argv.join(" ")}
|
|
3344
|
+
const runCommand = async () => {
|
|
3345
|
+
const resolvedTarget = resolveMessageTarget(message.target);
|
|
3346
|
+
process.stdout.write(`[r5d-worker] exec ${message.runId}: ${message.argv.join(" ")}
|
|
3422
3347
|
`);
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
}
|
|
3443
|
-
} finally {
|
|
3444
|
-
releaseWorkspaceWriter?.();
|
|
3348
|
+
return await executeCommand({ message, resolvedTarget, baseUrl, token, artifactRoot, planRoot });
|
|
3349
|
+
};
|
|
3350
|
+
result = await runWorkspaceCommand(message.target, workspaceSyncSingleFlight, runCommand);
|
|
3351
|
+
} catch (error) {
|
|
3352
|
+
result = {
|
|
3353
|
+
type: "exec_result",
|
|
3354
|
+
requestId: message.requestId,
|
|
3355
|
+
stdout: "",
|
|
3356
|
+
stderr: "",
|
|
3357
|
+
exitCode: 1,
|
|
3358
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3359
|
+
};
|
|
3360
|
+
}
|
|
3361
|
+
ws.send(JSON.stringify(result));
|
|
3362
|
+
if (targetMayMutateVisibleWorkspace(message.target)) {
|
|
3363
|
+
markWorkspaceDirty(
|
|
3364
|
+
{ type: "shell_inline", sessionId: message.sessionId, processRunId: message.runId, detail: "foreground command completed" },
|
|
3365
|
+
true
|
|
3366
|
+
);
|
|
3445
3367
|
}
|
|
3446
3368
|
return;
|
|
3447
3369
|
}
|
|
@@ -3461,7 +3383,6 @@ async function startWorker(options) {
|
|
|
3461
3383
|
runId: message.runId
|
|
3462
3384
|
});
|
|
3463
3385
|
const runCommand = async () => {
|
|
3464
|
-
const releaseWorkspaceWriter = targetMayMutateVisibleWorkspace(message.target) ? await workspaceFilesystemWriters.acquireWriter() : void 0;
|
|
3465
3386
|
try {
|
|
3466
3387
|
const resolvedTarget = resolveMessageTarget(message.target);
|
|
3467
3388
|
process.stdout.write(`[r5d-worker] exec_start ${message.runId}: ${message.argv.join(" ")}
|
|
@@ -3487,7 +3408,6 @@ async function startWorker(options) {
|
|
|
3487
3408
|
true
|
|
3488
3409
|
);
|
|
3489
3410
|
}
|
|
3490
|
-
releaseWorkspaceWriter?.();
|
|
3491
3411
|
}
|
|
3492
3412
|
};
|
|
3493
3413
|
const execution = runWorkspaceCommand(message.target, workspaceSyncSingleFlight, runCommand);
|
|
@@ -3502,46 +3422,41 @@ async function startWorker(options) {
|
|
|
3502
3422
|
return;
|
|
3503
3423
|
}
|
|
3504
3424
|
if (message.type === "read" || message.type === "write" || message.type === "edit" || message.type === "grep" || message.type === "find" || message.type === "ls" || message.type === "view_file_bytes") {
|
|
3505
|
-
const releaseWorkspaceWriter = (message.type === "write" || message.type === "edit") && targetMayMutateVisibleWorkspace(message.target) ? await workspaceFilesystemWriters.acquireWriter() : void 0;
|
|
3506
3425
|
try {
|
|
3507
|
-
|
|
3508
|
-
const
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3426
|
+
const result = await runWorkspaceCommand(message.target, workspaceSyncSingleFlight, async () => {
|
|
3427
|
+
const resolvedTarget = resolveMessageTarget(message.target);
|
|
3428
|
+
return await executeOperation({
|
|
3429
|
+
message,
|
|
3430
|
+
resolvedTarget,
|
|
3431
|
+
baseUrl,
|
|
3432
|
+
token,
|
|
3433
|
+
artifactRoot,
|
|
3434
|
+
planRoot
|
|
3435
|
+
});
|
|
3436
|
+
});
|
|
3437
|
+
ws.send(
|
|
3438
|
+
JSON.stringify({
|
|
3439
|
+
type: "operation_result",
|
|
3440
|
+
requestId: message.requestId,
|
|
3441
|
+
result
|
|
3442
|
+
})
|
|
3443
|
+
);
|
|
3444
|
+
if ((message.type === "write" || message.type === "edit") && targetMayMutateVisibleWorkspace(message.target)) {
|
|
3445
|
+
markWorkspaceDirty({
|
|
3446
|
+
type: message.type,
|
|
3447
|
+
sessionId: message.sessionId,
|
|
3448
|
+
toolCallId: message.requestId,
|
|
3449
|
+
...message.target.type === "project" ? { projectId: message.target.projectId, branchName: message.target.branchName } : {}
|
|
3518
3450
|
});
|
|
3519
|
-
ws.send(
|
|
3520
|
-
JSON.stringify({
|
|
3521
|
-
type: "operation_result",
|
|
3522
|
-
requestId: message.requestId,
|
|
3523
|
-
result
|
|
3524
|
-
})
|
|
3525
|
-
);
|
|
3526
|
-
if ((message.type === "write" || message.type === "edit") && targetMayMutateVisibleWorkspace(message.target)) {
|
|
3527
|
-
markWorkspaceDirty({
|
|
3528
|
-
type: message.type,
|
|
3529
|
-
sessionId: message.sessionId,
|
|
3530
|
-
toolCallId: message.requestId,
|
|
3531
|
-
...message.target.type === "project" ? { projectId: message.target.projectId, branchName: message.target.branchName } : {}
|
|
3532
|
-
});
|
|
3533
|
-
}
|
|
3534
|
-
} catch (error) {
|
|
3535
|
-
ws.send(
|
|
3536
|
-
JSON.stringify({
|
|
3537
|
-
type: "operation_result",
|
|
3538
|
-
requestId: message.requestId,
|
|
3539
|
-
error: error instanceof Error ? error.message : String(error)
|
|
3540
|
-
})
|
|
3541
|
-
);
|
|
3542
3451
|
}
|
|
3543
|
-
}
|
|
3544
|
-
|
|
3452
|
+
} catch (error) {
|
|
3453
|
+
ws.send(
|
|
3454
|
+
JSON.stringify({
|
|
3455
|
+
type: "operation_result",
|
|
3456
|
+
requestId: message.requestId,
|
|
3457
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3458
|
+
})
|
|
3459
|
+
);
|
|
3545
3460
|
}
|
|
3546
3461
|
return;
|
|
3547
3462
|
}
|
|
@@ -3566,13 +3481,12 @@ async function startWorker(options) {
|
|
|
3566
3481
|
process.off("SIGTERM", handleGracefulShutdown);
|
|
3567
3482
|
process.off("SIGINT", handleGracefulShutdown);
|
|
3568
3483
|
stopHeartbeatWatchdog();
|
|
3569
|
-
if (
|
|
3570
|
-
clearTimeout(
|
|
3571
|
-
|
|
3484
|
+
if (workspacePublicationTimer) {
|
|
3485
|
+
clearTimeout(workspacePublicationTimer);
|
|
3486
|
+
workspacePublicationTimer = void 0;
|
|
3572
3487
|
}
|
|
3573
|
-
if (!
|
|
3574
|
-
|
|
3575
|
-
workspaceConvergence.resetTransientCheckpointState();
|
|
3488
|
+
if (!pendingPublication?.workInFlight) {
|
|
3489
|
+
workspaceConvergence.resetTransientPublicationState();
|
|
3576
3490
|
}
|
|
3577
3491
|
if (workspaceSafetyScan) {
|
|
3578
3492
|
clearInterval(workspaceSafetyScan);
|