@akagilnc/pi-workflow-roles 0.1.5593 → 0.1.5614

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 (58) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/acp-host/production-host.js +521 -707
  4. package/dist/compliance-transport.js +2 -5
  5. package/dist/doctor-auditor.js +1 -1
  6. package/dist/gatekeeper-role.js +1 -1
  7. package/dist/headless-host/production-host.js +505 -691
  8. package/dist/merger-contracts.js +6 -2
  9. package/dist/migrate-book-topology.js +5142 -10735
  10. package/dist/navigator-public-session.js +4 -4
  11. package/dist/navigator-work-base.js +5 -30
  12. package/dist/package-contracts/worker-output.js +1 -6
  13. package/dist/packaged-role-registry.js +9 -6
  14. package/dist/pi/role-turn-host.js +10 -1
  15. package/dist/public-cli/instruction-seat-run.js +133 -38
  16. package/dist/public-cli/invocation.js +38 -74
  17. package/dist/public-cli/main.js +18155 -18235
  18. package/dist/public-cli/option-definitions.js +21 -2
  19. package/dist/public-cli/post-admission.js +25 -24
  20. package/dist/public-cli/run-lifecycle.js +4 -43
  21. package/dist/public-cli/turn-request.js +4 -9
  22. package/dist/public-role-summons.js +5 -3
  23. package/dist/role-run-placement.js +7 -1
  24. package/dist/role-run-relocation.js +3 -4
  25. package/package.json +1 -1
  26. package/scripts/build-package.mjs +2 -4
  27. package/src/audited-run-identity.ts +30 -0
  28. package/src/compliance-transport.ts +1 -4
  29. package/src/doctor-auditor.ts +1 -1
  30. package/src/gatekeeper-role.ts +1 -1
  31. package/src/host-contracts.ts +6 -4
  32. package/src/merger-contracts.ts +6 -2
  33. package/src/merger-role.ts +11 -1
  34. package/src/navigator-attendance.ts +2 -2
  35. package/src/navigator-public-session.ts +9 -5
  36. package/src/navigator-work-base.ts +19 -34
  37. package/src/notary-role.ts +12 -21
  38. package/src/package-contracts/worker-output.ts +0 -8
  39. package/src/packaged-role-registry.ts +8 -7
  40. package/src/pi/role-turn-host.ts +11 -1
  41. package/src/public-cli/instruction-seat-run.ts +162 -42
  42. package/src/public-cli/invocation.ts +55 -93
  43. package/src/public-cli/option-definitions.ts +22 -2
  44. package/src/public-cli/post-admission.ts +30 -24
  45. package/src/public-cli/run-lifecycle.ts +4 -56
  46. package/src/public-cli/turn-request.ts +9 -8
  47. package/src/public-role-summons.ts +17 -6
  48. package/src/role-envelope.ts +4 -0
  49. package/src/role-run-placement.ts +10 -1
  50. package/src/role-run-relocation.ts +3 -4
  51. package/src/role-runtime-dependencies.ts +0 -2
  52. package/src/role-runtime.ts +54 -47
  53. package/src/worker-role.ts +11 -78
  54. package/dist/auditor-dossier-tool.js +0 -47
  55. package/dist/package-contracts/fixer-packet.js +0 -77
  56. package/packets/fixer-prerequisites.json +0 -6
  57. package/src/auditor-dossier-tool.ts +0 -63
  58. package/src/package-contracts/fixer-packet.ts +0 -91
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Durable work subject and controlling authority for one navigator nest.
3
- * Settlement user turns keep a path pointer; the navigator system prompt
4
- * loads the bytes once per agent start from this file (base material).
3
+ * Startup materials load these bytes from the nest session directory; the
4
+ * caller/summon prompt must not receive a package-injected path (#1166).
5
5
  */
6
6
  import { existsSync, realpathSync } from "node:fs";
7
7
  import { readFile } from "node:fs/promises";
@@ -12,6 +12,9 @@ import { isEnoent, isRecord } from "./unknown-value.ts";
12
12
 
13
13
  export const NAVIGATOR_WORK_CONTEXT_BASENAME = "work-context.json";
14
14
 
15
+ /** Child-process / in-process locator for nest work-context bytes — not prompt material (#1166). */
16
+ export const AK_ROLE_NAVIGATOR_WORK_CONTEXT_ENV = "AK_ROLE_NAVIGATOR_WORK_CONTEXT" as const;
17
+
15
18
  export type NavigatorWorkBase = {
16
19
  readonly subject: string;
17
20
  readonly authority: string;
@@ -81,38 +84,20 @@ export async function readNavigatorWorkBase(path: string): Promise<NavigatorWork
81
84
  return { subject: record.subject, authority: record.authority };
82
85
  }
83
86
 
84
- function jsonObjectFromPrompt(prompt: string): Record<string, unknown> | undefined {
85
- const head = prompt.trimStart().split("\n\n", 1)[0] ?? "";
86
- if (!head.startsWith("{")) return undefined;
87
- try {
88
- const parsed: unknown = JSON.parse(head);
89
- if (!isRecord(parsed)) return undefined;
90
- return parsed as Record<string, unknown>;
91
- } catch {
92
- return undefined;
93
- }
94
- }
95
-
96
- export function workContextPathFromPrompt(prompt: string): string | undefined {
97
- const record = jsonObjectFromPrompt(prompt);
98
- const path = record?.workContextPath;
99
- if (typeof path !== "string" || path.trim() === "") return undefined;
100
- return isNavigatorWorkContextFile(path) ? path : undefined;
101
- }
102
-
103
- /** Settlement JSON gains the nest pointer. Non-JSON prompts stay unchanged. */
104
- export function attachWorkContextPointer(text: string, workContextPath: string): string {
105
- const record = jsonObjectFromPrompt(text);
106
- if (record === undefined) return text;
107
- if (typeof record.workContextPath === "string" && record.workContextPath.trim() !== "") return text;
108
- const head = text.trimStart().split("\n\n", 1)[0] ?? "";
109
- const rest = text.trimStart().slice(head.length);
110
- return `${JSON.stringify({ ...record, workContextPath })}${rest}`;
111
- }
112
-
113
- /** System-prompt base for one agent start. Absent or unreadable file yields undefined. */
114
- export async function loadNavigatorWorkBaseSuffix(prompt: string): Promise<string | undefined> {
115
- const path = workContextPathFromPrompt(prompt);
87
+ /** System-prompt base for one agent start. Prefer request locator, else nest session dir. */
88
+ export async function loadNavigatorWorkBaseSuffix(
89
+ sessionDir: string | undefined,
90
+ locatorPath?: string,
91
+ ): Promise<string | undefined> {
92
+ const path =
93
+ typeof locatorPath === "string" && locatorPath.trim() !== ""
94
+ ? locatorPath.trim()
95
+ : typeof process.env[AK_ROLE_NAVIGATOR_WORK_CONTEXT_ENV] === "string"
96
+ && process.env[AK_ROLE_NAVIGATOR_WORK_CONTEXT_ENV].trim() !== ""
97
+ ? process.env[AK_ROLE_NAVIGATOR_WORK_CONTEXT_ENV].trim()
98
+ : sessionDir !== undefined && sessionDir.trim() !== ""
99
+ ? navigatorWorkContextFile(sessionDir)
100
+ : undefined;
116
101
  if (path === undefined) return undefined;
117
102
  const body = await readNavigatorWorkBase(path);
118
103
  if (body === undefined) return undefined;
@@ -9,6 +9,10 @@ import { isSafePositiveTicketNumber } from "./run-ticket-number.ts";
9
9
  * This module keeps evidence assembly + projection only.
10
10
  */
11
11
 
12
+ import {
13
+ auditedRunIdentityFromParts,
14
+ auditedRunIdentityMaterial,
15
+ } from "./audited-run-identity.ts";
12
16
  import {
13
17
  NOTARY_OUTPUT_TOOL_NAME,
14
18
  NOTARY_SOURCE_RUN_FLAG,
@@ -53,25 +57,15 @@ export function readNotaryTicketFlag(flag: unknown): number | undefined {
53
57
  return n;
54
58
  }
55
59
 
56
- /**
57
- * Typed session bound (locator + optional ticket).
58
- * Sole production projection for agent-start material + session custom entry.
59
- */
60
- export function projectNotarySessionBound(input: {
61
- readonly sourceRun: NotarySourceRunLocator;
62
- readonly ticketNumber?: number;
63
- }): {
60
+ /** Agent-start reading material: identity `<runId>@<席>` only — no directory path. */
61
+ export function projectNotaryAuditedRunIdentity(input: {
64
62
  readonly sourceRun: NotarySourceRunLocator;
65
- readonly ticketNumber?: number;
66
- } {
67
- return {
68
- sourceRun: input.sourceRun,
69
- ...(input.ticketNumber === undefined ? {} : { ticketNumber: input.ticketNumber }),
70
- };
63
+ }) {
64
+ return auditedRunIdentityMaterial(
65
+ auditedRunIdentityFromParts(input.sourceRun.runId, input.sourceRun.role),
66
+ );
71
67
  }
72
68
 
73
- export type NotarySessionBound = ReturnType<typeof projectNotarySessionBound>;
74
-
75
69
  /**
76
70
  * Session custom-entry type for typed notary bound (written by shared envelope).
77
71
  * Role module only projects; lifecycle write is envelope-owned (ADR 0018).
@@ -102,7 +96,7 @@ export function projectNotaryBoundFromFlags(
102
96
  }
103
97
 
104
98
  /** Assemble systemPrompt body from base + soul only (NO embedded bound JSON).
105
- * The typed session bound travels separately as `readingMaterial`; adapters fold
99
+ * Audited-run identity travels separately as `readingMaterial`; adapters fold
106
100
  * it into the provider-visible prompt at the send boundary. */
107
101
  export function assembleNotaryAgentStartPrompt(input: {
108
102
  readonly baseSystemPrompt: string;
@@ -172,11 +166,8 @@ export function createNotaryRoleRuntime(
172
166
  baseSystemPrompt: event.systemPrompt,
173
167
  soul: activation.soul,
174
168
  }),
175
- readingMaterial: projectNotarySessionBound({
169
+ readingMaterial: projectNotaryAuditedRunIdentity({
176
170
  sourceRun: activation.sourceRun,
177
- ...(activation.ticketNumber === undefined
178
- ? {}
179
- : { ticketNumber: activation.ticketNumber }),
180
171
  }),
181
172
  };
182
173
  });
@@ -13,26 +13,18 @@ import {
13
13
  type FixerOutput,
14
14
  type FixerPhase,
15
15
  } from "./fixer-output.ts";
16
- import { fixerPrerequisiteSchema, fixerPrerequisitesSchema, parseFixerPrerequisites, validateFixerPrerequisites } from "./fixer-packet.ts";
17
- import type { FixerInvocationInput, FixerPrerequisite } from "./fixer-packet.ts";
18
16
  import { withTerminatingOutputDeclarations } from "./terminating-infrastructure.ts";
19
17
 
20
18
  export {
21
19
  FIXER_OUTPUT_TOOL_NAME,
22
20
  fixerOutputSchema,
23
21
  validateFixerOutput,
24
- fixerPrerequisiteSchema,
25
- fixerPrerequisitesSchema,
26
- parseFixerPrerequisites,
27
- validateFixerPrerequisites,
28
22
  };
29
23
  export type {
30
24
  FixerBlocker,
31
25
  FixerClassResult,
32
26
  FixerOutput,
33
27
  FixerPhase,
34
- FixerInvocationInput,
35
- FixerPrerequisite,
36
28
  };
37
29
 
38
30
  export const CODER_OUTPUT_TOOL_NAME = "ak_coder_output";
@@ -115,10 +115,10 @@ export const PUBLIC_ROLE_RECORDS = [
115
115
  reportPhase: true,
116
116
  },
117
117
  boardPlacement: "marshal",
118
+ /** #1168: dispatch is first message; navigator reads admitted instruction. */
119
+ navigatorSubject: "public-instruction",
118
120
  activationFlags: [
119
- { field: "packetPath", flag: "ak-fix-packet", binds: "input" },
120
121
  { field: "phase", flag: "ak-fixer-phase", binds: "phase" },
121
- { field: "prerequisitesPath", flag: "ak-fixer-prerequisites" },
122
122
  ],
123
123
  activationStage: "load-and-install",
124
124
  sessionMaterials: [
@@ -145,8 +145,9 @@ export const PUBLIC_ROLE_RECORDS = [
145
145
  artifactFace: {
146
146
  reportPhase: true,
147
147
  },
148
+ /** #1168: dispatch is first message; navigator reads admitted instruction. */
149
+ navigatorSubject: "public-instruction",
148
150
  activationFlags: [
149
- { field: "taskPath", flag: "ak-coder-task", binds: "input" },
150
151
  { field: "phase", flag: "ak-coder-phase", binds: "phase" },
151
152
  ],
152
153
  activationStage: "load-and-install",
@@ -389,7 +390,8 @@ export const PUBLIC_ROLE_RECORDS = [
389
390
  presentSettled: "always",
390
391
  summonResume: false,
391
392
  admission: "instruction",
392
- sameParent: "gate-pointer",
393
+ /** Structured --source-run only; instruction stays opaque caller/peer bytes (#1166). */
394
+ sameParent: "source-run",
393
395
  /** Resume restores an optional source-run path onto the admitted face. */
394
396
  resumeSourcePath: true,
395
397
  /** 台院 is a review officer and inherits the gatekeeper model. */
@@ -402,11 +404,12 @@ export const PUBLIC_ROLE_RECORDS = [
402
404
  /** Court reask replaces the initial prompt. */
403
405
  reaskPrompt: true,
404
406
  gateStageLabel: "台院",
407
+ /** Gate summon binds --source-run; identity is startup material (#1166). */
408
+ gateSummon: "source-run",
405
409
  activationFlags: [
406
410
  {
407
411
  field: "sourceRun",
408
412
  from: "sourceRunPath",
409
- fallback: "gate-pointer",
410
413
  flag: "ak-inspector-source-run",
411
414
  binds: "input",
412
415
  },
@@ -553,7 +556,6 @@ export function packagedModelParent(role: string): PackagedRole | undefined {
553
556
  * One seat's admitted → activation copy.
554
557
  * `field` is the activation key. `from` is the admitted path when it differs.
555
558
  * `text` writes a number as decimal text on the activation object.
556
- * `fallback: "gate-pointer"` is the inspector instruction pointer when sourceRunPath is blank.
557
559
  * `flag` omitted: the value stays on the activation object and is not a host flag
558
560
  * (secretariat and countersign ticketNumber).
559
561
  */
@@ -561,7 +563,6 @@ export type PackagedActivationFlag = {
561
563
  readonly field: string;
562
564
  readonly from?: string;
563
565
  readonly text?: true;
564
- readonly fallback?: "gate-pointer";
565
566
  readonly flag?: string;
566
567
  readonly binds?: "input" | "phase";
567
568
  };
@@ -429,7 +429,7 @@ export function createPiRoleTurnHost(config: PiRoleTurnHostConfig): RoleTurnHost
429
429
  if (request.deliveryRequestLimit === undefined) delete env[RECEIPT_DELIVERY_LIMIT_ENV];
430
430
  else env[RECEIPT_DELIVERY_LIMIT_ENV] = String(request.deliveryRequestLimit);
431
431
  applyEngineChildEnv(env, request.engine);
432
- // Nested auditor dossier tool binds the parent run pointer when published.
432
+ // Nested auditor binds the audited source-run for identity / soul selection.
433
433
  if (
434
434
  typeof process.env.AK_ROLE_AUDITOR_SOURCE_RUN === "string"
435
435
  && process.env.AK_ROLE_AUDITOR_SOURCE_RUN.trim() !== ""
@@ -443,6 +443,16 @@ export function createPiRoleTurnHost(config: PiRoleTurnHostConfig): RoleTurnHost
443
443
  ) {
444
444
  env.AK_ROLE_AUDITOR_SUBJECT = process.env.AK_ROLE_AUDITOR_SUBJECT;
445
445
  }
446
+ // Navigator nest work-context locator for startup materials (#1166) —
447
+ // child-process scoped only from this turn request; never parent process.env.
448
+ if (
449
+ typeof request.navigatorWorkContextPath === "string"
450
+ && request.navigatorWorkContextPath.trim() !== ""
451
+ ) {
452
+ env.AK_ROLE_NAVIGATOR_WORK_CONTEXT = request.navigatorWorkContextPath.trim();
453
+ } else {
454
+ delete env.AK_ROLE_NAVIGATOR_WORK_CONTEXT;
455
+ }
446
456
  if (
447
457
  config.recordLaunchedRolePackageIdentity !== undefined &&
448
458
  config.observeLaunchedRolePackageIdentity !== undefined
@@ -4,8 +4,7 @@
4
4
  * starts two ordinary single-axis runs here.
5
5
  */
6
6
  import { dirname, resolve } from "node:path";
7
- import { isUnboundRunDirectory } from "../role-run-placement.ts";
8
- import { AUDITOR_DOSSIER_PROMPT } from "../compliance-transport.ts";
7
+ import { isUnboundRunDirectory, sessionFileOf } from "../role-run-placement.ts";
9
8
  import { bookDirectOfficerRunPointer, readDirectOfficerRunPointer } from "../archivist-record-pointer.ts";
10
9
  import { createPiDoctorAuditor } from "../doctor-auditor.ts";
11
10
 
@@ -42,6 +41,7 @@ import { CliUsageError } from "./cli-errors.ts";
42
41
  import {
43
42
  admitPublicRole,
44
43
  bindAdmittedTicketNumber,
44
+ appendCallerFileFlagPaths,
45
45
  buildInstructionTransportPrompt,
46
46
  materializeCountersignInvocation,
47
47
  persistAdmittedSourceRunPath,
@@ -72,7 +72,6 @@ import {
72
72
  buildAutoResumeContinuationPrompt,
73
73
  loadResumablePublicRole,
74
74
  markRunAdmitted,
75
- parentRunPathFromGatePointerInstruction,
76
75
  readCurrentCourt,
77
76
  type PublicResumeRequest,
78
77
  type RunWriterLease,
@@ -291,14 +290,80 @@ export function buildInstructionSeatTurnRequest(
291
290
  );
292
291
  }
293
292
 
294
- function initialPrompt(
293
+ /**
294
+ * Direct review-seat call: reconnect ledger peer-body delivery (ADR 0085 / #1166).
295
+ * Gate summons already ride gateReviewInstruction; direct --source-run must not
296
+ * leave a blank fixed-kickoff.
297
+ */
298
+ async function loadLedgerPeerBody(
299
+ sourceRunDirectory: string,
300
+ projectRoot: string,
301
+ home: string,
302
+ ): Promise<string | undefined> {
303
+ const runId = runIdFromRunDirectory(sourceRunDirectory);
304
+ if (runId === undefined) return undefined;
305
+ // Bind to the already-resolved source directory — never re-discover by runId
306
+ // alone (another book may lawfully share the same id; #637 / #1166).
307
+ const rows = await readRecordedSubmissionRows(projectRoot, runId, {
308
+ home,
309
+ sessionParent: sessionFileOf(sourceRunDirectory),
310
+ });
311
+ // ADR 0085 / #1166 J10: ledger already keeps original words for every class —
312
+ // present the latest row as-is. Never filter current manuscript by accepted /
313
+ // terminal outcome kind (that resurrects a prior sealed draft).
314
+ const latest = rows.at(-1);
315
+ if (latest === undefined) return undefined;
316
+ return readableGateItem(latest.accepted);
317
+ }
318
+
319
+ /**
320
+ * One delivery rule for review-seat dialogue on new turns and same-parent resume
321
+ * (#1166 / ADR 0085 / ADR 0087): explicit reask → non-empty caller dispatch →
322
+ * source-only ledger peer body. Identity stays in startup materials.
323
+ */
324
+ async function resolveReviewSeatDialogueBody(input: {
325
+ readonly reask?: string;
326
+ readonly callerInstruction?: string;
327
+ readonly sourceRunPath?: string;
328
+ readonly projectRoot: string;
329
+ readonly home: string;
330
+ }): Promise<string | undefined> {
331
+ const reask = input.reask;
332
+ if (reask !== undefined && reask.length > 0) return reask;
333
+ const caller = input.callerInstruction ?? "";
334
+ if (caller.length > 0) return caller;
335
+ const sourceRunPath = input.sourceRunPath?.trim() ?? "";
336
+ if (sourceRunPath === "") return undefined;
337
+ return await loadLedgerPeerBody(sourceRunPath, input.projectRoot, input.home);
338
+ }
339
+
340
+ function admittedSourceRunPath(admitted: AdmittedRoleInvocation): string {
341
+ return "sourceRunPath" in admitted
342
+ && typeof (admitted as { sourceRunPath?: unknown }).sourceRunPath === "string"
343
+ ? (admitted as { sourceRunPath: string }).sourceRunPath.trim()
344
+ : "";
345
+ }
346
+
347
+ async function resolveInitialPrompt(
295
348
  admitted: AdmittedRoleInvocation,
296
349
  env: InstructionSeatRunEnv,
297
- ): string {
350
+ ): Promise<string> {
298
351
  const record = roleRecord(admitted.role);
299
- const reask = env.reviewReask ?? env.gateReviewInstruction;
300
- if (reask !== undefined && "reaskPrompt" in record && record.reaskPrompt === true) {
301
- return reask;
352
+ if ("reaskPrompt" in record && record.reaskPrompt === true) {
353
+ const sourceRunPath = admittedSourceRunPath(admitted);
354
+ const reask = env.reviewReask ?? env.gateReviewInstruction;
355
+ const body = await resolveReviewSeatDialogueBody({
356
+ ...(reask === undefined ? {} : { reask }),
357
+ callerInstruction: admitted.instruction,
358
+ ...(sourceRunPath === "" ? {} : { sourceRunPath }),
359
+ projectRoot: admitted.projectRoot,
360
+ home: env.home,
361
+ });
362
+ // #1166 J11: merge existing file-flag delivery — never early-return body alone
363
+ // when caller attach paths were admitted. Gate with no file flags stays body-only.
364
+ if (body !== undefined) {
365
+ return appendCallerFileFlagPaths(body, admitted.attachments);
366
+ }
302
367
  }
303
368
  return buildInstructionTransportPrompt(admitted);
304
369
  }
@@ -417,6 +482,7 @@ async function dispatchAdmitted(
417
482
  if (activeEnv.correlationId !== undefined && activeEnv.correlationId.trim() !== "") {
418
483
  await recordAdmittedCorrelation(admitted, activeEnv.correlationId);
419
484
  }
485
+ const initialPromptText = await resolveInitialPrompt(admitted, activeEnv);
420
486
  const auto = "inCallAutoResume" in record && record.inCallAutoResume === true;
421
487
  if (auto) {
422
488
  return await runPostAdmissionResumable({
@@ -425,7 +491,10 @@ async function dispatchAdmitted(
425
491
  io: turnIo,
426
492
  buildInitialRequest: () => buildInstructionSeatTurnRequest(
427
493
  admitted,
428
- roleTurnOptions(activeEnv, admitted, { kind: "initial", prompt: initialPrompt(admitted, activeEnv) }),
494
+ roleTurnOptions(activeEnv, admitted, {
495
+ kind: "initial",
496
+ prompt: initialPromptText,
497
+ }),
429
498
  ),
430
499
  buildResumeRequest: () => buildInstructionSeatTurnRequest(
431
500
  admitted,
@@ -444,7 +513,10 @@ async function dispatchAdmitted(
444
513
  io: turnIo,
445
514
  request: buildInstructionSeatTurnRequest(
446
515
  admitted,
447
- roleTurnOptions(activeEnv, admitted, { kind: "initial", prompt: initialPrompt(admitted, activeEnv) }),
516
+ roleTurnOptions(activeEnv, admitted, {
517
+ kind: "initial",
518
+ prompt: initialPromptText,
519
+ }),
448
520
  ),
449
521
  adapters,
450
522
  ...(activeEnv.engine === undefined ? {} : { effectiveEngine: activeEnv.engine }),
@@ -580,14 +652,28 @@ function resumeSameParentInstructionSeat(input: {
580
652
  });
581
653
  }
582
654
 
583
- /** Re-ask words when the parent supplied them; otherwise the caller's own instruction. */
584
- function sameParentInstruction(
655
+ /**
656
+ * Same delivery rule as resolveInitialPrompt for same-parent resume summons.
657
+ * Reask / explicit caller text / source-only ledger peer — one authority.
658
+ */
659
+ async function sameParentDialogue(
585
660
  env: InstructionSeatRunEnv,
586
661
  fallback: { readonly instruction: string; readonly instructionEmpty: boolean },
587
- ): { readonly instruction: string; readonly instructionEmpty: boolean } {
588
- const resumeInstruction = env.reviewReask ?? env.gateReviewInstruction;
589
- if (resumeInstruction === undefined) return fallback;
590
- return { instruction: resumeInstruction, instructionEmpty: false };
662
+ sourceRunPath: string,
663
+ projectRoot: string,
664
+ ): Promise<{ readonly instruction: string; readonly instructionEmpty: boolean }> {
665
+ const reask = env.reviewReask ?? env.gateReviewInstruction;
666
+ const body = await resolveReviewSeatDialogueBody({
667
+ ...(reask === undefined ? {} : { reask }),
668
+ callerInstruction: fallback.instruction,
669
+ sourceRunPath,
670
+ projectRoot,
671
+ home: env.home,
672
+ });
673
+ if (body !== undefined) {
674
+ return { instruction: body, instructionEmpty: false };
675
+ }
676
+ return fallback;
591
677
  }
592
678
 
593
679
  /**
@@ -618,6 +704,9 @@ async function runCountersignBody(
618
704
  sourceRunPath: gateParentRunPath,
619
705
  instruction: resumeInstruction,
620
706
  instructionEmpty: resumeInstruction.length === 0,
707
+ // #1166 J11: same-parent resume delivers this call's file-flag paths
708
+ // through the shared summons → appendCallerFileFlagPaths rule.
709
+ attachmentPaths: parsed.attachmentPaths ?? [],
621
710
  },
622
711
  });
623
712
  if (resumed != null) return resumed;
@@ -652,10 +741,14 @@ async function runCountersignBody(
652
741
  await persistAdmittedSourceRunPath(admitted, gateParentRunPath);
653
742
  admitted = { ...admitted, sourceRunPath: gateParentRunPath };
654
743
  }
744
+ // #1166 J11: review-body branch must merge the same file-flag delivery as
745
+ // resolveInitialPrompt / resumeTurnRequestProjectionOptions — never body alone.
746
+ const reviewBody = env.reviewReask ?? env.gateReviewInstruction;
655
747
  const turnProjection = roleTurnOptions(env, admitted, {
656
748
  kind: "initial",
657
- prompt: (env.reviewReask ?? env.gateReviewInstruction)
658
- ?? buildInstructionTransportPrompt(admitted),
749
+ prompt: reviewBody !== undefined
750
+ ? appendCallerFileFlagPaths(reviewBody, admitted.attachments)
751
+ : buildInstructionTransportPrompt(admitted),
659
752
  });
660
753
  const turnRequest = buildInstructionSeatTurnRequest(admitted, turnProjection);
661
754
  const result = await runPostAdmissionOneShot({
@@ -746,10 +839,15 @@ export async function runPublicInstructionSeat(
746
839
  if (auditorSource === undefined) return { exitCode: 2 };
747
840
  const sourceDirectory = auditorSource;
748
841
  const summons: SameTicketSummonsMaterials = {
749
- ...sameParentInstruction(env, {
750
- instruction: parsed.instruction ?? "",
751
- instructionEmpty: (parsed.instruction ?? "").length === 0,
752
- }),
842
+ ...(await sameParentDialogue(
843
+ env,
844
+ {
845
+ instruction: parsed.instruction ?? "",
846
+ instructionEmpty: (parsed.instruction ?? "").length === 0,
847
+ },
848
+ sourceDirectory,
849
+ projectRoot,
850
+ )),
753
851
  attachmentPaths: parsed.attachmentPaths ?? [],
754
852
  };
755
853
  const resumed = await withAuditorSoulEnv({
@@ -767,15 +865,34 @@ export async function runPublicInstructionSeat(
767
865
  if (resumed != null) return resumed;
768
866
  }
769
867
 
770
- if (record.sameParent === "gate-pointer") {
771
- const parentRunPath = parentRunPathFromGatePointerInstruction(parsed.instruction ?? "");
772
- if (parentRunPath !== undefined) {
868
+ if (record.sameParent === "source-run") {
869
+ // #1166: structured --source-run only. Instruction bytes stay opaque caller/peer text.
870
+ if (parsed.sourceRun !== undefined) {
871
+ let parentRunPath: string;
872
+ try {
873
+ parentRunPath = (await resolveNotarySourceRunLocator({
874
+ projectRoot,
875
+ sourceRun: parsed.sourceRun,
876
+ home: env.home,
877
+ })).runDirectory;
878
+ } catch (error) {
879
+ if (error instanceof NotarySourceRunError) {
880
+ presentStructuralRejection(new CliUsageError(error.message, { cause: error }), io);
881
+ return { exitCode: 2 };
882
+ }
883
+ throw error;
884
+ }
773
885
  const summons: SameTicketSummonsMaterials = {
774
886
  sourceRunPath: parentRunPath,
775
- ...sameParentInstruction(env, {
776
- instruction: parsed.instruction ?? "",
777
- instructionEmpty: (parsed.instruction ?? "").length === 0,
778
- }),
887
+ ...(await sameParentDialogue(
888
+ env,
889
+ {
890
+ instruction: parsed.instruction ?? "",
891
+ instructionEmpty: (parsed.instruction ?? "").length === 0,
892
+ },
893
+ parentRunPath,
894
+ projectRoot,
895
+ )),
779
896
  attachmentPaths: parsed.attachmentPaths ?? [],
780
897
  };
781
898
  const resumed = await resumeSameParentInstructionSeat({
@@ -805,13 +922,15 @@ export async function runPublicInstructionSeat(
805
922
  }
806
923
  throw error;
807
924
  }
808
- const resumeInstruction = env.reviewReask ?? env.gateReviewInstruction;
809
925
  const summons: SameTicketSummonsMaterials = {
810
926
  sourceRunPath: source.runDirectory,
811
927
  sourceRun: source,
812
- ...(resumeInstruction === undefined
813
- ? {}
814
- : { instruction: resumeInstruction, instructionEmpty: false }),
928
+ ...(await sameParentDialogue(
929
+ env,
930
+ { instruction: "", instructionEmpty: true },
931
+ source.runDirectory,
932
+ projectRoot,
933
+ )),
815
934
  };
816
935
  resolvedNotarySource = source;
817
936
  const resumed = await resumeSameParentInstructionSeat({
@@ -843,16 +962,18 @@ export async function runPublicInstructionSeat(
843
962
  throw error;
844
963
  }
845
964
 
846
- if (record.sameParent === "gate-pointer") {
847
- const parentRunPath = parentRunPathFromGatePointerInstruction(parsed.instruction ?? "");
848
- if (parentRunPath !== undefined) {
849
- await persistAdmittedSourceRunPath(admitted, parentRunPath);
850
- const sourcePatch = { sourceRunPath: parentRunPath };
851
- admitted = { ...admitted, ...sourcePatch };
852
- }
965
+ if (record.sameParent === "source-run" && parsed.sourceRun !== undefined) {
966
+ const parentRunPath = (await resolveNotarySourceRunLocator({
967
+ projectRoot,
968
+ sourceRun: parsed.sourceRun,
969
+ home: env.home,
970
+ })).runDirectory;
971
+ await persistAdmittedSourceRunPath(admitted, parentRunPath);
972
+ admitted = { ...admitted, sourceRunPath: parentRunPath } as AdmittedRoleInvocation;
853
973
  }
854
974
  if (record.sameParent === "subject-source" && auditorSource !== undefined) {
855
975
  await persistAdmittedSourceRunPath(admitted, auditorSource, auditorSubject);
976
+ admitted = { ...admitted, sourceRunPath: auditorSource } as AdmittedRoleInvocation;
856
977
  }
857
978
 
858
979
  const runAdmitted = async (): Promise<SeatRunResult> => {
@@ -1247,8 +1368,7 @@ async function auditSubmittedRole(
1247
1368
  summonAuditor: async (subject, sourceRunDirectory, signal, reask, submission) => {
1248
1369
  const summoned = await summonPublicRole({
1249
1370
  role: "auditor",
1250
- argv: ["--subject", subject, "--source-run", sourceRunDirectory,
1251
- `${AUDITOR_DOSSIER_PROMPT}${sourceRunDirectory}`],
1371
+ argv: ["--subject", subject, "--source-run", sourceRunDirectory],
1252
1372
  cwd: admitted.projectRoot, home: env.home, packageRoot: env.packageRoot,
1253
1373
  principalAuthority: env.principalAuthority,
1254
1374
  correlationId: admitted.runId,