@akagilnc/pi-workflow-roles 0.1.5483 → 0.1.5571

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.
Files changed (112) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/acp-host/production-host.js +6312 -6376
  4. package/dist/analyst-gate-cycles-read.js +28 -78
  5. package/dist/analyst-ledger.js +26 -42
  6. package/dist/analyst-metric-families/acceptance-success-rework.js +25 -1
  7. package/dist/analyst-metric-families/round-timeline.js +2 -5
  8. package/dist/archivist-record-entry.js +4 -3
  9. package/dist/archivist-record-pointer.js +71 -36
  10. package/dist/atomic-write.js +2 -2
  11. package/dist/auditor-dossier-tool.js +11 -6
  12. package/dist/auditor-soul.js +5 -12
  13. package/dist/collector-config.js +6 -5
  14. package/dist/doctor-evidence.js +8 -4
  15. package/dist/engine-detour-usage.js +8 -22
  16. package/dist/headless-host/production-host.js +5969 -6011
  17. package/dist/host-descriptions.js +0 -4
  18. package/dist/host-session-record.js +266 -267
  19. package/dist/ledger-session-read.js +5 -0
  20. package/dist/migrate-book-topology.js +1369 -722
  21. package/dist/navigator-public-session.js +13 -17
  22. package/dist/notary-source-run.js +8 -16
  23. package/dist/packaged-role-registry.js +5 -56
  24. package/dist/pi/durable-principal.js +2 -3
  25. package/dist/pi/role-turn-host.js +79 -15
  26. package/dist/public-cli/auto-resume.js +8 -47
  27. package/dist/public-cli/instruction-seat-run.js +9 -7
  28. package/dist/public-cli/invocation.js +67 -100
  29. package/dist/public-cli/main.js +2689 -2532
  30. package/dist/public-cli/post-admission.js +96 -55
  31. package/dist/public-cli/run-lifecycle.js +44 -100
  32. package/dist/public-cli/settlement.js +184 -429
  33. package/dist/public-cli/terminal.js +0 -12
  34. package/dist/role-run-placement.js +53 -16
  35. package/dist/role-run-relocation.js +111 -52
  36. package/dist/run-dossier-files.js +30 -0
  37. package/dist/run-dossier.js +322 -0
  38. package/dist/run-terminal-artifacts.js +69 -202
  39. package/dist/run-ticket-number.js +49 -7
  40. package/dist/session-assistant-usage.js +9 -18
  41. package/dist/session-identity.js +49 -19
  42. package/dist/sitian-appender.js +29 -18
  43. package/dist/sitian-facade.js +11 -0
  44. package/dist/sitian-reader.js +44 -29
  45. package/dist/submission-gate.js +1 -1
  46. package/dist/submission-ledger.js +4 -4
  47. package/dist/ticket-provenance-contracts.js +2 -8
  48. package/dist/ticket-provenance.js +25 -9
  49. package/package.json +1 -1
  50. package/scripts/build-package.mjs +2 -0
  51. package/src/acp-host/production-host.ts +1 -1
  52. package/src/acp-host/role-turn-host.ts +11 -4
  53. package/src/analyst-gate-cycles-read.ts +31 -88
  54. package/src/analyst-ledger.ts +36 -51
  55. package/src/analyst-metric-families/acceptance-success-rework.ts +24 -1
  56. package/src/analyst-metric-families/gate-cycles.ts +1 -1
  57. package/src/analyst-metric-families/round-timeline.ts +2 -5
  58. package/src/analyst-page.ts +3 -3
  59. package/src/archivist-record-entry.ts +6 -5
  60. package/src/archivist-record-pointer.ts +92 -42
  61. package/src/atomic-write.ts +3 -1
  62. package/src/auditor-dossier-tool.ts +14 -8
  63. package/src/auditor-soul.ts +5 -10
  64. package/src/book-topology-migration-placement.ts +16 -3
  65. package/src/book-topology-record-class-migrators.ts +4 -2
  66. package/src/book-topology-runs-migrator.ts +6 -3
  67. package/src/collector-config.ts +5 -3
  68. package/src/doctor-contracts.ts +1 -1
  69. package/src/doctor-evidence.ts +7 -3
  70. package/src/engine-detour-tool.ts +1 -1
  71. package/src/engine-detour-usage.ts +12 -25
  72. package/src/external-host-turn-loop.ts +25 -0
  73. package/src/factory-board.ts +16 -81
  74. package/src/headless-host/production-host.ts +1 -4
  75. package/src/headless-host/role-turn-host.ts +56 -21
  76. package/src/host-descriptions.ts +0 -6
  77. package/src/host-session-record.ts +224 -140
  78. package/src/ledger-session-read.ts +6 -0
  79. package/src/navigator-public-session.ts +13 -18
  80. package/src/navigator-work-context.ts +3 -1
  81. package/src/notary-role.ts +2 -1
  82. package/src/notary-source-run.ts +8 -18
  83. package/src/packaged-role-registry.ts +1 -65
  84. package/src/pi/adapter.ts +50 -7
  85. package/src/pi/durable-principal.ts +2 -4
  86. package/src/pi/role-turn-host.ts +85 -14
  87. package/src/public-cli/auto-resume.ts +7 -43
  88. package/src/public-cli/instruction-seat-run.ts +13 -6
  89. package/src/public-cli/invocation.ts +90 -144
  90. package/src/public-cli/post-admission.ts +111 -72
  91. package/src/public-cli/run-lifecycle.ts +47 -115
  92. package/src/public-cli/settlement.ts +221 -589
  93. package/src/public-cli/terminal.ts +1 -61
  94. package/src/role-envelope.ts +4 -2
  95. package/src/role-run-placement.ts +65 -17
  96. package/src/role-run-relocation.ts +110 -70
  97. package/src/role-runtime.ts +46 -69
  98. package/src/run-dossier-files.ts +33 -0
  99. package/src/run-dossier.ts +357 -0
  100. package/src/run-terminal-artifacts.ts +84 -240
  101. package/src/run-ticket-number.ts +82 -14
  102. package/src/session-assistant-usage.ts +8 -20
  103. package/src/session-identity.ts +52 -21
  104. package/src/sitian-appender.ts +33 -26
  105. package/src/sitian-facade.ts +22 -0
  106. package/src/sitian-reader.ts +62 -32
  107. package/src/submission-gate.ts +1 -1
  108. package/src/submission-ledger.ts +4 -4
  109. package/src/ticket-provenance-contracts.ts +3 -6
  110. package/src/ticket-provenance.ts +23 -11
  111. package/src/ticket-snapshot.ts +3 -2
  112. package/src/ticket-trajectory.ts +95 -109
