@akagilnc/pi-workflow-roles 0.1.4745 → 0.1.4772
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/acp-host/production-host.js +278 -590
- package/dist/headless-host/production-host.js +284 -596
- package/dist/migrate-book-topology.js +1255 -1087
- package/dist/notary-source-run.js +12 -2
- package/dist/public-cli/invocation.js +11 -8
- package/dist/public-cli/main.js +250 -563
- package/dist/public-cli/post-admission.js +96 -131
- package/dist/public-cli/run-lifecycle.js +25 -4
- package/dist/role-run-relocation.js +19 -18
- package/package.json +1 -1
- package/src/book-topology-migration-placement.ts +9 -1
- package/src/book-topology-runs-migrator.ts +156 -4
- package/src/notary-source-run.ts +18 -2
- package/src/public-cli/cli.ts +0 -5
- package/src/public-cli/config.ts +0 -1
- package/src/public-cli/countersign-run.ts +1 -1
- package/src/public-cli/diarist-run.ts +1 -1
- package/src/public-cli/invocation.ts +13 -9
- package/src/public-cli/post-admission.ts +124 -187
- package/src/public-cli/run-lifecycle.ts +42 -4
- package/src/role-run-relocation.ts +27 -30
- package/dist/public-cli/public-run-credentials.js +0 -49
- package/src/public-cli/public-run-credentials.ts +0 -71
|
@@ -22,11 +22,13 @@ import {
|
|
|
22
22
|
bindAdmittedTicketNumber,
|
|
23
23
|
buildInstructionTransportPrompt,
|
|
24
24
|
freezeAttachmentsIntoRun,
|
|
25
|
+
relocateAdmittedRunToTicket,
|
|
25
26
|
} from "./invocation.ts";
|
|
26
27
|
import { readRecordedSubmissionRows } from "../submission-ledger.ts";
|
|
27
28
|
import { pathContainedIn } from "../activation-ledger-topology.ts";
|
|
28
29
|
import { pickEngineAxis } from "../package-resources/engine-material.ts";
|
|
29
30
|
import { resolveHostAwareSessionAvailability } from "../session-identity.ts";
|
|
31
|
+
import { rewriteRunDirectoryPathValue } from "../role-run-relocation.ts";
|
|
30
32
|
|
|
31
33
|
import type {
|
|
32
34
|
ControlledFailureCause,
|
|
@@ -69,11 +71,6 @@ import {
|
|
|
69
71
|
import { projectHostTransitionPriorNative } from "../host-transition-prior-native.ts";
|
|
70
72
|
import type { CredentialProviders, SeatModelConfig } from "./config.ts";
|
|
71
73
|
import {
|
|
72
|
-
missingCredentialPreDispatchFailure,
|
|
73
|
-
postRunMissingCredentialFailure,
|
|
74
|
-
} from "./public-run-credentials.ts";
|
|
75
|
-
import {
|
|
76
|
-
acquireRunWriterLease,
|
|
77
74
|
clearCurrentCourt,
|
|
78
75
|
clearTypedProviderHttpObservation,
|
|
79
76
|
describeErrorIdentity,
|
|
@@ -81,11 +78,9 @@ import {
|
|
|
81
78
|
readCurrentCourt,
|
|
82
79
|
recordCurrentCourt,
|
|
83
80
|
renderResumeCommand,
|
|
84
|
-
RunWriterLeaseHeldError,
|
|
85
81
|
type CurrentCourtState,
|
|
86
82
|
type RunWriterLease,
|
|
87
83
|
type TypedProviderHttpObservation,
|
|
88
|
-
type WriterLeaseDiagnosticKind,
|
|
89
84
|
} from "./run-lifecycle.ts";
|
|
90
85
|
import {
|
|
91
86
|
processCancelDiagnostic,
|
|
@@ -124,6 +119,34 @@ import {
|
|
|
124
119
|
TurnDispatchedFailure,
|
|
125
120
|
} from "./auto-resume.ts";
|
|
126
121
|
|
|
122
|
+
function projectRelocatedTurnIdentity(
|
|
123
|
+
request: RoleTurnRequest,
|
|
124
|
+
result: { readonly terminal?: TerminalResult },
|
|
125
|
+
admitted: AdmittedRoleInvocation,
|
|
126
|
+
relocation: { readonly oldRunDirectory: string; readonly newRunDirectory: string },
|
|
127
|
+
): void {
|
|
128
|
+
const rewrite = (value: unknown) => rewriteRunDirectoryPathValue(
|
|
129
|
+
value,
|
|
130
|
+
relocation.oldRunDirectory,
|
|
131
|
+
relocation.newRunDirectory,
|
|
132
|
+
);
|
|
133
|
+
const mutableRequest = request as unknown as Record<string, unknown>;
|
|
134
|
+
mutableRequest.runDirectory = admitted.runDirectory;
|
|
135
|
+
mutableRequest.principal = admitted.principal;
|
|
136
|
+
const activation = mutableRequest.activation;
|
|
137
|
+
if (activation !== null && typeof activation === "object" && !Array.isArray(activation)) {
|
|
138
|
+
for (const field of ["taskPath", "packetPath", "prerequisitesPath", "inputPath", "requestManifestPath"] as const) {
|
|
139
|
+
const record = activation as Record<string, unknown>;
|
|
140
|
+
if (field in record) record[field] = rewrite(record[field]);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (result.terminal !== undefined) {
|
|
144
|
+
for (const artifact of result.terminal.artifacts) {
|
|
145
|
+
artifact.path = rewrite(artifact.path) as string;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
127
150
|
/** #855: process-cancel settlement never re-enters auto-resume. */
|
|
128
151
|
function withProcessCancelSkipAutoResume<T extends object>(
|
|
129
152
|
result: T,
|
|
@@ -234,8 +257,8 @@ async function recordBestEffortPostDispatchDiagnostic<A extends AdmittedRoleInvo
|
|
|
234
257
|
diagnostic: string,
|
|
235
258
|
io: CliIo,
|
|
236
259
|
): Promise<void> {
|
|
237
|
-
io.stderr(formatCliDiagnostic(diagnostic));
|
|
238
260
|
const payload = { diagnostic, recordedAt: new Date().toISOString() };
|
|
261
|
+
let retentionFailure: string | undefined;
|
|
239
262
|
try {
|
|
240
263
|
await env.sessionAppender(
|
|
241
264
|
env.principalAuthority,
|
|
@@ -243,7 +266,6 @@ async function recordBestEffortPostDispatchDiagnostic<A extends AdmittedRoleInvo
|
|
|
243
266
|
POST_ADMISSION_CLEANUP_DIAGNOSTIC_ENTRY_TYPE,
|
|
244
267
|
payload,
|
|
245
268
|
);
|
|
246
|
-
return;
|
|
247
269
|
} catch (appendError) {
|
|
248
270
|
try {
|
|
249
271
|
const artifactsDir = await ensureRealArtifactsDirectory(admitted.runDirectory);
|
|
@@ -253,13 +275,18 @@ async function recordBestEffortPostDispatchDiagnostic<A extends AdmittedRoleInvo
|
|
|
253
275
|
{ encoding: "utf8", flag: "wx" },
|
|
254
276
|
);
|
|
255
277
|
} catch (artifactError) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
`post-dispatch diagnostic durable retention failed on both channels (best-effort continue): dossier=${describeErrorIdentity(appendError)}; artifact=${describeErrorIdentity(artifactError)}`,
|
|
259
|
-
),
|
|
260
|
-
);
|
|
278
|
+
retentionFailure =
|
|
279
|
+
`post-dispatch diagnostic durable retention failed on both channels (best-effort continue): dossier=${describeErrorIdentity(appendError)}; artifact=${describeErrorIdentity(artifactError)}`;
|
|
261
280
|
}
|
|
262
281
|
}
|
|
282
|
+
try {
|
|
283
|
+
io.stderr(formatCliDiagnostic(diagnostic));
|
|
284
|
+
if (retentionFailure !== undefined) {
|
|
285
|
+
io.stderr(formatCliDiagnostic(retentionFailure));
|
|
286
|
+
}
|
|
287
|
+
} catch {
|
|
288
|
+
// Presentation is best-effort beside an already-formed host terminal.
|
|
289
|
+
}
|
|
263
290
|
}
|
|
264
291
|
|
|
265
292
|
export type PostAdmissionEnv = {
|
|
@@ -327,13 +354,14 @@ export type PostAdmissionAdapters<
|
|
|
327
354
|
result: RoleTurnResult;
|
|
328
355
|
sessionFile: string;
|
|
329
356
|
}) => Promise<RoleTurnKnownFailure | undefined>;
|
|
330
|
-
beforeDispatch?: (admitted: A, lease
|
|
357
|
+
beforeDispatch?: (admitted: A, lease?: RunWriterLease) => Promise<void> | void;
|
|
331
358
|
/**
|
|
332
|
-
* After the host turn settles and before
|
|
333
|
-
* Post-turn bind/relocate (e.g. Diarist first assert) must run here so
|
|
334
|
-
* held lease follows the run directory and failures stay
|
|
359
|
+
* After the host turn settles and before any held writer lease is released.
|
|
360
|
+
* Post-turn bind/relocate (e.g. Diarist first assert) must run here so a
|
|
361
|
+
* held lease (when present) follows the run directory and failures stay
|
|
362
|
+
* controlled. Public manual resume (#987) may omit the lease.
|
|
335
363
|
*/
|
|
336
|
-
afterDispatch?: (admitted: A, lease
|
|
364
|
+
afterDispatch?: (admitted: A, lease?: RunWriterLease) => Promise<void> | void;
|
|
337
365
|
};
|
|
338
366
|
|
|
339
367
|
export type ControlledFailureInput = {
|
|
@@ -614,7 +642,8 @@ export async function dispatchPostAdmissionTurn<
|
|
|
614
642
|
env: PostAdmissionEnv;
|
|
615
643
|
io: CliIo;
|
|
616
644
|
request: RoleTurnRequest;
|
|
617
|
-
|
|
645
|
+
/** Absent on public manual resume (#987): no package lease gate before host CLI resume. */
|
|
646
|
+
lease?: RunWriterLease;
|
|
618
647
|
adapters: PostAdmissionAdapters<A, T>;
|
|
619
648
|
effectiveEngine?: string;
|
|
620
649
|
persistRunState?: boolean;
|
|
@@ -704,17 +733,20 @@ export async function dispatchPostAdmissionTurn<
|
|
|
704
733
|
: (asserted as { ticketNumber: number }).ticketNumber;
|
|
705
734
|
if (ticketNumber !== undefined) await bindAdmittedTicketNumber(admitted, ticketNumber);
|
|
706
735
|
}
|
|
736
|
+
// #863: shared post-admission bind must relocate unbound→ticket in-home
|
|
737
|
+
// before lease release (work-seat self-report and any prior board bind).
|
|
738
|
+
const relocation = await relocateAdmittedRunToTicket(admitted, env.principalAuthority, lease);
|
|
739
|
+
if (relocation !== undefined) {
|
|
740
|
+
projectRelocatedTurnIdentity(request, result, admitted, relocation);
|
|
741
|
+
}
|
|
707
742
|
if (adapters.afterDispatch !== undefined) await adapters.afterDispatch(admitted, lease);
|
|
708
743
|
return result;
|
|
709
744
|
} catch (error) {
|
|
710
|
-
|
|
711
|
-
result.terminal !== undefined
|
|
712
|
-
&& !isLawfulTypedTerminalOutcome(result.terminal.roleOutcome);
|
|
713
|
-
if (primaryFailure) {
|
|
745
|
+
if (result.terminal !== undefined) {
|
|
714
746
|
await recordBestEffortPostDispatchDiagnostic(
|
|
715
747
|
admitted,
|
|
716
748
|
env,
|
|
717
|
-
`afterDispatch failed beside
|
|
749
|
+
`afterDispatch failed beside host terminal (best-effort continue): ${describeErrorIdentity(error)}`,
|
|
718
750
|
io,
|
|
719
751
|
);
|
|
720
752
|
return result;
|
|
@@ -740,23 +772,8 @@ export async function dispatchPostAdmissionTurn<
|
|
|
740
772
|
}
|
|
741
773
|
};
|
|
742
774
|
try {
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
env.credentials,
|
|
746
|
-
);
|
|
747
|
-
if (missingCredential !== undefined) {
|
|
748
|
-
return {
|
|
749
|
-
...(await presentControlledFailure(
|
|
750
|
-
admitted,
|
|
751
|
-
withEngineDetourInvocationScope(missingCredential, request.invocationScopeId),
|
|
752
|
-
adapters,
|
|
753
|
-
env.principalAuthority,
|
|
754
|
-
io,
|
|
755
|
-
persistRunState,
|
|
756
|
-
)) as { exitCode: number; admitted: A; terminal: T },
|
|
757
|
-
...deferredPersist,
|
|
758
|
-
};
|
|
759
|
-
}
|
|
775
|
+
// #987: do not pre-block or classify host dispatch from AK credential
|
|
776
|
+
// catalog facts. Per-invocation runner/host evidence owns provider identity.
|
|
760
777
|
// #617 DK-4: capture previous invocation host before markRunRunning overwrites it.
|
|
761
778
|
// Single authority projectHostTransitionPriorNative classifies the prior native volume.
|
|
762
779
|
// Same-run resume (#637) keeps host identity on the run's invocation page.
|
|
@@ -1001,15 +1018,10 @@ export async function dispatchPostAdmissionTurn<
|
|
|
1001
1018
|
adapters.resolveRunnerKnownFailure !== undefined && sessionFile !== ""
|
|
1002
1019
|
? await adapters.resolveRunnerKnownFailure({ result, sessionFile })
|
|
1003
1020
|
: result.knownFailure;
|
|
1004
|
-
const credentialFailure = postRunMissingCredentialFailure(
|
|
1005
|
-
result,
|
|
1006
|
-
env.model,
|
|
1007
|
-
env.credentials,
|
|
1008
|
-
);
|
|
1009
1021
|
resolution = await resolveAuditedRunnerFailureResolution({
|
|
1010
1022
|
runner: runnerKnownFailure,
|
|
1011
1023
|
sessionFile,
|
|
1012
|
-
credential:
|
|
1024
|
+
credential: undefined,
|
|
1013
1025
|
runDirectory: admitted.runDirectory,
|
|
1014
1026
|
});
|
|
1015
1027
|
// A direct, current signal from the host/runner itself (timeout / host
|
|
@@ -1030,7 +1042,6 @@ export async function dispatchPostAdmissionTurn<
|
|
|
1030
1042
|
result.timedOut
|
|
1031
1043
|
|| result.knownFailure !== undefined
|
|
1032
1044
|
|| runnerKnownFailure !== undefined
|
|
1033
|
-
|| credentialFailure !== undefined
|
|
1034
1045
|
|| processCancelName !== undefined;
|
|
1035
1046
|
hostSignalFailed =
|
|
1036
1047
|
directHostFailureSignal
|
|
@@ -1153,35 +1164,11 @@ export async function dispatchPostAdmissionTurn<
|
|
|
1153
1164
|
try {
|
|
1154
1165
|
await persistReturnedRunState(admitted, env.principalAuthority, { lawful: true });
|
|
1155
1166
|
} catch (error) {
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
// this function uses (ADR 0080: one settlement disposition owner)
|
|
1161
|
-
// — not escape uncaught to auto-resume's dispatch-retry path,
|
|
1162
|
-
// whose exhausted-budget terminal carries no recorded submissions
|
|
1163
|
-
// at all. skipRunStateWrite: the write that just threw is the same
|
|
1164
|
-
// write presentControlledFailure would otherwise retry — don't
|
|
1165
|
-
// call a known-failing operation twice.
|
|
1166
|
-
const failed = await settleAfterTurnStarted(
|
|
1167
|
-
admitted,
|
|
1168
|
-
withEngineDetourInvocationScope({
|
|
1169
|
-
timedOut: false,
|
|
1170
|
-
code: null,
|
|
1171
|
-
stderr: "",
|
|
1172
|
-
thrown: error,
|
|
1173
|
-
skipRunStateWrite: true,
|
|
1174
|
-
}, request.invocationScopeId),
|
|
1175
|
-
adapters,
|
|
1176
|
-
env.principalAuthority,
|
|
1167
|
+
await recordBestEffortPostDispatchDiagnostic(
|
|
1168
|
+
admitted,
|
|
1169
|
+
env,
|
|
1170
|
+
`run-state persistence failed beside host terminal (best-effort continue): ${describeErrorIdentity(error)}`,
|
|
1177
1171
|
io,
|
|
1178
|
-
persistRunState,
|
|
1179
|
-
);
|
|
1180
|
-
return await finishAfterTurn(
|
|
1181
|
-
withProcessCancelSkipAutoResume(
|
|
1182
|
-
{ ...failed, turnDispatched: true as const, ...deferredPersist },
|
|
1183
|
-
env.signal,
|
|
1184
|
-
),
|
|
1185
1172
|
);
|
|
1186
1173
|
}
|
|
1187
1174
|
}
|
|
@@ -1282,30 +1269,11 @@ export async function dispatchPostAdmissionTurn<
|
|
|
1282
1269
|
try {
|
|
1283
1270
|
await persistReturnedRunState(admitted, env.principalAuthority, { lawful: true });
|
|
1284
1271
|
} catch (error) {
|
|
1285
|
-
|
|
1286
|
-
// branch above — route through the shared controlled-failure seam so
|
|
1287
|
-
// the real failure surfaces loudly instead of escaping uncaught to
|
|
1288
|
-
// auto-resume's dispatch-retry path with no recorded submissions.
|
|
1289
|
-
// skipRunStateWrite: don't retry the write that just threw.
|
|
1290
|
-
const failed = await settleAfterTurnStarted(
|
|
1272
|
+
await recordBestEffortPostDispatchDiagnostic(
|
|
1291
1273
|
admitted,
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
code: null,
|
|
1295
|
-
stderr: "",
|
|
1296
|
-
thrown: error,
|
|
1297
|
-
skipRunStateWrite: true,
|
|
1298
|
-
}, request.invocationScopeId),
|
|
1299
|
-
adapters,
|
|
1300
|
-
env.principalAuthority,
|
|
1274
|
+
env,
|
|
1275
|
+
`run-state persistence failed beside host terminal (best-effort continue): ${describeErrorIdentity(error)}`,
|
|
1301
1276
|
io,
|
|
1302
|
-
persistRunState,
|
|
1303
|
-
);
|
|
1304
|
-
return await finishAfterTurn(
|
|
1305
|
-
withProcessCancelSkipAutoResume(
|
|
1306
|
-
{ ...failed, turnDispatched: true as const, ...deferredPersist },
|
|
1307
|
-
env.signal,
|
|
1308
|
-
),
|
|
1309
1277
|
);
|
|
1310
1278
|
}
|
|
1311
1279
|
}
|
|
@@ -1318,20 +1286,23 @@ export async function dispatchPostAdmissionTurn<
|
|
|
1318
1286
|
...deferredPersist,
|
|
1319
1287
|
});
|
|
1320
1288
|
} finally {
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1289
|
+
// Public manual resume (#987) holds no package writer lease.
|
|
1290
|
+
if (lease !== undefined) {
|
|
1291
|
+
try {
|
|
1292
|
+
await lease.release();
|
|
1293
|
+
} catch (error) {
|
|
1294
|
+
// lease.release() is documented best-effort and never rejects in the
|
|
1295
|
+
// production acquireRunWriterLease implementation (createWriterLease
|
|
1296
|
+
// reports cleanup failures via callback, never throws) — this guard is
|
|
1297
|
+
// structural only: an uncaught throw from a finally block silently
|
|
1298
|
+
// replaces whatever the try already returned, including a properly
|
|
1299
|
+
// tagged turnDispatched:true result (#840 r9 判词 class 1 boundary).
|
|
1300
|
+
io.stderr(
|
|
1301
|
+
formatCliDiagnostic(
|
|
1302
|
+
`writer lease release failed unexpectedly (best-effort continue): ${describeErrorIdentity(error)}`,
|
|
1303
|
+
),
|
|
1304
|
+
);
|
|
1305
|
+
}
|
|
1335
1306
|
}
|
|
1336
1307
|
}
|
|
1337
1308
|
}
|
|
@@ -1543,8 +1514,10 @@ export async function runPostAdmissionSeatResume<
|
|
|
1543
1514
|
}): Promise<{ exitCode: number; admitted?: A; terminal?: T }> {
|
|
1544
1515
|
let request = input.request;
|
|
1545
1516
|
|
|
1546
|
-
// Load once for runDirectory / structural rejection / afterAdmittedLoad
|
|
1547
|
-
//
|
|
1517
|
+
// Load once for runDirectory / structural rejection / afterAdmittedLoad.
|
|
1518
|
+
// Court identity for public manual resume is judged in the turn builder
|
|
1519
|
+
// (#987: no package writer-lease gate before host CLI resume). Station-child
|
|
1520
|
+
// auto-resume still acquires the shared lease in runWithAutoResumeLoop.
|
|
1548
1521
|
let loaded;
|
|
1549
1522
|
try {
|
|
1550
1523
|
loaded = await input.load(request);
|
|
@@ -1575,8 +1548,9 @@ export async function runPostAdmissionSeatResume<
|
|
|
1575
1548
|
|
|
1576
1549
|
const buildRequestAfterLease = async (): Promise<RoleTurnRequest> => {
|
|
1577
1550
|
let openCourtAttemptId: string | undefined;
|
|
1578
|
-
// Build uses the admitted
|
|
1579
|
-
//
|
|
1551
|
+
// Build uses the admitted for this resume (rehydrated when open court
|
|
1552
|
+
// materials ride). Settlement identity stays on the outer admitted.
|
|
1553
|
+
// Name kept for station-child callers that still build under lease.
|
|
1580
1554
|
let admittedForBuild = loaded.admitted;
|
|
1581
1555
|
|
|
1582
1556
|
// Bare resume: open-court pointer is the continue signal (not ledger seal).
|
|
@@ -1652,9 +1626,10 @@ export async function runPostAdmissionSeatResume<
|
|
|
1652
1626
|
return turnRequest;
|
|
1653
1627
|
};
|
|
1654
1628
|
|
|
1655
|
-
// Court recovery / open
|
|
1629
|
+
// Court recovery / open, then dispatch. Public manual resume does not take a
|
|
1630
|
+
// package writer lease before the host CLI (#987 / ADR 0080 one-shot).
|
|
1656
1631
|
// Station-child same-ticket/same-parent resume is call-local auto-resume
|
|
1657
|
-
// (#840 / #416)
|
|
1632
|
+
// (#840 / #416) and still acquires the shared lease in its loop.
|
|
1658
1633
|
// afterAdmittedPrepare runs inside this try so mint failure and cleanup share one finally.
|
|
1659
1634
|
try {
|
|
1660
1635
|
if (input.afterAdmittedPrepare !== undefined) {
|
|
@@ -1873,10 +1848,11 @@ export async function runPostAdmissionResumable<
|
|
|
1873
1848
|
}
|
|
1874
1849
|
|
|
1875
1850
|
/**
|
|
1876
|
-
* Manual resume:
|
|
1877
|
-
* short-circuit (#833 / #416). Court open
|
|
1878
|
-
*
|
|
1879
|
-
*
|
|
1851
|
+
* Manual resume: pass-through to the host CLI resume — no package writer-lease
|
|
1852
|
+
* pre-gate (#987), no sealed-accepted short-circuit (#833 / #416). Court open
|
|
1853
|
+
* (summons / message / open court) is built when using buildRequestAfterLease;
|
|
1854
|
+
* sole-final stays per-attempt. Station-child auto-resume keeps shared lease
|
|
1855
|
+
* acquire in runWithAutoResumeLoop + dispatchAfterWriterLease.
|
|
1880
1856
|
*/
|
|
1881
1857
|
export async function runPostAdmissionManualResume<
|
|
1882
1858
|
A extends AdmittedRoleInvocation,
|
|
@@ -1885,9 +1861,9 @@ export async function runPostAdmissionManualResume<
|
|
|
1885
1861
|
admitted: A;
|
|
1886
1862
|
env: PostAdmissionEnv;
|
|
1887
1863
|
io: CliIo;
|
|
1888
|
-
/** Eager turn request (non-court path / seats that build before
|
|
1864
|
+
/** Eager turn request (non-court path / seats that build before dispatch). */
|
|
1889
1865
|
request?: RoleTurnRequest;
|
|
1890
|
-
/**
|
|
1866
|
+
/** Turn builder (#637). Mutually exclusive with a prebuilt request. */
|
|
1891
1867
|
buildRequestAfterLease?: () => Promise<RoleTurnRequest>;
|
|
1892
1868
|
adapters: PostAdmissionAdapters<A, T>;
|
|
1893
1869
|
/** Seat-table engine axis on resume (#600). */
|
|
@@ -1896,7 +1872,6 @@ export async function runPostAdmissionManualResume<
|
|
|
1896
1872
|
exitCode: number;
|
|
1897
1873
|
admitted?: A;
|
|
1898
1874
|
terminal?: T;
|
|
1899
|
-
staleWriterLeaseReclaimed?: true;
|
|
1900
1875
|
}> {
|
|
1901
1876
|
const {
|
|
1902
1877
|
admitted,
|
|
@@ -1910,67 +1885,34 @@ export async function runPostAdmissionManualResume<
|
|
|
1910
1885
|
// #617 DK-3: manual resume writes the live seat/env model (same as new legs).
|
|
1911
1886
|
const effectiveModel = env.model;
|
|
1912
1887
|
|
|
1913
|
-
let lease: RunWriterLease;
|
|
1914
|
-
let staleWriterLeaseReclaimed: true | undefined;
|
|
1915
|
-
try {
|
|
1916
|
-
lease = await acquireRunWriterLease(admitted.runDirectory, (diagnostic, kind?: WriterLeaseDiagnosticKind) => {
|
|
1917
|
-
// Record the typed fact before the fallible sink: if io.stderr throws
|
|
1918
|
-
// (acquire deliberately swallows diagnostic-sink failures), the reclaim
|
|
1919
|
-
// still happened and must stay observable.
|
|
1920
|
-
if (kind === "stale-reclaimed") staleWriterLeaseReclaimed = true;
|
|
1921
|
-
io.stderr(diagnostic);
|
|
1922
|
-
});
|
|
1923
|
-
} catch (error) {
|
|
1924
|
-
if (error instanceof RunWriterLeaseHeldError) {
|
|
1925
|
-
io.stderr(formatCliDiagnostic(error.message));
|
|
1926
|
-
// A held rejection after our own reclaim must still carry the fact that
|
|
1927
|
-
// this caller reclaimed the stale lock — e.g. another resumer re-locked
|
|
1928
|
-
// before our retry create (#629).
|
|
1929
|
-
return {
|
|
1930
|
-
exitCode: 1,
|
|
1931
|
-
...(staleWriterLeaseReclaimed === true
|
|
1932
|
-
? { staleWriterLeaseReclaimed: true as const }
|
|
1933
|
-
: {}),
|
|
1934
|
-
};
|
|
1935
|
-
}
|
|
1936
|
-
throw error;
|
|
1937
|
-
}
|
|
1938
|
-
|
|
1939
1888
|
// Explicit public resume is a new counting unit — mint once for this call.
|
|
1940
1889
|
const invocationScopeId = mintEngineDetourInvocationScope({
|
|
1941
1890
|
...(effectiveEngine === undefined ? {} : { effectiveEngine }),
|
|
1942
1891
|
});
|
|
1943
1892
|
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1893
|
+
if (request === undefined) {
|
|
1894
|
+
if (buildRequestAfterLease === undefined) {
|
|
1895
|
+
throw new Error(
|
|
1896
|
+
"runPostAdmissionManualResume requires request or buildRequestAfterLease",
|
|
1897
|
+
);
|
|
1898
|
+
}
|
|
1899
|
+
request = await buildRequestAfterLease();
|
|
1900
|
+
}
|
|
1901
|
+
request = withEngineDetourInvocationScope(request, invocationScopeId);
|
|
1902
|
+
|
|
1903
|
+
const result = await dispatchPostAdmissionTurn({
|
|
1904
|
+
admitted,
|
|
1905
|
+
env: {
|
|
1906
|
+
...env,
|
|
1907
|
+
...(effectiveModel === undefined ? {} : { model: effectiveModel }),
|
|
1908
|
+
...(admitted.correlationId === undefined
|
|
1909
|
+
? {}
|
|
1910
|
+
: { correlationId: admitted.correlationId }),
|
|
1957
1911
|
},
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
...env,
|
|
1963
|
-
...(effectiveModel === undefined ? {} : { model: effectiveModel }),
|
|
1964
|
-
...(admitted.correlationId === undefined
|
|
1965
|
-
? {}
|
|
1966
|
-
: { correlationId: admitted.correlationId }),
|
|
1967
|
-
},
|
|
1968
|
-
io,
|
|
1969
|
-
request: turnRequest,
|
|
1970
|
-
lease,
|
|
1971
|
-
adapters,
|
|
1972
|
-
...(effectiveEngine === undefined ? {} : { effectiveEngine }),
|
|
1973
|
-
}),
|
|
1912
|
+
io,
|
|
1913
|
+
request,
|
|
1914
|
+
adapters,
|
|
1915
|
+
...(effectiveEngine === undefined ? {} : { effectiveEngine }),
|
|
1974
1916
|
});
|
|
1975
1917
|
if (
|
|
1976
1918
|
result.terminal !== undefined &&
|
|
@@ -1986,10 +1928,5 @@ export async function runPostAdmissionManualResume<
|
|
|
1986
1928
|
if (result.terminal !== undefined) {
|
|
1987
1929
|
(result.terminal as { autoResumeCount?: number }).autoResumeCount = 0;
|
|
1988
1930
|
}
|
|
1989
|
-
return
|
|
1990
|
-
...result,
|
|
1991
|
-
...(staleWriterLeaseReclaimed === true
|
|
1992
|
-
? { staleWriterLeaseReclaimed: true as const }
|
|
1993
|
-
: {}),
|
|
1994
|
-
};
|
|
1931
|
+
return result;
|
|
1995
1932
|
}
|
|
@@ -40,6 +40,10 @@ import {
|
|
|
40
40
|
} from "../diarist-contracts.ts";
|
|
41
41
|
import type { DoctorCaseIdentity } from "../doctor-contracts.ts";
|
|
42
42
|
import type { NotarySourceRunLocator } from "../notary-contracts.ts";
|
|
43
|
+
import {
|
|
44
|
+
rewriteAdmittedRoleRunPage,
|
|
45
|
+
rewriteRunDirectoryPathValue,
|
|
46
|
+
} from "../role-run-relocation.ts";
|
|
43
47
|
import {
|
|
44
48
|
appendEngineSessionMaterial,
|
|
45
49
|
engineSessionMaterialFromOptions,
|
|
@@ -419,15 +423,20 @@ async function readRoleRunStateDisk(
|
|
|
419
423
|
if (typeof record.projectRoot !== "string") return undefined;
|
|
420
424
|
if (typeof record.sessionDirectory !== "string") return undefined;
|
|
421
425
|
if (typeof record.admittedRequestPath !== "string") return undefined;
|
|
422
|
-
const
|
|
426
|
+
const storedRunDirectory =
|
|
423
427
|
typeof record.runDirectory === "string" && record.runDirectory.trim() !== ""
|
|
424
428
|
? record.runDirectory
|
|
425
429
|
: runDirectory;
|
|
430
|
+
const runDir = runDirectory;
|
|
426
431
|
// Principal wire stays uninterpreted — authority owns legacy sessionFile fallback.
|
|
427
432
|
const principalWire: RoleRunPrincipalWire = {
|
|
428
|
-
sessionDirectory:
|
|
433
|
+
sessionDirectory: rewriteRunDirectoryPathValue(
|
|
434
|
+
record.sessionDirectory,
|
|
435
|
+
storedRunDirectory,
|
|
436
|
+
runDirectory,
|
|
437
|
+
) as string,
|
|
429
438
|
...(typeof record.sessionFile === "string"
|
|
430
|
-
? { sessionFile: record.sessionFile }
|
|
439
|
+
? { sessionFile: rewriteRunDirectoryPathValue(record.sessionFile, storedRunDirectory, runDirectory) as string }
|
|
431
440
|
: {}),
|
|
432
441
|
};
|
|
433
442
|
let resumable: TypedHttp429Observation | undefined;
|
|
@@ -458,7 +467,11 @@ async function readRoleRunStateDisk(
|
|
|
458
467
|
bookKey: record.bookKey,
|
|
459
468
|
projectRoot: record.projectRoot,
|
|
460
469
|
runDirectory: runDir,
|
|
461
|
-
admittedRequestPath:
|
|
470
|
+
admittedRequestPath: rewriteRunDirectoryPathValue(
|
|
471
|
+
record.admittedRequestPath,
|
|
472
|
+
storedRunDirectory,
|
|
473
|
+
runDirectory,
|
|
474
|
+
) as string,
|
|
462
475
|
principalWire,
|
|
463
476
|
...(phase === undefined ? {} : { phase }),
|
|
464
477
|
...(resumable === undefined ? {} : { resumable }),
|
|
@@ -1433,6 +1446,14 @@ async function loadResumableRunRecord(
|
|
|
1433
1446
|
);
|
|
1434
1447
|
if (raw !== null && typeof raw === "object" && !Array.isArray(raw)) {
|
|
1435
1448
|
const record = raw as Record<string, unknown>;
|
|
1449
|
+
const storedRunDirectory =
|
|
1450
|
+
typeof record.runDirectory === "string" && record.runDirectory.trim() !== ""
|
|
1451
|
+
? record.runDirectory
|
|
1452
|
+
: run.runDirectory;
|
|
1453
|
+
rewriteAdmittedRoleRunPage(record, [{
|
|
1454
|
+
oldRunDirectory: storedRunDirectory,
|
|
1455
|
+
newRunDirectory: run.runDirectory,
|
|
1456
|
+
}]);
|
|
1436
1457
|
if (typeof record.instruction === "string") {
|
|
1437
1458
|
instruction = record.instruction;
|
|
1438
1459
|
}
|
|
@@ -1649,6 +1670,23 @@ async function loadResumableRunRecord(
|
|
|
1649
1670
|
} else if (mergedSet.tickets !== undefined) {
|
|
1650
1671
|
courtTicketNumbers = mergedSet.tickets;
|
|
1651
1672
|
}
|
|
1673
|
+
const sourceRunLeaf = basename(sourceRunPath ?? "").split("@");
|
|
1674
|
+
const referencedRunId = sourceRun?.runId ?? sourceRunLeaf[0];
|
|
1675
|
+
const referencedRole = sourceRun?.role ?? sourceRunLeaf[1];
|
|
1676
|
+
if (referencedRunId !== undefined && referencedRunId !== "") {
|
|
1677
|
+
const currentSourceRunDirectory = await findRunDirectoryById(
|
|
1678
|
+
home,
|
|
1679
|
+
referencedRunId,
|
|
1680
|
+
run.bookKey,
|
|
1681
|
+
referencedRole,
|
|
1682
|
+
);
|
|
1683
|
+
if (currentSourceRunDirectory !== undefined) {
|
|
1684
|
+
sourceRunPath = currentSourceRunDirectory;
|
|
1685
|
+
if (sourceRun !== undefined) {
|
|
1686
|
+
sourceRun = { ...sourceRun, runDirectory: currentSourceRunDirectory };
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1652
1690
|
return {
|
|
1653
1691
|
run,
|
|
1654
1692
|
principal,
|
|
@@ -196,6 +196,29 @@ function rewriteSummonsMaterials(
|
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
+
/** Project one admitted page in memory after its containing run moved. */
|
|
200
|
+
export function rewriteAdmittedRoleRunPage(
|
|
201
|
+
page: Record<string, unknown>,
|
|
202
|
+
rewrites: readonly RunDirectoryPathRewrite[],
|
|
203
|
+
): void {
|
|
204
|
+
rewriteRunDirectoryPathFieldsAgainstRewrites(page, ADMITTED_PAGE_FIELDS, rewrites);
|
|
205
|
+
rewriteSourceRunLocator(page.sourceRun, rewrites);
|
|
206
|
+
if (Array.isArray(page.attachments)) {
|
|
207
|
+
for (const attachment of page.attachments) {
|
|
208
|
+
if (isPlainObject(attachment)) {
|
|
209
|
+
rewriteRunDirectoryPathFieldsAgainstRewrites(attachment, ["frozenPath"], rewrites);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (isPlainObject(page.principal)) {
|
|
214
|
+
rewriteRunDirectoryPathFieldsAgainstRewrites(
|
|
215
|
+
page.principal,
|
|
216
|
+
["sessionDirectory", "sessionFile"],
|
|
217
|
+
rewrites,
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
199
222
|
async function rewriteJsonObjectFile(
|
|
200
223
|
path: string,
|
|
201
224
|
fields: readonly string[],
|
|
@@ -394,9 +417,9 @@ async function rewriteNestedMachinePathPages(
|
|
|
394
417
|
/**
|
|
395
418
|
* Rewrite admitted-request / invocation / run-state path fields (attachment
|
|
396
419
|
* frozenPath / summons.attachmentPaths pointers only), then nested package-owned
|
|
397
|
-
* session seams
|
|
398
|
-
* `pagesDirectory` is where the pages
|
|
399
|
-
*
|
|
420
|
+
* session seams. Callers may rewrite before or after the filesystem move/copy:
|
|
421
|
+
* `pagesDirectory` is where the pages currently live on disk; path strings that
|
|
422
|
+
* still name `oldRunDirectory` become `newRunDirectory`. `crossRunRewrites`
|
|
400
423
|
* covers officer/parent pointers that landed under a different final placement.
|
|
401
424
|
*/
|
|
402
425
|
export async function rewriteRoleRunDurablePages(input: {
|
|
@@ -414,33 +437,7 @@ export async function rewriteRoleRunDurablePages(input: {
|
|
|
414
437
|
string,
|
|
415
438
|
unknown
|
|
416
439
|
>;
|
|
417
|
-
|
|
418
|
-
page,
|
|
419
|
-
ADMITTED_PAGE_FIELDS,
|
|
420
|
-
rewrites,
|
|
421
|
-
);
|
|
422
|
-
// Nested notary typed locator — same value family as top-level sourceRunPath.
|
|
423
|
-
rewriteSourceRunLocator(page.sourceRun, rewrites);
|
|
424
|
-
if (Array.isArray(page.attachments)) {
|
|
425
|
-
for (const attachment of page.attachments) {
|
|
426
|
-
if (isPlainObject(attachment)) {
|
|
427
|
-
// Pointer only — never open or mutate frozen attachment bytes.
|
|
428
|
-
rewriteRunDirectoryPathFieldsAgainstRewrites(
|
|
429
|
-
attachment,
|
|
430
|
-
["frozenPath"],
|
|
431
|
-
rewrites,
|
|
432
|
-
);
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
// Nested principal wire (sessionDirectory/sessionFile) when present.
|
|
437
|
-
if (isPlainObject(page.principal)) {
|
|
438
|
-
rewriteRunDirectoryPathFieldsAgainstRewrites(
|
|
439
|
-
page.principal,
|
|
440
|
-
["sessionDirectory", "sessionFile"],
|
|
441
|
-
rewrites,
|
|
442
|
-
);
|
|
443
|
-
}
|
|
440
|
+
rewriteAdmittedRoleRunPage(page, rewrites);
|
|
444
441
|
await writeFile(admittedPath, `${JSON.stringify(page, null, 2)}\n`, "utf8");
|
|
445
442
|
}
|
|
446
443
|
|