@akagilnc/pi-workflow-roles 0.1.5406 → 0.1.5475

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 (93) hide show
  1. package/CLAUDE.md +1 -1
  2. package/README.md +42 -55
  3. package/README.zh-CN.md +59 -73
  4. package/THIRD_PARTY_NOTICES.md +4 -7
  5. package/dist/acp-host/production-host.js +8261 -9073
  6. package/dist/collector-tool-schemas.js +1 -1
  7. package/dist/compliance-transport.js +2 -5
  8. package/dist/engine-detour-usage.js +3 -30
  9. package/dist/gatekeeper-role.js +4 -18
  10. package/dist/headless-host/description.js +0 -209
  11. package/dist/headless-host/production-host.js +6973 -8035
  12. package/dist/host-contracts.js +8 -0
  13. package/dist/migrate-book-topology.js +30 -27
  14. package/dist/packaged-role-registry.js +1 -9
  15. package/dist/pi/role-turn-host.js +37 -19
  16. package/dist/public-cli/auto-resume.js +29 -66
  17. package/dist/public-cli/countersign-run.js +4 -18
  18. package/dist/public-cli/instruction-seat-run.js +59 -76
  19. package/dist/public-cli/main.js +9844 -10720
  20. package/dist/public-cli/option-definitions.js +1 -1
  21. package/dist/public-cli/post-admission.js +204 -285
  22. package/dist/public-cli/process-cancel.js +8 -0
  23. package/dist/public-cli/run-lifecycle.js +24 -94
  24. package/dist/public-cli/settlement.js +614 -1524
  25. package/dist/public-cli/terminal.js +23 -53
  26. package/dist/public-role-summons.js +3 -8
  27. package/dist/receipt-delivery-policy.js +11 -12
  28. package/dist/run-terminal-artifacts.js +19 -14
  29. package/dist/serialize-thrown-value.js +7 -1
  30. package/dist/submission-ledger.js +21 -89
  31. package/package.json +1 -1
  32. package/packets/fixer-repair.md +3 -1
  33. package/packets/judge-apply.md +7 -16
  34. package/packets/judge-authority.md +7 -17
  35. package/packets/judge-plan.md +7 -18
  36. package/packets/judge-review.md +8 -22
  37. package/packets/judge-submission.md +4 -7
  38. package/resources/collector-bot-handbook.md +2 -2
  39. package/resources/diarist-collect.md +2 -2
  40. package/resources/engine-dispatch.md +2 -2
  41. package/resources/navigator-route-playbook.md +1 -1
  42. package/resources/secretariat-ticket-identity.md +1 -1
  43. package/scripts/build-package.mjs +0 -2
  44. package/souls/coder-output-guide.md +3 -3
  45. package/souls/coder.md +2 -3
  46. package/souls/doctor-auditor.md +0 -3
  47. package/souls/fixer-auditor.md +0 -2
  48. package/souls/fixer-output-guide.md +3 -3
  49. package/souls/fixer.md +2 -2
  50. package/souls/gate-output-guide.md +1 -1
  51. package/souls/quality-law.md +2 -0
  52. package/src/acp-host/role-turn-host.ts +168 -75
  53. package/src/collector-tool-schemas.ts +1 -1
  54. package/src/compliance-transport.ts +3 -6
  55. package/src/engine-detour-usage.ts +3 -33
  56. package/src/external-host-turn-loop.ts +81 -36
  57. package/src/gatekeeper-role.ts +5 -20
  58. package/src/headless-host/description.ts +4 -214
  59. package/src/headless-host/production-host.ts +2 -1
  60. package/src/headless-host/role-turn-host.ts +153 -186
  61. package/src/host-contracts.ts +13 -10
  62. package/src/navigator-public-session.ts +1 -1
  63. package/src/navigator-session-contracts.ts +1 -1
  64. package/src/package-contracts/gatekeeper-output.ts +1 -1
  65. package/src/package-contracts/navigator-output.ts +1 -1
  66. package/src/package-contracts/terminating-infrastructure.ts +1 -1
  67. package/src/packaged-role-registry.ts +2 -10
  68. package/src/pi/adapter.ts +0 -3
  69. package/src/pi/role-turn-host.ts +42 -28
  70. package/src/prepared-role-turn.ts +2 -0
  71. package/src/public-cli/auto-resume.ts +27 -66
  72. package/src/public-cli/countersign-run.ts +4 -22
  73. package/src/public-cli/instruction-seat-run.ts +79 -80
  74. package/src/public-cli/option-definitions.ts +1 -1
  75. package/src/public-cli/post-admission.ts +264 -340
  76. package/src/public-cli/process-cancel.ts +9 -0
  77. package/src/public-cli/run-lifecycle.ts +25 -126
  78. package/src/public-cli/settlement.ts +792 -1912
  79. package/src/public-cli/terminal.ts +29 -69
  80. package/src/public-role-summons.ts +3 -8
  81. package/src/receipt-delivery-policy.ts +11 -12
  82. package/src/role-envelope.ts +16 -9
  83. package/src/role-runtime.ts +14 -81
  84. package/src/run-terminal-artifacts.ts +21 -17
  85. package/src/serialize-thrown-value.ts +7 -1
  86. package/src/submission-ledger.ts +30 -116
  87. package/dist/audit-escalation.js +0 -7
  88. package/dist/pi/known-failure.js +0 -51
  89. package/dist/typed-provider-http.js +0 -57
  90. package/dist/upstream-error-testimony.js +0 -19
  91. package/src/pi/known-failure.ts +0 -76
  92. package/src/typed-provider-http.ts +0 -89
  93. package/src/upstream-error-testimony.ts +0 -45
