@akagilnc/pi-workflow-roles 0.1.5357 → 0.1.5368

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 (43) hide show
  1. package/dist/acp-host/production-host.js +5353 -5480
  2. package/dist/doctor-contracts.js +22 -34
  3. package/dist/headless-host/production-host.js +1215 -1342
  4. package/dist/merger-contracts.js +11 -6
  5. package/dist/migrate-book-topology.js +57 -101
  6. package/dist/open-tool-schema.js +1 -25
  7. package/dist/package-contracts/fixer-output.js +21 -35
  8. package/dist/public-cli/auto-resume.js +1 -1
  9. package/dist/public-cli/cli.js +57 -56
  10. package/dist/public-cli/config.js +4 -0
  11. package/dist/public-cli/instruction-seat-run.js +65 -40
  12. package/dist/public-cli/invocation.js +132 -128
  13. package/dist/public-cli/main.js +449 -506
  14. package/dist/public-cli/post-admission.js +85 -86
  15. package/dist/public-cli/role-seat-runtime.js +29 -0
  16. package/dist/public-cli/run-lifecycle.js +6 -6
  17. package/dist/public-cli/turn-request.js +14 -0
  18. package/dist/public-role-summons.js +20 -33
  19. package/dist/role-run-placement.js +4 -0
  20. package/package.json +1 -1
  21. package/src/book-topology-migration-placement.ts +3 -5
  22. package/src/diarist-role.ts +12 -49
  23. package/src/doctor-contracts.ts +38 -49
  24. package/src/gleaner-left-role.ts +10 -11
  25. package/src/merger-contracts.ts +19 -12
  26. package/src/open-tool-schema.ts +8 -32
  27. package/src/package-contracts/fixer-output.ts +49 -52
  28. package/src/public-cli/auto-resume.ts +1 -1
  29. package/src/public-cli/cli.ts +67 -73
  30. package/src/public-cli/config.ts +8 -0
  31. package/src/public-cli/instruction-seat-run.ts +82 -55
  32. package/src/public-cli/invocation.ts +148 -162
  33. package/src/public-cli/post-admission.ts +110 -100
  34. package/src/public-cli/role-seat-runtime.ts +72 -0
  35. package/src/public-cli/run-lifecycle.ts +7 -26
  36. package/src/public-cli/terminal.ts +2 -17
  37. package/src/public-cli/turn-request.ts +26 -0
  38. package/src/public-role-summons.ts +20 -45
  39. package/src/reviewer-role.ts +16 -35
  40. package/src/role-run-placement.ts +5 -0
  41. package/src/secretariat-role.ts +16 -12
  42. package/src/worker-role.ts +14 -29
  43. package/src/worker-submission-gates.ts +14 -3
@@ -4,7 +4,6 @@ import { piDurablePrincipalAuthority } from "../pi/durable-principal.ts";
4
4
  * Public ak-role CLI dispatcher (roles / config / layered help / Judge run).
5
5
  */
6
6
  import { realpath } from "node:fs/promises";
7
- import { join } from "node:path";
8
7
 
9
8
  import { packageMachineHome } from "../activation-ledger-topology.ts";
