@akagilnc/pi-workflow-roles 0.1.4021 → 0.1.4035

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.
@@ -8,9 +8,8 @@
8
8
  import { sitianReport } from "./sitian-facade.ts";
9
9
  import {
10
10
  NavigatorUnavailableError,
11
- navigatorProviderFailureFromDiagnostics,
11
+ navigatorProviderFailureFromPublicTerminal,
12
12
  navigatorProviderFailureFromError,
13
- navigatorProviderFailureFromStatus,
14
13
  navigatorUnavailableError,
15
14
  parseNavigatorModelSetting,
16
15
  resolveNavigatorSeatSelection,
@@ -18,43 +17,6 @@ import {
18
17
  type NavigatorSessionFactory,
19
18
  } from "./navigator-session-contracts.ts";
20
19
  import type { NoReceiptLifecycleFacts } from "./receipt-delivery-policy.ts";
21
- import { lastRolePayloadRecord } from "./public-cli/terminal.ts";
22
-
23
- /**
24
- * Classify public-navigator failure terminal from structured decisiveFacts only
25
- * (httpStatus / diagnostics / secondaryEvidence on the terminal) — same fields
26
- * settlement already stamps from typed HTTP observation / provider stop.
27
- */
28
- function providerFailureFromPublicTerminal(outcome: {
29
- readonly cause: string;
30
- readonly diagnostic: string;
31
- readonly decisiveFacts: Readonly<Record<string, unknown>>;
32
- }): NavigatorProviderFailureFact {
33
- const facts = outcome.decisiveFacts;
34
- const secondary =
35
- typeof facts.secondaryEvidence === "object" && facts.secondaryEvidence !== null
36
- ? (facts.secondaryEvidence as Record<string, unknown>)
37
- : undefined;
38
- const httpStatus =
39
- typeof secondary?.httpStatus === "number"
40
- ? secondary.httpStatus
41
- : typeof facts.httpStatus === "number"
42
- ? facts.httpStatus
43
- : typeof facts.errorCode === "number"
44
- ? facts.errorCode
45
- : undefined;
46
- const fromStatus = navigatorProviderFailureFromStatus(httpStatus);
47
- if (fromStatus !== undefined) return fromStatus;
48
- const diagnostics = secondary?.diagnostics ?? facts.diagnostics;
49
- const fromDiagnostics = navigatorProviderFailureFromDiagnostics(diagnostics);
50
- if (fromDiagnostics !== undefined) return fromDiagnostics;
51
- const fromCode = navigatorProviderFailureFromError({
52
- code: secondary?.code ?? facts.errorCode,
53
- });
54
- if (fromCode !== undefined) return fromCode;
55
- if (outcome.cause === "provider") return { source: "transport", cause: "transport" };
56
- return { source: "session", cause: "session" };
57
- }
58
20
 
59
21
  export function createNativeNavigatorSessionFactory(): NavigatorSessionFactory {
60
22
  return async ({ context, subject, tool }) => {
@@ -122,14 +84,16 @@ export function createNativeNavigatorSessionFactory(): NavigatorSessionFactory {
122
84
  const outcome = summoned.terminal?.roleOutcome;
123
85
  if (outcome === undefined) {
124
86
  const detail = summoned.stderr?.trim() || `exit ${summoned.exitCode}`;
125
- providerFailure = { source: "transport", cause: "transport" };
87
+ // Known source is the public-summon transport path; unconfirmed cause stays unknown.
88
+ providerFailure = { source: "transport", cause: "unknown" };
126
89
  throw navigatorUnavailableError(
127
- "transport",
90
+ providerFailure.source,
128
91
  new Error(`Navigator public summon produced no terminal (${detail})`),
92
+ providerFailure.cause,
129
93
  );
130
94
  }
131
95
  if (outcome.kind === "failure") {
132
- providerFailure = providerFailureFromPublicTerminal(outcome);
96
+ providerFailure = navigatorProviderFailureFromPublicTerminal(outcome);
133
97
  throw navigatorUnavailableError(
134
98
  providerFailure.source,
135
99
  new Error(outcome.diagnostic),
@@ -147,23 +111,23 @@ export function createNativeNavigatorSessionFactory(): NavigatorSessionFactory {
147
111
  // Not a shape-unusable judgment on the navigator reply (#757).
148
112
  return;
149
113
  }
150
- const candidates = lastRolePayloadRecord(outcome.payloads ?? [])?.candidates;
151
- if (!Array.isArray(candidates)) {
152
- // Accepted reply without candidates array — no advice, no judgment.
153
- return;
114
+ for (const payload of outcome.payloads ?? []) {
115
+ if (typeof payload !== "object" || payload === null || Array.isArray(payload)) continue;
116
+ const candidates = (payload as { candidates?: unknown }).candidates;
117
+ if (!Array.isArray(candidates)) continue;
118
+ await tool.execute(
119
+ "navigator-public-prepare",
120
+ { candidates },
121
+ undefined,
122
+ undefined,
123
+ context as never,
124
+ );
154
125
  }
155
- // Rejoin attendance prepare tool sink (same candidate shape as public advice).
156
- await tool.execute(
157
- "navigator-public-prepare",
158
- { candidates },
159
- undefined,
160
- undefined,
161
- context as never,
162
- );
163
126
  } catch (error) {
164
127
  if (error instanceof NavigatorUnavailableError) throw error;
165
128
  const fact = navigatorProviderFailureFromError(error);
166
- providerFailure = fact ?? { source: "transport", cause: "transport" };
129
+ // Catch path is the public-summon seam (source transport); untyped cause stays unknown.
130
+ providerFailure = fact ?? { source: "transport", cause: "unknown" };
167
131
  throw navigatorUnavailableError(providerFailure.source, error, providerFailure.cause);
168
132
  }
169
133
  })();
@@ -133,6 +133,45 @@ export function navigatorProviderFailureFromDiagnostics(diagnostics: unknown): N
133
133
  return undefined;
134
134
  }
135
135
 
136
+ /**
137
+ * Classify a public-navigator failure terminal from structured facts only.
138
+ * Untyped cause stays unknown — never relabeled session.
139
+ */
140
+ export function navigatorProviderFailureFromPublicTerminal(outcome: {
141
+ readonly cause?: string;
142
+ readonly diagnostic: string;
143
+ readonly decisiveFacts: Readonly<Record<string, unknown>>;
144
+ }): NavigatorProviderFailureFact {
145
+ const facts = outcome.decisiveFacts;
146
+ const secondary =
147
+ typeof facts.secondaryEvidence === "object" && facts.secondaryEvidence !== null
148
+ ? (facts.secondaryEvidence as Record<string, unknown>)
149
+ : undefined;
150
+ const httpStatus =
151
+ typeof secondary?.httpStatus === "number"
152
+ ? secondary.httpStatus
153
+ : typeof facts.httpStatus === "number"
154
+ ? facts.httpStatus
155
+ : typeof facts.errorCode === "number"
156
+ ? facts.errorCode
157
+ : undefined;
158
+ const fromStatus = navigatorProviderFailureFromStatus(httpStatus);
159
+ if (fromStatus !== undefined) return fromStatus;
160
+ const diagnostics = secondary?.diagnostics ?? facts.diagnostics;
161
+ const fromDiagnostics = navigatorProviderFailureFromDiagnostics(diagnostics);
162
+ if (fromDiagnostics !== undefined) return fromDiagnostics;
163
+ const fromCode = navigatorProviderFailureFromError({
164
+ code: secondary?.code ?? facts.errorCode,
165
+ });
166
+ if (fromCode !== undefined) return fromCode;
167
+ // cause=provider names the known source region only; without status/diagnostics/code
168
+ // the specific cause stays unknown (#881 — never invent transport/session labels).
169
+ if (outcome.cause === "provider") return { source: "transport", cause: "unknown" };
170
+ const typed = navigatorUnavailableKey(outcome.cause);
171
+ if (typed !== undefined) return { source: typed, cause: typed };
172
+ return { source: "unknown", cause: "unknown" };
173
+ }
174
+
136
175
  const navigatorProviderFailureSchema = Type.Object({
137
176
  source: Type.Union([
138
177
  Type.Literal("context"), Type.Literal("session"), Type.Literal("model"), Type.Literal("thinking"),
@@ -49,9 +49,8 @@ function sessionStopDetails(input: {
49
49
  /**
50
50
  * Project a native session assistant stop onto the existing knownFailure chain.
51
51
  * Classification follows two-way testimony: typed HTTP status or SDK structure
52
- * keeps provider; stopReason, configured provider/model, or errorMessage prose
53
- * alone is the existing unrecognized value. Present upstream payload is preserved
54
- * in details without rewriting; missing fields are omitted.
52
+ * keeps provider; stopReason / errorMessage prose alone never invents a class (#881).
53
+ * Present upstream payload is preserved in details without rewriting; missing fields are omitted.
55
54
  */
56
55
  export function knownFailureFromProviderStop(input: {
57
56
  readonly stopReason?: string;
@@ -70,7 +69,7 @@ export function knownFailureFromProviderStop(input: {
70
69
  const diagnostic = nonEmptyString(input.errorMessage);
71
70
  const details = sessionStopDetails(input);
72
71
  return {
73
- cause: hasUpstreamErrorTestimony(input) ? "provider" : "unrecognized",
72
+ ...(hasUpstreamErrorTestimony(input) ? { cause: "provider" as const } : {}),
74
73
  ...(diagnostic === undefined ? {} : { diagnostic }),
75
74
  ...(Object.keys(details).length === 0 ? {} : { details }),
76
75
  };
@@ -33,6 +33,7 @@ import {
33
33
  import { parseAutoResumeLimit } from "./config.ts";
34
34
  import { isLawfulTypedTerminalOutcome, formatTerminalResult, type TerminalArtifactRef, type TerminalResult, type TerminalRoleName } from "./terminal.ts";
35
35
  import {
36
+ attachRecordedSubmissions,
36
37
  presentFailureTerminal,
37
38
  presentStructuralRejection,
38
39
  resolveControlledFailureResumeObservation,
@@ -362,6 +363,33 @@ function unwrapTurnDispatchedFailure(error: unknown): unknown {
362
363
  return current;
363
364
  }
364
365
 
366
+ async function attachDispatchExceptionTerminal(
367
+ admitted: {
368
+ readonly runDirectory: string;
369
+ readonly runId: string;
370
+ readonly projectRoot: string;
371
+ },
372
+ terminal: TerminalResult,
373
+ io: CliIo,
374
+ ): Promise<TerminalResult> {
375
+ try {
376
+ return await attachRecordedSubmissions(
377
+ {
378
+ projectRoot: admitted.projectRoot,
379
+ runId: admitted.runId,
380
+ runDirectory: admitted.runDirectory,
381
+ },
382
+ terminal,
383
+ );
384
+ } catch (error) {
385
+ // Existing diagnostic seam: attach true cause on stderr, original Terminal stays.
386
+ io.stderr(
387
+ `dispatch exception ledger attach failed (best-effort continue): ${describeErrorIdentity(error)}\n`,
388
+ );
389
+ return terminal;
390
+ }
391
+ }
392
+
365
393
  /**
366
394
  * Typed failure terminal for a retry path that ended with only exceptions:
367
395
  * loud, non-lawful, carrying the last true cause and the pointers to the
@@ -384,8 +412,8 @@ function dispatchExceptionFailureTerminal(input: {
384
412
  ? "dispatch threw an exception on every attempt"
385
413
  : "the final dispatch threw an exception";
386
414
  const diagnostic = `${history} (${input.endReason}; resumes used ${input.autoResumeAttempts}); last cause: ${describeErrorIdentity(causeError)}`;
415
+ // #881: no fabricated cause class — original error identity + error-file pointers carry the fact.
387
416
  const decisiveFacts: Record<string, unknown> = {
388
- cause: "unrecognized",
389
417
  diagnostic,
390
418
  resumesUsed: input.autoResumeAttempts,
391
419
  dispatchErrorFiles: [...input.errorFiles],
@@ -406,7 +434,6 @@ function dispatchExceptionFailureTerminal(input: {
406
434
  roleOutcome: {
407
435
  kind: "failure",
408
436
  role: input.role,
409
- cause: "unrecognized",
410
437
  diagnostic,
411
438
  decisiveFacts,
412
439
  },
@@ -427,6 +454,8 @@ export async function runWithAutoResumeLoop<
427
454
  role: TerminalRoleName;
428
455
  runId: string;
429
456
  principal: DurablePrincipal;
457
+ /** Required: exception terminals attach the ledger via this existing admitted fact. */
458
+ projectRoot: string;
430
459
  };
431
460
  principalAuthority: DurablePrincipalAuthority;
432
461
  /**
@@ -571,15 +600,19 @@ export async function runWithAutoResumeLoop<
571
600
  } else {
572
601
  // Exception path: continue through the identical budget/session gates.
573
602
  if (autoResumeAttempts >= limit) {
574
- const terminal = dispatchExceptionFailureTerminal({
575
- role: options.admitted.role,
576
- runId: options.admitted.runId,
577
- causeError: lastThrownError,
578
- errorFiles: retainedErrorFiles,
579
- autoResumeAttempts,
580
- endReason: "auto-resume budget exhausted",
581
- everyAttemptThrew,
582
- });
603
+ const terminal = await attachDispatchExceptionTerminal(
604
+ options.admitted,
605
+ dispatchExceptionFailureTerminal({
606
+ role: options.admitted.role,
607
+ runId: options.admitted.runId,
608
+ causeError: lastThrownError,
609
+ errorFiles: retainedErrorFiles,
610
+ autoResumeAttempts,
611
+ endReason: "auto-resume budget exhausted",
612
+ everyAttemptThrew,
613
+ }),
614
+ options.io,
615
+ );
583
616
  await finalizeExceptionRunBestEffort(options.admitted.runDirectory, options.io);
584
617
  presentTerminal(terminal, options.io);
585
618
  return {
@@ -588,15 +621,19 @@ export async function runWithAutoResumeLoop<
588
621
  } as T;
589
622
  }
590
623
  if (!(await isPrincipalAvailable(options.admitted.principal))) {
591
- const terminal = dispatchExceptionFailureTerminal({
592
- role: options.admitted.role,
593
- runId: options.admitted.runId,
594
- causeError: lastThrownError,
595
- errorFiles: retainedErrorFiles,
596
- autoResumeAttempts,
597
- endReason: "session principal unavailable before further resume",
598
- everyAttemptThrew,
599
- });
624
+ const terminal = await attachDispatchExceptionTerminal(
625
+ options.admitted,
626
+ dispatchExceptionFailureTerminal({
627
+ role: options.admitted.role,
628
+ runId: options.admitted.runId,
629
+ causeError: lastThrownError,
630
+ errorFiles: retainedErrorFiles,
631
+ autoResumeAttempts,
632
+ endReason: "session principal unavailable before further resume",
633
+ everyAttemptThrew,
634
+ }),
635
+ options.io,
636
+ );
600
637
  await finalizeExceptionRunBestEffort(options.admitted.runDirectory, options.io);
601
638
  presentTerminal(terminal, options.io);
602
639
  return {
@@ -1637,10 +1637,10 @@ export async function runAkRole(
1637
1637
  if (error.cause !== undefined) {
1638
1638
  const detail = formatErrorCauseDetail(error.cause);
1639
1639
  if (detail.trim().length > 0) {
1640
- label = `${label || error.name || "unrecognized exception"}; cause: ${detail}`;
1640
+ label = `${label || error.name || "exception"}; cause: ${detail}`;
1641
1641
  }
1642
1642
  }
1643
- io.stderr(formatCliDiagnostic(label || error.name || "unrecognized exception"));
1643
+ io.stderr(formatCliDiagnostic(label || error.name || "exception"));
1644
1644
  return { exitCode: 1 };
1645
1645
  }
1646
1646
  io.stderr(formatCliDiagnostic(String(error)));
@@ -142,7 +142,9 @@ function collectorAdapters(): PostAdmissionAdapters<AdmittedCollectorInvocation>
142
142
  (infrastructureFailure === undefined
143
143
  ? undefined
144
144
  : {
145
- cause: infrastructureFailure.cause,
145
+ ...(infrastructureFailure.cause === undefined
146
+ ? {}
147
+ : { cause: infrastructureFailure.cause }),
146
148
  diagnostic: infrastructureFailure.diagnostic,
147
149
  ...(infrastructureFailure.identity === undefined
148
150
  ? {}
@@ -54,7 +54,7 @@ import {
54
54
  trySettleCountersignTerminalResult,
55
55
  } from "./settlement.ts";
56
56
  import type { CliIo } from "./cli-io.ts";
57
- import { lastRolePayloadRecord, type TerminalResult } from "./terminal.ts";
57
+ import type { TerminalResult, TerminalRoleOutcome } from "./terminal.ts";
58
58
  import {
59
59
  projectRoleTurnRequest,
60
60
  type RoleTurnRequestProjectionOptions,
@@ -96,7 +96,24 @@ export function buildCountersignTurnRequest(
96
96
  type CourtDiaristIdentity =
97
97
  | { readonly kind: "ticket"; readonly ticketNumber: number }
98
98
  | { readonly kind: "unbound" }
99
- | { readonly kind: "escalate"; readonly reason: string };
99
+ | { readonly kind: "escalate" };
100
+
101
+ type CourtDiaristInvocationResult = {
102
+ readonly identity: CourtDiaristIdentity;
103
+ readonly failedWithoutEscalate?: { readonly diagnostic: string };
104
+ };
105
+
106
+ /** Routing boolean over the child's own typed sequence — does not pick or rewrite a sole row. */
107
+ function courtDiaristEscalated(roleOutcome: TerminalRoleOutcome | undefined): boolean {
108
+ if (roleOutcome === undefined) return false;
109
+ if (roleOutcome.kind === "audit_escalation") return true;
110
+ if (roleOutcome.kind !== "accepted") return false;
111
+ return (roleOutcome.payloads ?? []).some((payload) => {
112
+ if (typeof payload !== "object" || payload === null || Array.isArray(payload)) return false;
113
+ const record = payload as Record<string, unknown>;
114
+ return record.status === "escalate" || record.countersignStatus === "escalate";
115
+ });
116
+ }
100
117
 
101
118
  /**
102
119
  * Invoke public 起居郎 under the court-pipeline quiet face.
@@ -112,10 +129,7 @@ async function invokeCourtDiarist(input: {
112
129
  readonly failureLabel: string;
113
130
  /** Already-verified typed key from countersign (refresh / post-assert handoff). */
114
131
  readonly boundTicketNumber?: number;
115
- }, env: CountersignRunEnv, io: CliIo): Promise<{
116
- readonly identity: CourtDiaristIdentity;
117
- readonly failedWithoutEscalate?: { readonly diagnostic: string };
118
- }> {
132
+ }, env: CountersignRunEnv, io: CliIo): Promise<CourtDiaristInvocationResult> {
119
133
  // Quiet face: the countersign caller must not see diarist CLI chatter.
120
134
  const quietIo: CliIo = {
121
135
  stdout() {},
@@ -145,23 +159,12 @@ async function invokeCourtDiarist(input: {
145
159
  });
146
160
 
147
161
  const roleOutcome = result.terminal?.roleOutcome;
148
- if (roleOutcome !== undefined && (roleOutcome.kind === "accepted" || roleOutcome.kind === "audit_escalation")) {
149
- const facts = lastRolePayloadRecord(roleOutcome.payloads ?? []);
150
- const status =
151
- typeof facts?.status === "string"
152
- ? facts.status
153
- : typeof facts?.countersignStatus === "string"
154
- ? facts.countersignStatus
155
- : roleOutcome.kind === "audit_escalation"
156
- ? "escalate"
157
- : undefined;
158
- if (status === "escalate") {
159
- const reason =
160
- typeof facts?.reason === "string"
161
- ? facts.reason
162
- : "diarist escalated without reason";
163
- return { identity: { kind: "escalate", reason } };
164
- }
162
+ // Escalate routing is a boolean over the preserved sequence (#881). Reasons and
163
+ // payload bodies stay on roleOutcome — never rewritten into a sole identity reason.
164
+ if (courtDiaristEscalated(roleOutcome)) {
165
+ return {
166
+ identity: { kind: "escalate" },
167
+ };
165
168
  }
166
169
 
167
170
  if (result.exitCode !== 0) {
@@ -183,9 +186,13 @@ async function invokeCourtDiarist(input: {
183
186
  Number.isSafeInteger(asserted) &&
184
187
  asserted >= 1
185
188
  ) {
186
- return { identity: { kind: "ticket", ticketNumber: asserted } };
189
+ return {
190
+ identity: { kind: "ticket", ticketNumber: asserted },
191
+ };
187
192
  }
188
- return { identity: { kind: "unbound" } };
193
+ return {
194
+ identity: { kind: "unbound" },
195
+ };
189
196
  }
190
197
 
191
198
  /**
@@ -226,7 +233,7 @@ export async function runCountersignCourtDiaristStation(
226
233
 
227
234
  if (outcome.identity.kind === "escalate") {
228
235
  throw new StationChildExhaustedError(
229
- `court diarist station escalated (cannot identify court target): ${outcome.identity.reason}`,
236
+ "court diarist station escalated (cannot identify court target)",
230
237
  );
231
238
  }
232
239
  if (outcome.failedWithoutEscalate !== undefined) {
@@ -309,7 +316,7 @@ export async function runPublicCountersign(
309
316
  code: null,
310
317
  stderr: "",
311
318
  thrown: new Error(
312
- `court diarist station escalated (cannot identify court target): ${outcome.identity.reason}`,
319
+ "court diarist station escalated (cannot identify court target)",
313
320
  ),
314
321
  },
315
322
  countersignAdapters(),
@@ -35,11 +35,12 @@ import {
35
35
  trySettleDiaristTerminalResult,
36
36
  } from "./settlement.ts";
37
37
  import type { CliIo } from "./cli-io.ts";
38
- import { lastRolePayloadRecord, type TerminalResult } from "./terminal.ts";
38
+ import type { TerminalResult } from "./terminal.ts";
39
39
  import {
40
40
  projectRoleTurnRequest,
41
41
  type RoleTurnRequestProjectionOptions,
42
42
  } from "./turn-request.ts";
43
+ import { readRunTicketNumber } from "../run-ticket-number.ts";
43
44
 
44
45
  export type DiaristRunEnv = PostAdmissionEnv & {
45
46
  principalAuthority: DurablePrincipalAuthority;
@@ -191,33 +192,15 @@ export async function runPublicDiarist(
191
192
  adapters: diaristAdapters(),
192
193
  ...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
193
194
  }).then(async (result) => {
194
- // Accept may have bound ticket onto durable pages after LLM assertion.
195
- // Mirror only lawful non-escalate accepted terminals — escalate must not
196
- // leak an unverified ticketNumber into the caller-visible typed key.
195
+ // Accept hook binds ticket onto durable pages (#771). Mirror that page fact
196
+ // onto the caller-visible admitted object — never pick a ticketNumber out of
197
+ // the payload sequence (#881 sole-collapse ban on ticket/escalate).
197
198
  if (admitted.ticketNumber === undefined && result.admitted !== undefined) {
198
- const roleOutcome = result.terminal?.roleOutcome;
199
- const facts =
200
- roleOutcome?.kind === "accepted"
201
- ? lastRolePayloadRecord(roleOutcome.payloads ?? [])
202
- : undefined;
203
- if (
204
- roleOutcome !== undefined &&
205
- roleOutcome.kind === "accepted" &&
206
- facts?.status !== "escalate"
207
- ) {
208
- const raw =
209
- typeof facts?.ticketNumber === "number"
210
- ? facts.ticketNumber
211
- : typeof facts?.sitian === "object"
212
- && facts.sitian !== null
213
- && typeof (facts.sitian as { ticketNumber?: unknown }).ticketNumber === "number"
214
- ? (facts.sitian as { ticketNumber: number }).ticketNumber
215
- : undefined;
216
- if (typeof raw === "number" && Number.isSafeInteger(raw) && raw >= 1) {
217
- (admitted as { ticketNumber?: number }).ticketNumber = raw;
218
- if (result.admitted.ticketNumber === undefined) {
219
- (result.admitted as { ticketNumber?: number }).ticketNumber = raw;
220
- }
199
+ const fromPages = await readRunTicketNumber(admitted.runDirectory);
200
+ if (fromPages !== undefined) {
201
+ await bindAdmittedTicketNumber(admitted, fromPages);
202
+ if (result.admitted.ticketNumber === undefined) {
203
+ (result.admitted as { ticketNumber?: number }).ticketNumber = fromPages;
221
204
  }
222
205
  }
223
206
  }
@@ -122,7 +122,9 @@ function instructionSeatAdapters(options?: {
122
122
  return infrastructureFailure === undefined
123
123
  ? result.knownFailure
124
124
  : {
125
- cause: infrastructureFailure.cause,
125
+ ...(infrastructureFailure.cause === undefined
126
+ ? {}
127
+ : { cause: infrastructureFailure.cause }),
126
128
  diagnostic: infrastructureFailure.diagnostic,
127
129
  ...(infrastructureFailure.identity === undefined
128
130
  ? {}
@@ -63,7 +63,9 @@ function judgeAdapters(): PostAdmissionAdapters<AdmittedJudgeInvocation> {
63
63
  (infrastructureFailure === undefined
64
64
  ? undefined
65
65
  : {
66
- cause: infrastructureFailure.cause,
66
+ ...(infrastructureFailure.cause === undefined
67
+ ? {}
68
+ : { cause: infrastructureFailure.cause }),
67
69
  diagnostic: infrastructureFailure.diagnostic,
68
70
  ...(infrastructureFailure.identity === undefined
69
71
  ? {}
@@ -104,7 +104,9 @@ function reviewerAdapters(
104
104
  return infrastructureFailure === undefined
105
105
  ? result.knownFailure
106
106
  : {
107
- cause: infrastructureFailure.cause,
107
+ ...(infrastructureFailure.cause === undefined
108
+ ? {}
109
+ : { cause: infrastructureFailure.cause }),
108
110
  diagnostic: infrastructureFailure.diagnostic,
109
111
  ...(infrastructureFailure.identity === undefined
110
112
  ? {}