@@ -96,7 +96,6 @@ export const PUBLIC_ROLE_RECORDS = [
96
96
  outputTool: JUDGE_OUTPUT_TOOL_NAME,
97
97
  auditTool: JUDGE_AUDIT_TOOL_NAME,
98
98
  settlement: "sealed",
99
- runnerFailure: "engine-detour-known-first",
100
99
  /** Factory board: court until another station has started, then marshal. */
101
100
  boardPlacement: "judge-history",
102
101
  /** Navigator subject is the public admitted instruction when the run is bound. */
@@ -214,7 +213,6 @@ export const PUBLIC_ROLE_RECORDS = [
214
213
  },
215
214
  /** Frozen base/lens/authority become the initial prompt; instruction follows. */
216
215
  transportPrompt: "skill-args",
217
- runnerFailure: "engine-detour-record-first",
218
216
  boardPlacement: "marshal",
219
217
  activationFlags: [
220
218
  { field: "baseRevision", flag: "ak-review-base" },
@@ -309,10 +307,7 @@ export const PUBLIC_ROLE_RECORDS = [
309
307
  /** Method-material load failure keeps the activation cause (not reviewer/fixer). */
310
308
  phases: [null],
311
309
  outputTool: MERGER_OUTPUT_TOOL_NAME,
312
- settlement: "residual",
313
- /** #836: residual scan stays on the whole host session. */
314
- residualScan: "session",
315
- residualTool: MERGER_OUTPUT_TOOL_NAME,
310
+ settlement: "sealed",
316
311
  artifactFace: {
317
312
  evidenceRole: true,
318
313
  leaves: [
@@ -489,7 +484,6 @@ export const PUBLIC_ROLE_RECORDS = [
489
484
  phases: [null],
490
485
  outputTool: GATEKEEPER_OUTPUT_TOOL_NAME,
491
486
  settlement: "accepted",
492
- runnerFailure: "engine-detour-record-first",
493
487
  activationStage: "load-and-install",
494
488
  // Province materials; officers reuse their own public records below.
495
489
  sessionMaterials: ["CLAUDE.md", "souls/gatekeeper.md", "souls/quality-law.md", "souls/gate-output-guide.md"],
@@ -504,7 +498,6 @@ export const PUBLIC_ROLE_RECORDS = [
504
498
  phases: [null],
505
499
  outputTool: NAVIGATOR_OUTPUT_TOOL_NAME,
506
500
  settlement: "accepted",
507
- runnerFailure: "engine-detour-record-first",
508
501
  activationStage: "load-and-install",
509
502
  sessionMaterials: ["CLAUDE.md", "souls/navigator.md"],
510
503
  },
@@ -527,7 +520,6 @@ export const PUBLIC_ROLE_RECORDS = [
527
520
  settlement: "accepted",
528
521
  /** Court reask replaces the initial prompt. */
529
522
  reaskPrompt: true,
530
- runnerFailure: "engine-detour-record-first",
531
523
  gateStageLabel: "审刑院",
532
524
  /** Gate summon binds --subject judge and --source-run. */
533
525
  gateSummon: "subject-source",
@@ -630,7 +622,7 @@ export type PackagedActivationFlag = {
630
622
  };
631
623
 
632
624
  /**
633
- * Output tool for seats whose settlement leaf is the shared accepted-tool scan.
625
+ * Output tool for seats whose settlement leaf is the accepted ledger.
634
626
  * Any other settlement leaf names that seat's own reader.
635
627
  */
636
628
  export function packagedRoleAcceptedOutputTool(role: string): string | undefined {
package/src/pi/adapter.ts CHANGED
@@ -194,9 +194,6 @@ export function createPiRoleHostAdapter(
194
194
  } else if (registration[0] === "session_shutdown") {
195
195
  const [, handler] = registration;
196
196
  pi.on("session_shutdown", (_value, ctx) => handler({}, context(ctx)));
197
- } else if (registration[0] === "after_provider_response") {
198
- const [, handler] = registration;
199
- pi.on("after_provider_response", (value, ctx) => handler({ status: value.status }, context(ctx)));
200
197
  } else if (registration[0] === "agent_end") {
201
198
  const [, handler] = registration;
202
199
  pi.on("agent_end", (value, ctx) => handler({
@@ -25,6 +25,8 @@ import { ExplicitInternalActivationError } from "../host-contracts.ts";
25
25
  import { applyEngineChildEnv, ENGINE_MODEL_FLAG_NAME, normalizeEngineName } from "../engine-detour.ts";
26
26
  import { projectActivationFlags } from "../role-activation-flags.ts";
27
27
  import { encodeUserDialogueStdin } from "../user-dialogue-stdin.ts";
28
+ import { projectThrownFailureLeaf, retainPackageFault } from "../public-cli/settlement.ts";
29
+ import { describeErrorIdentity } from "../public-cli/run-lifecycle.ts";
28
30
  import { RECEIPT_DELIVERY_LIMIT_ENV } from "../receipt-delivery-policy.ts";
29
31
 
30
32
  /** Package-relative Internal role entrypoint (ADR 0052; same path as public-cli registry). */
@@ -156,6 +158,7 @@ export type PiSpawnRunner = (
156
158
  code: number | null;
157
159
  stderr: string;
158
160
  timedOut: boolean;
161
+ signal?: string;
159
162
  knownFailure?: RoleTurnKnownFailure;
160
163
  }>;
161
164
 
@@ -287,7 +290,6 @@ export function createDefaultPiSpawnRunner(options: {
287
290
  // fall back to rejecting on `error` if `close` never fires (e.g. spawn
288
291
  // never succeeded so no `close` event will arrive).
289
292
  let hasSpawned = false;
290
- let executionError: Error | undefined;
291
293
  const armTimeoutAfterChildReady = (): void => {
292
294
  if (spawnOptions.timeoutMs === undefined) return;
293
295
  timer = setTimeout(() => {
@@ -302,6 +304,8 @@ export function createDefaultPiSpawnRunner(options: {
302
304
  child.kill("SIGTERM");
303
305
  };
304
306
  parentSignal?.addEventListener("abort", terminateForParentAbort, { once: true });
307
+ const packageErrors: unknown[] = [];
308
+ let identityFailure: RoleTurnKnownFailure | undefined;
305
309
  let identityRecorded: Promise<void> = Promise.resolve();
306
310
  child.once("spawn", () => {
307
311
  hasSpawned = true;
@@ -313,7 +317,12 @@ export function createDefaultPiSpawnRunner(options: {
313
317
  runDirectory !== "" &&
314
318
  options.recordLaunchedPiIdentity !== undefined
315
319
  ) {
316
- identityRecorded = options.recordLaunchedPiIdentity(runDirectory, piIdentity);
320
+ identityRecorded = Promise.resolve().then(() =>
321
+ options.recordLaunchedPiIdentity!(runDirectory, piIdentity),
322
+ ).catch((error) => {
323
+ packageErrors.push(error);
324
+ identityFailure = projectThrownFailureLeaf(error);
325
+ });
317
326
  }
318
327
  });
319
328
  child.stderr.setEncoding("utf8").on("data", (chunk) => {
@@ -323,8 +332,9 @@ export function createDefaultPiSpawnRunner(options: {
323
332
  // A pre-spawn error has no child lifecycle to close. After spawn,
324
333
  // retain the execution error and let the mandatory close event own
325
334
  // cleanup and the single settlement.
326
- if (settled || hasSpawned) {
327
- executionError = error;
335
+ if (settled) return;
336
+ if (hasSpawned) {
337
+ packageErrors.push(error);
328
338
  return;
329
339
  }
330
340
  if (timer !== undefined) clearTimeout(timer);
@@ -332,33 +342,37 @@ export function createDefaultPiSpawnRunner(options: {
332
342
  settled = true;
333
343
  reject(error);
334
344
  });
335
- child.on("close", (code) => {
345
+ // `close` carries (code, signal): a signal-killed child reports code null.
346
+ // Both travel out so a signal death is never read as a clean exit.
347
+ child.on("close", (code, closeSignal) => {
336
348
  if (timer !== undefined) clearTimeout(timer);
337
349
  parentSignal?.removeEventListener("abort", terminateForParentAbort);
338
- void identityRecorded.then(
339
- () => {
340
- if (settled) return;
341
- settled = true;
342
- if (executionError !== undefined) {
343
- reject(executionError);
344
- return;
350
+ void (async () => {
351
+ if (settled) return;
352
+ settled = true;
353
+ await identityRecorded;
354
+ if (stdinDeliveryError !== undefined) packageErrors.push(stdinDeliveryError);
355
+ for (const error of packageErrors) {
356
+ const diagnostic = `Pi transport handling failed beside host terminal: ${describeErrorIdentity(error)}`;
357
+ const runDirectory = spawnOptions.env.AK_ROLE_RUN_DIR;
358
+ if (runDirectory !== undefined) {
359
+ await retainPackageFault({ runDirectory, diagnostic, error });
360
+ } else {
361
+ // Bare runner callers have no admitted artifact directory.
362
+ try { process.stderr.write(`${diagnostic}\n`); }
363
+ catch { /* Best-effort presentation beside the already closed child. */ }
345
364
  }
346
- if (stdinDeliveryError !== undefined) {
347
- reject(stdinDeliveryError);
348
- return;
349
- }
350
- resolveResult({
351
- code,
352
- stderr,
353
- timedOut,
354
- });
355
- },
356
- (error) => {
357
- if (settled) return;
358
- settled = true;
359
- reject(error);
360
- },
361
- );
365
+ }
366
+ resolveResult({
367
+ code,
368
+ stderr,
369
+ timedOut,
370
+ ...(closeSignal === null ? {} : { signal: closeSignal }),
371
+ ...(code === 0 && !timedOut && closeSignal === null && identityFailure !== undefined
372
+ ? { knownFailure: identityFailure }
373
+ : {}),
374
+ });
375
+ })();
362
376
  });
363
377
  });
364
378
  };
@@ -37,6 +37,8 @@ export type PreparedRoleTurn = Readonly<{
37
37
  }
38
38
  | { readonly accepted: false; readonly failure: RoleTurnKnownFailure }
39
39
  >;
40
+ /** Drain required shutdown work. Its failures throw; ordinary transport
41
+ * cleanup is reported by its owner without rejecting this drain. */
40
42
  dispose?(): Promise<void>;
41
43
  /**
42
44
  * Headless CLI family (#645): role terminating-tool schema for host-native
@@ -13,10 +13,8 @@
13
13
  */
14
14
  import { constants as fsConstants } from "node:fs";
15
15
  import { randomUUID } from "node:crypto";
16
- import { lstat, mkdir, open } from "node:fs/promises";
16
+ import { open } from "node:fs/promises";
17
17
  import { join } from "node:path";
18
-
19
- import { roleRunArtifactsDirectory } from "../role-run-placement.ts";
20
18
  import type {
21
19
  DurablePrincipal,
22
20
  DurablePrincipalAuthority,
@@ -26,7 +24,6 @@ import {
26
24
  AUTO_RESUME_LIMIT,
27
25
  describeErrorIdentity,
28
26
  acquireRunWriterLease,
29
- markRunResumable,
30
27
  markRunTerminal,
31
28
  RunWriterLeaseHeldError,
32
29
  type RunWriterLease,
@@ -36,47 +33,47 @@ import { processCancelSignalName } from "./process-cancel.ts";
36
33
  import { isLawfulTypedTerminalOutcome, formatTerminalResult, type TerminalArtifactRef, type TerminalResult, type TerminalRoleName } from "./terminal.ts";
37
34
  import {
38
35
  attachRecordedSubmissions,
36
+ ensureRealArtifactsDirectory,
39
37
  presentFailureTerminal,
40
- resolveControlledFailureResumeObservation,
38
+ retainPackageFault,
41
39
  } from "./settlement.ts";
40
+
41
+ export { ensureRealArtifactsDirectory };
42
42
  import type { CliIo } from "./cli-io.ts";
43
43
  import { serializeThrownValue } from "../serialize-thrown-value.ts";
44
44
 
45
- import { errorText, isEnoent } from "../unknown-value.ts";
45
+ import { errorText } from "../unknown-value.ts";
46
46
 
47
47
  const dummyIo: CliIo = { stdout: () => {}, stderr: () => {} };
48
48
 
49
49
  /**
50
50
  * Persist run-state after a host-turn result, outside the retried dispatch try.
51
- * Lawful settlement always seals terminal — a typed 429 observation must not
52
- * win (#416 成功即停). 429 only marks resumable on the controlled-failure path.
53
- * Write failure must escape the retried dispatch try instead of becoming a
54
- * synthetic host-turn terminal.
51
+ * The host outcome already decided the terminal. This write only seals the run.
55
52
  */
56
53
  export async function persistReturnedRunState(
57
- admitted: { runDirectory: string; principal?: DurablePrincipal },
58
- options?: { readonly lawful?: boolean },
54
+ admitted: { runDirectory: string },
59
55
  ): Promise<void> {
60
- if (options?.lawful === true) {
61
- await markRunTerminal(admitted.runDirectory);
62
- return;
63
- }
64
- const resumeObservation = await resolveControlledFailureResumeObservation({
65
- runDirectory: admitted.runDirectory,
66
- });
67
- const typedHttp429 = resumeObservation.typedHttp429;
68
- if (admitted.principal !== undefined && typedHttp429 !== undefined) {
69
- await markRunResumable(admitted.runDirectory, typedHttp429);
70
- return;
71
- }
72
56
  await markRunTerminal(admitted.runDirectory);
73
57
  }
74
58
 
75
- function presentTerminal(terminal: TerminalResult, io: CliIo): void {
76
- if (terminal.roleOutcome.kind === "failure" || terminal.roleOutcome.kind === "no_receipt") {
77
- presentFailureTerminal(terminal, io);
78
- } else {
79
- io.stdout(formatTerminalResult(terminal));
59
+ export async function presentTerminal(terminal: TerminalResult, io: CliIo, runDirectory: string, held?: readonly string[]): Promise<void> {
60
+ try {
61
+ if (held !== undefined) {
62
+ for (const value of held) io.stdout(value);
63
+ } else if (terminal.roleOutcome.kind === "failure" || terminal.roleOutcome.kind === "no_receipt") {
64
+ presentFailureTerminal(terminal, io);
65
+ } else {
66
+ io.stdout(formatTerminalResult(terminal));
67
+ }
68
+ } catch (error) {
69
+ // Presentation consumes a settled report; it never reopens dispatch or
70
+ // replaces the host's cause, identity or open details.
71
+ await retainPackageFault({
72
+ runDirectory,
73
+ diagnostic: `terminal presentation failed beside host terminal: ${describeErrorIdentity(error)}`,
74
+ error,
75
+ stderr: (text) => io.stderr(text),
76
+ });
80
77
  }
81
78
  }
82
79
 
@@ -144,38 +141,6 @@ export class TurnDispatchedFailure extends Error {
144
141
  /** Session custom-entry type carrying the pointer to one dispatch error file. */
145
142
  export const DISPATCH_ERROR_RETENTION_ENTRY_TYPE = "ak_run_dispatch_error_retention" as const;
146
143
 
147
- /**
148
- * #182-A hardened path identity, mirrored from settlement.ts's
149
- * ensureAuditEvidenceDirectory: a planted symlink at the run directory or the
150
- * artifacts path must not receive a durable run artifact (O_NOFOLLOW only
151
- * protects the final file name; recursive mkdir would accept a symlinked
152
- * parent). Fails loudly with the true cause instead. Shared by every caller
153
- * that needs a durable run-artifacts write independent of session/dossier
154
- * health — dispatch-error retention here, and post-admission's best-effort
155
- * cleanup diagnostic (#840 bounce class 2).
156
- */
157
- export async function ensureRealArtifactsDirectory(runDirectory: string): Promise<string> {
158
- const runStat = await lstat(runDirectory);
159
- if (runStat.isSymbolicLink() || !runStat.isDirectory()) {
160
- throw new Error("run artifact retention: run directory is not a real directory");
161
- }
162
- const artifactsDir = roleRunArtifactsDirectory(runDirectory);
163
- try {
164
- const existing = await lstat(artifactsDir);
165
- if (existing.isSymbolicLink() || !existing.isDirectory()) {
166
- throw new Error("run artifact retention: artifacts path is not a real directory");
167
- }
168
- } catch (error) {
169
- if (!isEnoent(error)) throw error;
170
- await mkdir(artifactsDir, { recursive: true });
171
- const created = await lstat(artifactsDir);
172
- if (created.isSymbolicLink() || !created.isDirectory()) {
173
- throw new Error("run artifact retention: artifacts directory is not a real directory");
174
- }
175
- }
176
- return artifactsDir;
177
- }
178
-
179
144
  /** Cycle- and bigint-safe JSON replacer so serialization itself cannot drop data. */
180
145
  function jsonSafeReplacer(): (key: string, value: unknown) => unknown {
181
146
  const seen = new WeakSet<object>();
@@ -563,11 +528,7 @@ export async function runWithAutoResumeLoop<
563
528
  } } as TerminalResult
564
529
  : terminal;
565
530
  if (complete !== undefined) {
566
- if (isLawfulTypedTerminalOutcome(complete.roleOutcome)) {
567
- options.io.stdout(formatTerminalResult(complete));
568
- } else {
569
- presentTerminal(complete, options.io);
570
- }
531
+ await presentTerminal(complete, options.io, options.admitted.runDirectory);
571
532
  }
572
533
  return stoppedResult === undefined
573
534
  ? { exitCode: 1, terminal: complete } as T
@@ -7,7 +7,7 @@ import { readableGateItem } from "../readable-gate-item.ts";
7
7
  import type { AdmittedCountersignInvocation } from "./invocation.ts";
8
8
  import type { PostAdmissionEnv } from "./post-admission.ts";
9
9
  import type { CliIo } from "./cli-io.ts";
10
- import type { TerminalRoleOutcome } from "./terminal.ts";
10
+ import { currentReplyRows, type TerminalResult, type TerminalRoleOutcome } from "./terminal.ts";
11
11
 
12
12
  import { isRecord } from "../unknown-value.ts";
13
13
 
@@ -60,22 +60,6 @@ function isEscalatePayload(payload: unknown): boolean {
60
60
  );
61
61
  }
62
62
 
63
- /**
64
- * Rows a parent may read from a nested diarist terminal (#836 / #953).
65
- * Accepted/audit use role result payloads; failure history is submissions only.
66
- */
67
- function courtDiaristPayloadRows(
68
- roleOutcome: TerminalRoleOutcome | undefined,
69
- submissions: readonly unknown[] | undefined,
70
- ): readonly unknown[] {
71
- if (roleOutcome === undefined) return submissions ?? [];
72
- if (roleOutcome.kind === "accepted" || roleOutcome.kind === "audit_escalation") {
73
- return roleOutcome.payloads ?? [];
74
- }
75
- if (roleOutcome.kind === "failure") return submissions ?? [];
76
- return [];
77
- }
78
-
79
63
  /**
80
64
  * Parent diagnostic for court-diarist escalate (#953 / 失败诚实 / 传话).
81
65
  * Relays diarist escalate payload(s) via readableGateItem — never invents
@@ -87,10 +71,9 @@ function courtDiaristPayloadRows(
87
71
  * every escalate receipt as-is, last = currentConclusion.
88
72
  */
89
73
  function courtDiaristEscalateDiagnostic(
90
- roleOutcome: TerminalRoleOutcome | undefined,
91
- submissions?: readonly unknown[],
74
+ terminal: TerminalResult | undefined,
92
75
  ): string {
93
- const payloads = courtDiaristPayloadRows(roleOutcome, submissions);
76
+ const payloads = currentReplyRows(terminal);
94
77
  const escalatePayloads: unknown[] = [];
95
78
  for (const payload of payloads) {
96
79
  if (isEscalatePayload(payload)) escalatePayloads.push(payload);
@@ -183,14 +166,13 @@ export async function invokeCourtDiarist(
183
166
  });
184
167
 
185
168
  const roleOutcome = result.terminal?.roleOutcome;
186
- const submissions = result.terminal?.submissions;
187
169
  // Escalate routing is a boolean over the preserved sequence (#881). Reasons and
188
170
  // payload bodies stay on roleOutcome — never rewritten into a sole identity reason.
189
171
  if (latestPayloadEscalated(roleOutcome)) {
190
172
  return {
191
173
  identity: {
192
174
  kind: "escalate",
193
- diagnostic: courtDiaristEscalateDiagnostic(roleOutcome, submissions),
175
+ diagnostic: courtDiaristEscalateDiagnostic(result.terminal),
194
176
  },
195
177
  ...(result.admitted === undefined ? {} : { admitted: result.admitted }),
196
178
  ...(result.terminal === undefined ? {} : { terminal: result.terminal }),