@@ -16,7 +16,7 @@ import {
16
16
  activationBookDirectory,
17
17
  resolveActivationLedgerHome,
18
18
  } from "../activation-ledger-topology.ts";
19
- import { listBookRunDirectories } from "../role-run-placement.ts";
19
+ import { findRoleRunDirectory, listBookRunDirectories, parseRunLeaf, sessionFileIn } from "../role-run-placement.ts";
20
20
  import { isSafePositiveTicketNumber } from "../run-ticket-number.ts";
21
21
  import { CliUsageError } from "./cli-errors.ts";
22
22
  import type { FixerPhase } from "../package-contracts/fixer-output.ts";
@@ -25,6 +25,7 @@ import { parseCollectorRepository } from "../collector-config.ts";
25
25
  import type { DoctorCaseIdentity } from "../doctor-contracts.ts";
26
26
  import type { NotarySourceRunLocator } from "../notary-contracts.ts";
27
27
  import {
28
+ retainedRunPathsMatch,
28
29
  rewriteAdmittedRoleRunPage,
29
30
  rewriteRunDirectoryPathValue,
30
31
  } from "../role-run-relocation.ts";
@@ -62,6 +63,7 @@ import {
62
63
  type ReviewerLens,
63
64
  } from "./invocation.ts";
64
65
 