10
9
  import {
@@ -26,13 +25,13 @@ import {
26
25
  missingResolvedSeatModelMessage,
27
26
  parseModelSpec,
28
27
  resolveEffectiveSeat,
29
- resolvedSeatWithModel,
30
28
  savePublicCliConfig,
31
29
  setAutoResumeLimit,
32
30
  setPersistentSeatConfig,
33
31
  setPersistentSeatEngine,
34
32
  setPersistentSeatEngineModel,
35
33
  setPersistentSeatHost,
34
+ resolvePublicAgentDir,
36
35
  validatePublicCliConfigAxes,
37
36
  type CredentialProviders,
38
37
  type EffectiveSeat,
@@ -41,7 +40,6 @@ import {
41
40
  } from "./config.ts";
42
41
  import {
43
42
  loadHostProvidersTable,
44
- projectHostFacingProvider,
45
43
  renderHostProvidersTable,
46
44
  } from "./host-providers.ts";
47
45
  import { packagedModelParent } from "../packaged-role-registry.ts";
@@ -54,11 +52,10 @@ import {
54
52
  type PostAdmissionEnv,
55
53
  } from "./post-admission.ts";
56
54
  import { appendPiSessionCustomEntry } from "../pi/role-turn-host.ts";
55
+ import { openRoleSeatRuntime } from "./role-seat-runtime.ts";
57
56
  import {
58
- composeRoleTurnHostAdapters,
59
57
  formatHostSelectionFailure,
60
58
  HostSelectionError,
61
- selectRoleTurnHost,
62
59
  type HostSelectionFailure,
63
60
  type NamedRoleTurnHostAdapter,
64
61
  } from "./role-turn-host-resolution.ts";
@@ -258,54 +255,43 @@ function createRoleEnvironment(
258
255
  /** #178: after host selection, before missing-model — structural argv parse once. */
259
256
  afterHost?: () => void,
260
257
  ) {
261
- const role = options.role;
262
258
  const extraPiArgs = env.extraPiArgs;
263
259
  const timeoutMs = env.timeoutMs;
264
-
265
- // #617/#178/#788/#840: host first, then model; nested child seat selects own host.
266
- const hostAdapters = composeRoleTurnHostAdapters(
267
- {
260
+ const opened = openRoleSeatRuntime({
261
+ resolution: {
268
262
  packageRoot: env.packageRoot,
269
263
  ...(env.roleTurnHost === undefined ? {} : { roleTurnHost: env.roleTurnHost }),
270
264
  ...(env.hostAdapters === undefined ? {} : { hostAdapters: env.hostAdapters }),
271
265
  ...(extraPiArgs === undefined ? {} : { extraPiArgs }),
272
266
  ...(timeoutMs === undefined ? {} : { timeoutMs }),
273
267
  },
274
- env.principalAuthority!,
275
- );
276
- const roleTurnHost = selectRoleTurnHost(hostAdapters, {
277
- role,
268
+ principalAuthority: env.principalAuthority!,
269
+ role: options.role,
278
270
  seat: options.seat,
271
+ home: options.home,
272
+ ...(afterHost === undefined ? {} : { afterHost }),
279
273
  });
280
- // #178: host → argv (afterHost) → missing-model → provider projection.
281
- afterHost?.();
282
- const seatWithModel = resolvedSeatWithModel(options.seat);
283
- if (seatWithModel === undefined) {
274
+ if (!opened.ok) {
284
275
  // Direct CLI has --model; dual remediation is intentional here (#178 / #916).
285
276
  throw new CliUsageError(
286
277
  missingResolvedSeatModelMessage(options.seat.seat, "invocation-or-config"),
287
278
  );
288
279
  }
289
- const hostFacingSelection = projectHostFacingProvider(
290
- seatWithModel.selection,
291
- seatWithModel.host,
292
- loadHostProvidersTable(options.home),
293
- options.home,
294
- );
295
280
  return {
296
281
  home: options.home,
297
282
  principalAuthority: env.principalAuthority!,
298
283
  agentDir: options.agentDir,
299
284
  sessionAppender: appendPiSessionCustomEntry,
300
285
  packageRoot: env.packageRoot,
301
- roleTurnHost,
302
- hostAdapters,
286
+ roleTurnHost: opened.roleTurnHost,
287
+ hostAdapters: opened.hostAdapters,
303
288
  cwd: options.cwd,
304
289
  ...(options.credentials === undefined ? {} : { credentials: options.credentials }),
305
290
  ...(env.correlationId === undefined ? {} : { correlationId: env.correlationId }),
306
- ...(hostFacingSelection === undefined ? {} : { model: hostFacingSelection }),
307
- ...projectSeatEngine(options.seat),
308
- ...projectSeatHost(options.seat),
291
+ ...(opened.model === undefined ? {} : { model: opened.model }),
292
+ ...(opened.engine === undefined ? {} : { engine: opened.engine }),
293
+ ...(opened.engineModel === undefined ? {} : { engineModel: opened.engineModel }),
294
+ host: opened.host,
309
295
  ...(timeoutMs === undefined ? {} : { timeoutMs }),
310
296
  ...(env.createRunId === undefined ? {} : { createRunId: env.createRunId }),
311
297
  ...(options.config?.autoResumeLimit === undefined
@@ -319,6 +305,25 @@ function createRoleEnvironment(
319
305
  };
320
306
  }
321
307
 
308
+ type PublicRoleDispatchParts = {
309
+ readonly agentDir: string;
310
+ readonly cwd: string;
311
+ readonly config: PublicCliConfig;
312
+ readonly credentials: CredentialProviders;
313
+ };
314
+
315
+ /** Shared preamble for a new public role command and an explicit resume. */
316
+ async function loadPublicRoleDispatchParts(
317
+ env: CliEnv,
318
+ home: string,
319
+ ): Promise<PublicRoleDispatchParts> {
320
+ const agentDir = resolvePublicAgentDir(env.agentDir, home);
321
+ const cwd = env.cwd ?? process.cwd();
322
+ const config = await loadAndValidateConfig(home, env.packageRoot);
323
+ const credentials = env.credentials ?? (await loadCredentialProviders(agentDir));
324
+ return { agentDir, cwd, config, credentials };
325
+ }
326
+
322
327
  /**
323
328
  * #178 single spine: resolve seat → host select → argv parse once → missing-model → run.
324
329
  * Keeps host-unregistered ahead of bad argv and missing-model.
@@ -336,15 +341,11 @@ async function dispatchPublicRoleCommand<TParsed>(
336
341
  parseOnce: () => TParsed,
337
342
  ) => Promise<{ exitCode: number; terminal?: TerminalResult }>,
338
343
  ): Promise<CliResult> {
339
- const agentDir = resolveAgentDir(env, home);
340
- const cwd = env.cwd ?? process.cwd();
341
- const config = await loadAndValidateConfig(home, env.packageRoot);
342
- const credentials =
343
- env.credentials ?? (await loadCredentialProviders(agentDir));
344
+ const parts = await loadPublicRoleDispatchParts(env, home);
344
345
  const seat = resolveEffectiveSeat(
345
- config,
346
+ parts.config,
346
347
  role,
347
- credentials,
348
+ parts.credentials,
348
349
  invocationFromParsed(parsed),
349
350
  );
350
351
  let parsedRoleArgv!: TParsed;
@@ -352,7 +353,15 @@ async function dispatchPublicRoleCommand<TParsed>(
352
353
  parsed.args,
353
354
  createRoleEnvironment(
354
355
  env,
355
- { role, home, agentDir, cwd, credentials, seat, config },
356
+ {
357
+ role,
358
+ home,
359
+ agentDir: parts.agentDir,
360
+ cwd: parts.cwd,
361
+ credentials: parts.credentials,
362
+ seat,
363
+ config: parts.config,
364
+ },
356
365
  () => {
357
366
  parsedRoleArgv = parse(parsed.args);
358
367
  },
@@ -397,14 +406,6 @@ function resolveHome(env: CliEnv): string {
397
406
  return env.home ?? packageMachineHome();
398
407
  }
399
408
 
400
- function resolveAgentDir(env: CliEnv, home: string): string {
401
- return (
402
- env.agentDir ??
403
- process.env.PI_CODING_AGENT_DIR ??
404
- join(home, ".pi", "agent")
405
- );
406
- }
407
-
408
409
  type ParsedGlobal = {
409
410
  command?: string;
410
411
  args: string[];
@@ -575,20 +576,6 @@ function requireCallableSeat(
575
576
  }
576
577
  }
577
578
 
578
- /** Single seat engine axis → run-options projection (#391 E2 / #883). */
579
- function projectSeatEngine(
580
- seat: Readonly<{ engine?: string; engineModel?: string }>,
581
- ): { engine?: string; engineModel?: string } {
582
- return pickEngineAxis(seat);
583
- }
584
-
585
- /** Single seat.host → run-options projection (#595 / #617). */
586
- function projectSeatHost(
587
- seat: Readonly<{ host: string }>,
588
- ): { host: string } {
589
- return { host: seat.host };
590
- }
591
-
592
579
  function loadAndValidateConfig(
593
580
  home: string,
594
581
  packageRoot: string,
@@ -1170,7 +1157,7 @@ export async function runAkRole(
1170
1157
  const config = await loadAndValidateConfig(home, env.packageRoot);
1171
1158
  const credentials =
1172
1159
  env.credentials ??
1173
- (await loadCredentialProviders(resolveAgentDir(env, home)));
1160
+ (await loadCredentialProviders(resolvePublicAgentDir(env.agentDir, home)));
1174
1161
  const seats = effectiveSeatConfigurations(
1175
1162
  config,
1176
1163
  credentials,
@@ -1219,19 +1206,15 @@ export async function runAkRole(
1219
1206
  runId: resumeRequest.runId,
1220
1207
  authority: env.principalAuthority ?? piDurablePrincipalAuthority,
1221
1208
  };
1222
- const agentDir = resolveAgentDir(env, home);
1223
- const cwd = env.cwd ?? process.cwd();
1224
- const config = await loadAndValidateConfig(home, env.packageRoot);
1225
- const credentials =
1226
- env.credentials ?? (await loadCredentialProviders(agentDir));
1209
+ const parts = await loadPublicRoleDispatchParts(env, home);
1227
1210
  const resumeRole = await peekRoleRunRole(home, resumeRequest.runId);
1228
1211
  // Missing durable role keeps the judge seat table. The resume entry
1229
1212
  // itself is one function; it reads the stored run.
1230
1213
  const seatRole = resumeRole ?? "judge";
1231
1214
  const seat = resolveEffectiveSeat(
1232
- config,
1215
+ parts.config,
1233
1216
  seatRole,
1234
- credentials,
1217
+ parts.credentials,
1235
1218
  invocationFromParsed(parsed),
1236
1219
  );
1237
1220
  const result = await runPublicInstructionSeatResume(
@@ -1239,11 +1222,11 @@ export async function runAkRole(
1239
1222
  createRoleEnvironment(env, {
1240
1223
  role: seatRole,
1241
1224
  home,
1242
- agentDir,
1243
- cwd,
1244
- credentials,
1225
+ agentDir: parts.agentDir,
1226
+ cwd: parts.cwd,
1227
+ credentials: parts.credentials,
1245
1228
  seat,
1246
- config,
1229
+ config: parts.config,
1247
1230
  }),
1248
1231
  io,
1249
1232
  );
@@ -1261,9 +1244,20 @@ export async function runAkRole(
1261
1244
  };
1262
1245
  const parentRole = await peekRoleRunRole(home, parentRunId);
1263
1246
  if (parentRole === undefined) break;
1264
- const parentSeat = resolveEffectiveSeat(config, parentRole, credentials, invocationFromParsed(parsed));
1247
+ const parentSeat = resolveEffectiveSeat(
1248
+ parts.config,
1249
+ parentRole,
1250
+ parts.credentials,
1251
+ invocationFromParsed(parsed),
1252
+ );
1265
1253
  const parentEnv = createRoleEnvironment(env, {
1266
- role: parentRole, home, agentDir, cwd, credentials, seat: parentSeat, config,
1254
+ role: parentRole,
1255
+ home,
1256
+ agentDir: parts.agentDir,
1257
+ cwd: parts.cwd,
1258
+ credentials: parts.credentials,
1259
+ seat: parentSeat,
1260
+ config: parts.config,
1267
1261
  });
1268
1262
  current = await continueParentAfterChild(parentRunId, current.admitted, parentEnv, io);
1269
1263
  }
@@ -835,6 +835,14 @@ export function credentialProvidersFromAuthData(
835
835
  };
836
836
  }
837
837
 
838
+ /**
839
+ * Public entry agent directory: explicit value, else PI_CODING_AGENT_DIR,
840
+ * else `<home>/.pi/agent`. CLI and nested summons share this rule.
841
+ */
842
+ export function resolvePublicAgentDir(explicit: string | undefined, home: string): string {
843
+ return explicit ?? process.env.PI_CODING_AGENT_DIR ?? join(home, ".pi", "agent");
844
+ }
845
+
838
846
  export async function loadCredentialProviders(
839
847
  agentDir: string,
840
848
  ): Promise<CredentialProviders> {
@@ -3,7 +3,8 @@
3
3
  * Seat differences are composition-root fields. An omitted reviewer lens
4
4
  * starts two ordinary single-axis runs here.
5
5
  */
6
- import { dirname, resolve, sep } from "node:path";
6
+ import { dirname, resolve } from "node:path";
7
+ import { isUnboundRunDirectory } from "../role-run-placement.ts";
7
8
  import { AUDITOR_DOSSIER_PROMPT } from "../compliance-transport.ts";
8
9
  import { bookDirectOfficerRunPointer } from "../archivist-record-pointer.ts";
9
10
  import { createPiDoctorAuditor } from "../doctor-auditor.ts";
@@ -24,6 +25,7 @@ import {
24
25
  latestQueueStatus,
25
26
  } from "../submission-gate.ts";
26
27
  import { isSafePositiveTicketNumber, readBoardTicketNumber } from "../run-ticket-number.ts";
28
+ import type { NotarySourceRunLocator } from "../notary-contracts.ts";
27
29
  import { NotarySourceRunError, resolveNotarySourceRunLocator } from "../notary-source-run.ts";
28
30
  import { engineSessionMaterialFromOptions, pickEngineAxis } from "../package-resources/engine-material.ts";
29
31
  import type { PackagedRole } from "../packaged-role-registry.ts";
@@ -33,7 +35,7 @@ import {
33
35
  packagedRebindSourceOnResume,
34
36
  packagedRoleMetadata,
35
37
  } from "../packaged-role-registry.ts";
36
- import { isAuditorSoulRole, readAuditorResumeBinding } from "../auditor-soul.ts";
38
+ import { readAuditorResumeBinding } from "../auditor-soul.ts";
37
39
  import { CliUsageError } from "./cli-errors.ts";
38
40
  import {
39
41
  admitPublicRole,
@@ -89,6 +91,7 @@ import {
89
91
  } from "./terminal.ts";
90
92
  import {
91
93
  admittedSeatTurnDetails,
94
+ projectPublicTurnAxes,
92
95
  projectRoleTurnRequest,
93
96
  requiredMethodSkills,
94
97
  type RoleTurnRequestProjectionOptions,
@@ -455,6 +458,45 @@ async function runOmittedLensBatch(
455
458
  return { exitCode: failed ? 1 : 0, terminal };
456
459
  }
457
460
 
461
+ /**
462
+ * One same-parent lookup. Seat branches still build their own summons.
463
+ * A found child resumes that child; no prior run returns undefined so the caller mints.
464
+ */
465
+ function resumeSameParentInstructionSeat(input: {
466
+ readonly env: InstructionSeatRunEnv;
467
+ readonly io: CliIo;
468
+ readonly projectRoot: string;
469
+ readonly role: PackagedRole;
470
+ readonly parentRunPath: string;
471
+ readonly summons: SameTicketSummonsMaterials;
472
+ readonly ticketNumber?: number;
473
+ }): Promise<SeatRunResult | undefined> {
474
+ return tryResumeSameTicketSeatRun({
475
+ home: input.env.home,
476
+ projectRoot: input.projectRoot,
477
+ role: input.role,
478
+ parentRunPath: input.parentRunPath,
479
+ ...(input.ticketNumber === undefined ? {} : { ticketNumber: input.ticketNumber }),
480
+ freshSummons: input.env.freshSummons,
481
+ summons: input.summons,
482
+ resume: (runId, materials) => runPublicInstructionSeatResume(
483
+ { runId, ...(materials === undefined ? {} : { summons: materials }) },
484
+ input.env,
485
+ input.io,
486
+ ),
487
+ });
488
+ }
489
+
490
+ /** Re-ask words when the parent supplied them; otherwise the caller's own instruction. */
491
+ function sameParentInstruction(
492
+ env: InstructionSeatRunEnv,
493
+ fallback: { readonly instruction: string; readonly instructionEmpty: boolean },
494
+ ): { readonly instruction: string; readonly instructionEmpty: boolean } {
495
+ const resumeInstruction = env.reviewReask ?? env.gateReviewInstruction;
496
+ if (resumeInstruction === undefined) return fallback;
497
+ return { instruction: resumeInstruction, instructionEmpty: false };
498
+ }
499
+
458
500
  /**
459
501
  * Countersign on the shared entry: gate parent resume, deferred materialization,
460
502
  * then the same one-shot settlement as every other seat.
@@ -470,23 +512,20 @@ async function runCountersignBody(
470
512
  : undefined;
471
513
  if (gateParentRunPath !== undefined) {
472
514
  const resumeInstruction = env.reviewReask ?? env.gateReviewInstruction ?? parsed.instruction ?? "";
473
- const resumed = await tryResumeSameTicketSeatRun({
474
- home: env.home,
515
+ const resumed = await resumeSameParentInstructionSeat({
516
+ env,
517
+ io,
475
518
  projectRoot: resolve(parsed.project ?? env.cwd),
476
519
  role: "countersign",
477
520
  parentRunPath: gateParentRunPath,
478
- ...(isSafePositiveTicketNumber(env.boundTicketNumber) ? { ticketNumber: env.boundTicketNumber } : {}),
479
- freshSummons: env.freshSummons,
521
+ ...(isSafePositiveTicketNumber(env.boundTicketNumber)
522
+ ? { ticketNumber: env.boundTicketNumber }
523
+ : {}),
480
524
  summons: {
481
525
  sourceRunPath: gateParentRunPath,
482
526
  instruction: resumeInstruction,
483
527
  instructionEmpty: resumeInstruction.trim() === "",
484
528
  },
485
- resume: (runId, materials) => runPublicInstructionSeatResume(
486
- { runId, ...(materials === undefined ? {} : { summons: materials }) },
487
- env,
488
- io,
489
- ),
490
529
  });
491
530
  if (resumed != null) return resumed;
492
531
  }
@@ -521,12 +560,7 @@ async function runCountersignBody(
521
560
  admitted = { ...admitted, sourceRunPath: gateParentRunPath };
522
561
  }
523
562
  const turnProjection: RoleTurnRequestProjectionOptions = {
524
- packageRoot: env.packageRoot,
525
- home: env.home,
526
- agentDir: env.agentDir,
527
- ...(env.model === undefined ? {} : { model: env.model }),
528
- ...pickEngineAxis(env),
529
- ...(env.timeoutMs === undefined ? {} : { timeoutMs: env.timeoutMs }),
563
+ ...projectPublicTurnAxes(env),
530
564
  ...(env.correlationId === undefined || env.correlationId.trim() === ""
531
565
  ? {}
532
566
  : { correlationId: env.correlationId }),
@@ -608,17 +642,18 @@ export async function runPublicInstructionSeat(
608
642
  let auditorSubject: "judge" | "doctor" | undefined;
609
643
  let auditorSource: string | undefined;
610
644
  let auditorTicket: number | undefined;
645
+ let resolvedNotarySource: NotarySourceRunLocator | undefined;
611
646
  if (record.sameParent === "subject-source") {
612
- if (!isAuditorSoulRole(parsed.subject)) {
647
+ if (parsed.subject === undefined) {
613
648
  presentStructuralRejection(new CliUsageError("auditor --subject requires judge|doctor"), io);
614
649
  return { exitCode: 2 };
615
650
  }
616
651
  auditorSubject = parsed.subject;
617
- const source = typeof parsed.sourceRun === "string" ? parsed.sourceRun.trim() : "";
618
- if (source === "") {
652
+ if (parsed.sourceRun === undefined) {
619
653
  presentStructuralRejection(new CliUsageError("auditor --source-run requires a run locator"), io);
620
654
  return { exitCode: 2 };
621
655
  }
656
+ const source = parsed.sourceRun;
622
657
  try {
623
658
  const resolved = await resolveNotarySourceRunLocator({ projectRoot, sourceRun: source, home: env.home });
624
659
  auditorSource = resolved.runDirectory;
@@ -629,28 +664,23 @@ export async function runPublicInstructionSeat(
629
664
  }
630
665
  if (auditorSource === undefined) return { exitCode: 2 };
631
666
  const sourceDirectory = auditorSource;
632
- const resumeInstruction = env.reviewReask ?? env.gateReviewInstruction;
633
667
  const summons: SameTicketSummonsMaterials = {
634
- ...(resumeInstruction === undefined
635
- ? { instruction: parsed.instruction ?? "", instructionEmpty: (parsed.instruction ?? "").trim() === "" }
636
- : { instruction: resumeInstruction, instructionEmpty: false }),
668
+ ...sameParentInstruction(env, {
669
+ instruction: parsed.instruction ?? "",
670
+ instructionEmpty: (parsed.instruction ?? "").trim() === "",
671
+ }),
637
672
  attachmentPaths: parsed.attachmentPaths ?? [],
638
673
  };
639
674
  const resumed = await withAuditorSoulEnv({
640
675
  subject: auditorSubject,
641
676
  sourceRunDirectory: auditorSource,
642
- run: () => tryResumeSameTicketSeatRun({
643
- home: env.home,
677
+ run: () => resumeSameParentInstructionSeat({
678
+ env,
679
+ io,
644
680
  projectRoot,
645
681
  role,
646
682
  parentRunPath: sourceDirectory,
647
- freshSummons: env.freshSummons,
648
683
  summons,
649
- resume: (runId, materials) => runPublicInstructionSeatResume(
650
- { runId, ...(materials === undefined ? {} : { summons: materials }) },
651
- env,
652
- io,
653
- ),
654
684
  }),
655
685
  });
656
686
  if (resumed != null) return resumed;
@@ -659,26 +689,21 @@ export async function runPublicInstructionSeat(
659
689
  if (record.sameParent === "gate-pointer") {
660
690
  const parentRunPath = parentRunPathFromGatePointerInstruction(parsed.instruction ?? "");
661
691
  if (parentRunPath !== undefined) {
662
- const resumeInstruction = env.reviewReask ?? env.gateReviewInstruction;
663
692
  const summons: SameTicketSummonsMaterials = {
664
693
  sourceRunPath: parentRunPath,
665
- ...(resumeInstruction === undefined
666
- ? { instruction: parsed.instruction ?? "", instructionEmpty: (parsed.instruction ?? "").trim() === "" }
667
- : { instruction: resumeInstruction, instructionEmpty: false }),
694
+ ...sameParentInstruction(env, {
695
+ instruction: parsed.instruction ?? "",
696
+ instructionEmpty: (parsed.instruction ?? "").trim() === "",
697
+ }),
668
698
  attachmentPaths: parsed.attachmentPaths ?? [],
669
699
  };
670
- const resumed = await tryResumeSameTicketSeatRun({
671
- home: env.home,
700
+ const resumed = await resumeSameParentInstructionSeat({
701
+ env,
702
+ io,
672
703
  projectRoot,
673
704
  role,
674
705
  parentRunPath,
675
- freshSummons: env.freshSummons,
676
706
  summons,
677
- resume: (runId, materials) => runPublicInstructionSeatResume(
678
- { runId, ...(materials === undefined ? {} : { summons: materials }) },
679
- env,
680
- io,
681
- ),
682
707
  });
683
708
  if (resumed != null) return resumed;
684
709
  }
@@ -703,31 +728,33 @@ export async function runPublicInstructionSeat(
703
728
  const summons: SameTicketSummonsMaterials = {
704
729
  sourceRunPath: source.runDirectory,
705
730
  sourceRun: source,
706
- ...(resumeInstruction === undefined ? {} : { instruction: resumeInstruction, instructionEmpty: false }),
731
+ ...(resumeInstruction === undefined
732
+ ? {}
733
+ : { instruction: resumeInstruction, instructionEmpty: false }),
707
734
  };
708
- const resumed = await tryResumeSameTicketSeatRun({
709
- home: env.home,
735
+ resolvedNotarySource = source;
736
+ const resumed = await resumeSameParentInstructionSeat({
737
+ env,
738
+ io,
710
739
  projectRoot,
711
740
  role,
712
741
  parentRunPath: source.runDirectory,
713
- freshSummons: env.freshSummons,
714
742
  summons,
715
- resume: (runId, materials) => runPublicInstructionSeatResume(
716
- { runId, ...(materials === undefined ? {} : { summons: materials }) },
717
- env,
718
- io,
719
- ),
720
743
  });
721
744
  if (resumed != null) return resumed;
722
745
  }
723
746
 
724
747
  let admitted: AdmittedRoleInvocation;
725
748
  try {
749
+ const admissionOverride = {
750
+ ...(auditorTicket === undefined ? {} : { assertedTicketNumber: auditorTicket }),
751
+ ...(resolvedNotarySource === undefined ? {} : { resolvedSourceRun: resolvedNotarySource }),
752
+ };
726
753
  admitted = await admitPublicRole(
727
754
  role,
728
755
  parsed,
729
756
  env,
730
- auditorTicket === undefined ? undefined : { assertedTicketNumber: auditorTicket },
757
+ auditorTicket === undefined && resolvedNotarySource === undefined ? undefined : admissionOverride,
731
758
  );
732
759
  } catch (error) {
733
760
  const rejected = usageExit(error, io);
@@ -764,7 +791,7 @@ export async function runPublicInstructionSeat(
764
791
  timedOut: false, code: null, stderr: "", thrown: error,
765
792
  }, seatAdapters(admitted, env), env.principalAuthority, io) as SeatRunResult;
766
793
  }
767
- if (outcome.admitted?.runDirectory.includes(`${sep}unbound${sep}runs${sep}`)) {
794
+ if (outcome.admitted !== undefined && isUnboundRunDirectory(outcome.admitted.runDirectory)) {
768
795
  await recordChildDiaristRun(admitted, outcome.admitted.runId);
769
796
  }
770
797
  if (outcome.failedWithoutEscalate !== undefined) {