66
+ import { readPageSync, writeSectionSync } from "../run-dossier.ts";
65
67
  import { isRecord, errorText, isEnoent } from "../unknown-value.ts";
66
68
 
67
69
  export type RoleRunState = "admitted" | "running" | "resumable" | "terminal";
@@ -82,7 +84,6 @@ export type RoleRunRecord = {
82
84
  /** Exact Pi session file principal reopened on resume (not directory-latest). */
83
85
  readonly sessionFile: string;
84
86
  readonly runDirectory: string;
85
- readonly admittedRequestPath: string;
86
87
  /** Coder/Fixer — preserved for resume continuation. */
87
88
  readonly phase?: CoderPhase | FixerPhase;
88
89
  };
@@ -153,7 +154,6 @@ export function buildAutoResumeContinuationPrompt(options: {
153
154
  ).join("\n");
154
155
  }
155
156
 
156
- const RUN_STATE_FILE = "run-state.json";
157
157
  const WRITER_LOCK_FILE = "writer.lock";
158
158
 
159
159
  export async function writeRoleRunState(
@@ -161,15 +161,11 @@ export async function writeRoleRunState(
161
161
  record: Omit<RoleRunRecord, "runDirectory">,
162
162
  ): Promise<void> {
163
163
  const payload: RoleRunRecord = { ...record, runDirectory };
164
- await writeFile(
165
- join(runDirectory, RUN_STATE_FILE),
166
- `${JSON.stringify(payload, null, 2)}\n`,
167
- "utf8",
168
- );
164
+ writeSectionSync(runDirectory, "runState", { ...payload });
169
165
  }
170
166
 
171
167
  /**
172
- * Uninterpreted principal wire as stored on run-state.json.
168
+ * Uninterpreted principal wire as stored on the state.jsonl run-state row.
173
169
  * Legacy rows may omit sessionFile; only DurablePrincipalAuthority decodes it.
174
170
  */
175
171
  type RoleRunPrincipalWire = {
@@ -185,7 +181,6 @@ type RoleRunStateDisk = {
185
181
  readonly bookKey: string;
186
182
  readonly projectRoot: string;
187
183
  readonly runDirectory: string;
188
- readonly admittedRequestPath: string;
189
184
  readonly principalWire: RoleRunPrincipalWire;
190
185
  readonly phase?: CoderPhase | FixerPhase;
191
186
  /** Open court turn (#637); omit when no unsealed current court. */
@@ -262,20 +257,13 @@ function parseCurrentCourtState(raw: unknown): CurrentCourtState | undefined {
262
257
  }
263
258
 
264
259
  async function readRoleRunStateRaw(runDirectory: string): Promise<unknown | undefined> {
265
- let raw: unknown;
266
- try {
267
- raw = JSON.parse(await readFile(join(runDirectory, RUN_STATE_FILE), "utf8"));
268
- } catch (error) {
269
- // Only true absence (ENOENT) is a lawful "no run state yet". A real read
270
- // failure (EISDIR, EACCES, ...) or a JSON.parse SyntaxError on a present
271
- // file is genuine infrastructure/data damage and must keep its own
272
- // identity — callers route it through the controlled-failure seam
273
- // (markRunRunning/markRunTerminal/recordCurrentCourt)
274
- // instead of it being relabeled "run state missing" (#836).
275
- if (errorCodeOf(error) === "ENOENT") return undefined;
276
- throw error;
277
- }
278
- return raw;
260
+ // Only true absence (no state.jsonl run-state row) is a lawful "no run state
261
+ // yet". A real read failure (EISDIR, EACCES, ...) or an illegal latest payload
262
+ // is genuine infrastructure/data damage and must keep its own identity —
263
+ // callers route it through the controlled-failure seam
264
+ // (markRunRunning/markRunTerminal/recordCurrentCourt) instead of it being
265
+ // relabeled "run state missing" (#836). readPageSync throws those.
266
+ return readPageSync(runDirectory, "runState");
279
267
  }
280
268
 
281
269
  function parseRoleRunIdentity(raw: unknown, runDirectory: string): {
@@ -286,15 +274,15 @@ function parseRoleRunIdentity(raw: unknown, runDirectory: string): {
286
274
  readonly state: RoleRunState;
287
275
  } {
288
276
  if (!isRecord(raw)) {
289
- throw new TypeError("Invalid run-state.json");
277
+ throw new TypeError("Invalid state.jsonl run-state");
290
278
  }
291
279
  const record = raw;
292
280
  if (typeof record.runId !== "string" || record.runId.trim() === "") {
293
- throw new TypeError("Invalid run-state.json");
281
+ throw new TypeError("Invalid state.jsonl run-state");
294
282
  }
295
283
  const role = typeof record.role === "string" ? packagedRoleMetadata(record.role)?.role : undefined;
296
284
  if (role === undefined) {
297
- throw new TypeError("Invalid run-state.json");
285
+ throw new TypeError("Invalid state.jsonl run-state");
298
286
  }
299
287
  if (
300
288
  record.state !== "admitted" &&
@@ -302,9 +290,9 @@ function parseRoleRunIdentity(raw: unknown, runDirectory: string): {
302
290
  record.state !== "resumable" &&
303
291
  record.state !== "terminal"
304
292
  ) {
305
- throw new TypeError("Invalid run-state.json");
293
+ throw new TypeError("Invalid state.jsonl run-state");
306
294
  }
307
- if (typeof record.bookKey !== "string") throw new TypeError("Invalid run-state.json");
295
+ if (typeof record.bookKey !== "string") throw new TypeError("Invalid state.jsonl run-state");
308
296
  return {
309
297
  runId: record.runId,
310
298
  role,
@@ -321,9 +309,8 @@ async function readRoleRunStateDisk(
321
309
  if (raw === undefined) return undefined;
322
310
  const identity = parseRoleRunIdentity(raw, runDirectory);
323
311
  const record = raw as Record<string, unknown>;
324
- if (typeof record.projectRoot !== "string") throw new TypeError("Invalid run-state.json");
325
- if (typeof record.sessionDirectory !== "string") throw new TypeError("Invalid run-state.json");
326
- if (typeof record.admittedRequestPath !== "string") throw new TypeError("Invalid run-state.json");
312
+ if (typeof record.projectRoot !== "string") throw new TypeError("Invalid state.jsonl run-state");
313
+ if (typeof record.sessionDirectory !== "string") throw new TypeError("Invalid state.jsonl run-state");
327
314
  const storedRunDirectory =
328
315
  typeof record.runDirectory === "string" && record.runDirectory.trim() !== ""
329
316
  ? record.runDirectory
@@ -352,11 +339,6 @@ async function readRoleRunStateDisk(
352
339
  bookKey: identity.bookKey,
353
340
  projectRoot: record.projectRoot,
354
341
  runDirectory: runDir,
355
- admittedRequestPath: rewriteRunDirectoryPathValue(
356
- record.admittedRequestPath,
357
- storedRunDirectory,
358
- runDirectory,
359
- ) as string,
360
342
  principalWire,
361
343
  ...(phase === undefined ? {} : { phase }),
362
344
  ...(currentCourt === undefined ? {} : { currentCourt }),
@@ -378,15 +360,10 @@ async function writeRoleRunStateDisk(
378
360
  ...(disk.principalWire.sessionFile === undefined
379
361
  ? {}
380
362
  : { sessionFile: disk.principalWire.sessionFile }),
381
- admittedRequestPath: disk.admittedRequestPath,
382
363
  ...(disk.phase === undefined ? {} : { phase: disk.phase }),
383
364
  ...(disk.currentCourt === undefined ? {} : { currentCourt: disk.currentCourt }),
384
365
  };
385
- await writeFile(
386
- join(runDirectory, RUN_STATE_FILE),
387
- `${JSON.stringify(payload, null, 2)}\n`,
388
- "utf8",
389
- );
366
+ writeSectionSync(runDirectory, "runState", payload);
390
367
  }
391
368
 
392
369
  /** One authority.decode of the uninterpreted wire → record + opaque principal (frozen wire itself). */
@@ -407,7 +384,6 @@ function materializeRoleRunFromDisk(
407
384
  sessionDirectory: coordinates.sessionDirectory,
408
385
  sessionFile: coordinates.sessionFile,
409
386
  runDirectory: disk.runDirectory,
410
- admittedRequestPath: disk.admittedRequestPath,
411
387
  ...(disk.phase === undefined ? {} : { phase: disk.phase }),
412
388
  },
413
389
  };
@@ -466,7 +442,6 @@ export async function markRunAdmitted(
466
442
  projectRoot: admitted.projectRoot,
467
443
  sessionDirectory,
468
444
  sessionFile,
469
- admittedRequestPath: admitted.admittedRequestPath,
470
445
  ...(workerPhase === undefined ? {} : { phase: workerPhase }),
471
446
  });
472
447
  }
@@ -474,7 +449,7 @@ export async function markRunAdmitted(
474
449
  /**
475
450
  * Shared dispatch execution seam: transition to running, then record the
476
451
  * effective launch model (initial or resume override) and the authoritative
477
- * seat engine/host onto invocation.json.
452
+ * seat engine/host onto the invocation section.
478
453
  * Role runners must not coordinate lifecycle ledger writes themselves.
479
454
  * Engine axis is authoritative here (#617): present string is written; omit/undefined
480
455
  * clears any prior engine key so unset-engine + resume does not keep a stale value.
@@ -915,31 +890,12 @@ export async function findRunDirectoryById(
915
890
  if (errorCodeOf(error) === "ENOENT") return undefined;
916
891
  throw error;
917
892
  }
918
- const matches: string[] = [];
919
- for (const bookKey of bookKeys) {
920
- if (onlyBookKey !== undefined && bookKey !== onlyBookKey) continue;
921
- const bookDir = activationBookDirectory(ledgerHome, bookKey);
922
- let runDirectories: string[];
923
- try {
924
- runDirectories = await listBookRunDirectories(bookDir);
925
- } catch (error) {
926
- if (errorCodeOf(error) === "ENOENT") continue;
927
- throw error;
928
- }
929
- for (const runDirectory of runDirectories) {
930
- const entry = basename(runDirectory);
931
- if (
932
- (onlyRole === undefined && (entry === `${runId}@judge` || entry.startsWith(`${runId}@`))) ||
933
- entry === `${runId}@${onlyRole}`
934
- ) {
935
- matches.push(runDirectory);
936
- }
937
- }
938
- }
939
- if (matches.length === 0) return undefined;
940
- if (matches.length === 1) return matches[0];
941
- throw new Error(
942
- `ambiguous role run id ${runId}: ${matches.join(", ")}`,
893
+ return findRoleRunDirectory(
894
+ bookKeys
895
+ .filter((bookKey) => onlyBookKey === undefined || bookKey === onlyBookKey)
896
+ .map((bookKey) => activationBookDirectory(ledgerHome, bookKey)),
897
+ runId,
898
+ onlyRole,
943
899
  );
944
900
  }
945
901
 
@@ -963,19 +919,8 @@ export function parentRunPathFromGatePointerInstruction(
963
919
  export async function readRunParentPath(
964
920
  runDirectory: string,
965
921
  ): Promise<string | undefined> {
966
- let raw: unknown;
967
- try {
968
- raw = JSON.parse(
969
- await readFile(join(runDirectory, "admitted-request.json"), "utf8"),
970
- );
971
- } catch (error) {
972
- if (errorCodeOf(error) === "ENOENT") return undefined;
973
- throw error;
974
- }
975
- if (!isRecord(raw)) {
976
- return undefined;
977
- }
978
- const record = raw;
922
+ const record = readPageSync(runDirectory, "admitted");
923
+ if (record === undefined) return undefined;
979
924
  if (typeof record.sourceRunPath === "string" && record.sourceRunPath.trim() !== "") {
980
925
  return record.sourceRunPath;
981
926
  }
@@ -999,7 +944,7 @@ async function runHasFormedSessionPrincipal(runDirectory: string): Promise<boole
999
944
  typeof disk.principalWire.sessionFile === "string" &&
1000
945
  disk.principalWire.sessionFile.trim() !== ""
1001
946
  ? disk.principalWire.sessionFile
1002
- : join(disk.principalWire.sessionDirectory, "session.jsonl");
947
+ : sessionFileIn(disk.principalWire.sessionDirectory);
1003
948
  try {
1004
949
  const stat = await lstat(sessionFile);
1005
950
  return stat.isFile() && !stat.isSymbolicLink();
@@ -1040,17 +985,14 @@ export async function findLatestRunIdForSeatTicket(input: {
1040
985
  if (errorCodeOf(error) === "ENOENT") return undefined;
1041
986
  throw error;
1042
987
  }
1043
- const suffix = `@${input.role}`;
1044
988
  let best: string | undefined;
1045
989
  for (const runDirectory of runDirectories) {
1046
- const entry = basename(runDirectory);
1047
- if (!entry.endsWith(suffix)) continue;
1048
- const runId = entry.slice(0, entry.length - suffix.length);
1049
- if (runId.length === 0) continue;
990
+ const parsed = parseRunLeaf(basename(runDirectory));
991
+ if (parsed === undefined || parsed.role !== input.role) continue;
992
+ const runId = parsed.runId;
1050
993
  const parentPath = await readRunParentPath(runDirectory);
1051
- // The parent's ticket can change after this child was admitted. Within one
1052
- // book the run leaf is its stable identity; directory placement is not.
1053
- if (parentPath !== input.parentRunPath && basename(parentPath ?? "") !== basename(input.parentRunPath)) continue;
994
+ // Follow the parent's retained placement without collapsing distinct book/subject bindings.
995
+ if (!await retainedRunPathsMatch(parentPath, input.parentRunPath, input.parentRunPath)) continue;
1054
996
  // Durable fact: never resume-select a provisional that never formed principal.
1055
997
  if (!(await runHasFormedSessionPrincipal(runDirectory))) continue;
1056
998
  if (best === undefined || runId > best) best = runId;
@@ -1148,7 +1090,7 @@ async function loadResumableRunRecord(
1148
1090
  throw new CliUsageError(`unknown role run id: ${runId}`);
1149
1091
  }
1150
1092
  const { run, principal } = materialized;
1151
- // Reconstruct admitted identity from durable run record + admitted-request.json.
1093
+ // Reconstruct admitted identity from durable run record + the admitted section.
1152
1094
  let instruction = "";
1153
1095
  let instructionEmpty = true;
1154
1096
  let attachments: FrozenAttachment[] = [];
@@ -1176,11 +1118,9 @@ async function loadResumableRunRecord(
1176
1118
  let sourceRunPath: string | undefined;
1177
1119
  let sourceRun: NotarySourceRunLocator | undefined;
1178
1120
  try {
1179
- const raw: unknown = JSON.parse(
1180
- await readFile(run.admittedRequestPath, "utf8"),
1181
- );
1182
- if (isRecord(raw)) {
1183
- const record = raw as Record<string, unknown>;
1121
+ const record = readPageSync(run.runDirectory, "admitted");
1122
+ if (record === undefined) throw new Error("state.jsonl has no admitted-request row");
1123
+ {
1184
1124
  const storedRunDirectory =
1185
1125
  typeof record.runDirectory === "string" && record.runDirectory.trim() !== ""
1186
1126
  ? record.runDirectory
@@ -1226,7 +1166,7 @@ async function loadResumableRunRecord(
1226
1166
  lens = record.lens;
1227
1167
  }
1228
1168
  // Collector — admitted repository/PR identity (#633 resume).
1229
- if (typeof record.prNumber === "number" && Number.isSafeInteger(record.prNumber) && record.prNumber >= 1) {
1169
+ if (isSafePositiveTicketNumber(record.prNumber)) {
1230
1170
  prNumber = record.prNumber;
1231
1171
  }
1232
1172
  if (typeof record.repository === "string" && record.repository.trim() !== "") {
@@ -1245,7 +1185,7 @@ async function loadResumableRunRecord(
1245
1185
  waitWindowMs = record.waitWindowMs;
1246
1186
  }
1247
1187
  // Doctor — admitted single-case identity (#633 resume).
1248
- if (typeof record.issueNumber === "number" && Number.isSafeInteger(record.issueNumber) && record.issueNumber >= 1) {
1188
+ if (isSafePositiveTicketNumber(record.issueNumber)) {
1249
1189
  issueNumber = record.issueNumber;
1250
1190
  }
1251
1191
  if (typeof record.caseRunsPath === "string" && record.caseRunsPath.trim() !== "") {
@@ -1256,9 +1196,7 @@ async function loadResumableRunRecord(
1256
1196
  ) {
1257
1197
  const ci = record.caseIdentity as Record<string, unknown>;
1258
1198
  if (
1259
- typeof ci.issueNumber === "number" &&
1260
- Number.isSafeInteger(ci.issueNumber) &&
1261
- ci.issueNumber >= 1 &&
1199
+ isSafePositiveTicketNumber(ci.issueNumber) &&
1262
1200
  typeof ci.runsPath === "string" &&
1263
1201
  ci.runsPath.trim() !== ""
1264
1202
  ) {
@@ -1341,13 +1279,8 @@ async function loadResumableRunRecord(
1341
1279
  }
1342
1280
  let model: InvocationEffectiveModel | undefined;
1343
1281
  try {
1344
- const invocationRaw: unknown = JSON.parse(
1345
- await readFile(join(run.runDirectory, "invocation.json"), "utf8"),
1346
- );
1347
- if (
1348
- isRecord(invocationRaw)
1349
- ) {
1350
- const rec = invocationRaw as Record<string, unknown>;
1282
+ const rec = readPageSync(run.runDirectory, "invocation");
1283
+ if (rec !== undefined) {
1351
1284
  if (typeof rec.provider === "string" && typeof rec.model === "string") {
1352
1285
  model = {
1353
1286
  provider: rec.provider,
@@ -1376,9 +1309,9 @@ async function loadResumableRunRecord(
1376
1309
  );
1377
1310
  }
1378
1311
  }
1379
- const sourceRunLeaf = basename(sourceRunPath ?? "").split("@");
1380
- const referencedRunId = sourceRun?.runId ?? sourceRunLeaf[0];
1381
- const referencedRole = sourceRun?.role ?? sourceRunLeaf[1];
1312
+ const sourceRunLeaf = parseRunLeaf(basename(sourceRunPath ?? ""));
1313
+ const referencedRunId = sourceRun?.runId ?? sourceRunLeaf?.runId;
1314
+ const referencedRole = sourceRun?.role ?? sourceRunLeaf?.role;
1382
1315
  if (referencedRunId !== undefined && referencedRunId !== "") {
1383
1316
  const currentSourceRunDirectory = await findRunDirectoryById(
1384
1317
  home,
@@ -1446,7 +1379,6 @@ function resumedBaseAdmitted(loaded: {
1446
1379
  attachments: loaded.admittedFields.attachments,
1447
1380
  runDirectory: loaded.run.runDirectory,
1448
1381
  principal: loaded.principal,
1449
- admittedRequestPath: loaded.run.admittedRequestPath,
1450
1382
  ...(loaded.admittedFields.model === undefined ? {} : { model: loaded.admittedFields.model }),
1451
1383
  ...restoredTicketFields(loaded.admittedFields),
1452
1384
  };