@akagilnc/pi-workflow-roles 0.1.5368 → 0.1.5382

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 (188) hide show
  1. package/dist/acp-host/production-host.js +1192 -1195
  2. package/dist/activation-ledger-git.js +2 -2
  3. package/dist/activation-ledger-topology.js +1 -7
  4. package/dist/analyst-entry.js +4 -8
  5. package/dist/analyst-gate-cycles-read.js +3 -11
  6. package/dist/analyst-index.js +3 -9
  7. package/dist/analyst-ledger.js +1 -11
  8. package/dist/analyst-metric-families/acceptance-success-rework.js +1 -3
  9. package/dist/analyst-metric-families/round-timeline.js +1 -3
  10. package/dist/analyst-page.js +2 -2
  11. package/dist/archivist-record-topology.js +2 -2
  12. package/dist/audit-escalation.js +2 -1
  13. package/dist/auditor-soul.js +2 -1
  14. package/dist/collector-config.js +6 -8
  15. package/dist/collector-tool-schemas.js +18 -0
  16. package/dist/compliance-transport.js +2 -1
  17. package/dist/countersign-contracts.js +2 -1
  18. package/dist/diarist-contracts.js +29 -2
  19. package/dist/doctor-contracts.js +1 -3
  20. package/dist/doctor-evidence.js +7 -8
  21. package/dist/dossier-resolution.js +1 -3
  22. package/dist/engine-detour-usage.js +1 -3
  23. package/dist/filed-submission.js +28 -0
  24. package/dist/gatekeeper-role.js +8 -19
  25. package/dist/gleaner-left-contracts.js +18 -1
  26. package/dist/headless-host/description.js +14 -17
  27. package/dist/headless-host/production-host.js +1210 -1215
  28. package/dist/host-session-record.js +3 -2
  29. package/dist/inspector-contracts.js +2 -1
  30. package/dist/judge-role.js +8 -16
  31. package/dist/ledger-session-read.js +1 -3
  32. package/dist/merger-contracts.js +9 -9
  33. package/dist/migrate-book-topology.js +610 -321
  34. package/dist/navigator-attendance.js +9 -11
  35. package/dist/navigator-invocation-identity.js +3 -2
  36. package/dist/navigator-public-session.js +3 -5
  37. package/dist/navigator-session-contracts.js +8 -10
  38. package/dist/navigator-work-base.js +5 -8
  39. package/dist/notary-contracts.js +3 -5
  40. package/dist/package-contracts/auditor-output.js +1 -3
  41. package/dist/package-contracts/fixer-packet.js +3 -6
  42. package/dist/package-contracts/gatekeeper-output.js +1 -3
  43. package/dist/package-contracts/navigator-output.js +8 -8
  44. package/dist/package-contracts/reviewer-output.js +16 -0
  45. package/dist/package-contracts/terminating-infrastructure.js +5 -9
  46. package/dist/package-contracts/terminating-tools.js +4 -3
  47. package/dist/package-contracts/worker-output.js +18 -2
  48. package/dist/public-cli/analyst-run.js +3 -2
  49. package/dist/public-cli/auto-resume.js +3 -8
  50. package/dist/public-cli/cli.js +9 -8
  51. package/dist/public-cli/config.js +8 -11
  52. package/dist/public-cli/countersign-run.js +1 -3
  53. package/dist/public-cli/host-providers.js +7 -14
  54. package/dist/public-cli/instruction-seat-run.js +12 -14
  55. package/dist/public-cli/invocation.js +9 -8
  56. package/dist/public-cli/main.js +1820 -1599
  57. package/dist/public-cli/post-admission.js +4 -3
  58. package/dist/public-cli/run-lifecycle.js +14 -26
  59. package/dist/public-cli/settlement.js +10 -26
  60. package/dist/public-role-summons.js +3 -2
  61. package/dist/receipt-delivery-policy.js +1 -3
  62. package/dist/role-run-placement.js +3 -5
  63. package/dist/role-run-relocation.js +12 -19
  64. package/dist/role-submission-declarations.js +131 -0
  65. package/dist/run-terminal-artifacts.js +1 -11
  66. package/dist/run-ticket-number.js +2 -4
  67. package/dist/secretariat-contracts.js +23 -1
  68. package/dist/session-assistant-usage.js +2 -1
  69. package/dist/session-dialogue.js +1 -14
  70. package/dist/sitian-reader.js +2 -4
  71. package/dist/submission-correctable-error.js +2 -1
  72. package/dist/submission-errors.js +2 -1
  73. package/dist/submission-gate.js +6 -8
  74. package/dist/submission-ledger.js +4 -5
  75. package/dist/ticket-provenance-contracts.js +1 -3
  76. package/dist/ticket-provenance.js +2 -1
  77. package/dist/typed-provider-http.js +4 -7
  78. package/dist/unknown-value.js +28 -0
  79. package/dist/user-dialogue-stdin.js +2 -1
  80. package/package.json +1 -1
  81. package/src/acp-host/role-turn-host.ts +4 -2
  82. package/src/activation-ledger-git.ts +3 -2
  83. package/src/activation-ledger-topology.ts +4 -12
  84. package/src/analyst-entry.ts +3 -11
  85. package/src/analyst-gate-cycles-read.ts +3 -15
  86. package/src/analyst-index.ts +3 -13
  87. package/src/analyst-ledger.ts +2 -16
  88. package/src/analyst-metric-families/acceptance-success-rework.ts +2 -4
  89. package/src/analyst-metric-families/round-timeline.ts +2 -4
  90. package/src/analyst-page.ts +2 -2
  91. package/src/archivist-record-topology.ts +2 -2
  92. package/src/audit-escalation.ts +2 -1
  93. package/src/auditor-role.ts +2 -8
  94. package/src/auditor-soul.ts +3 -1
  95. package/src/book-topology-migration-placement.ts +5 -16
  96. package/src/book-topology-migrators-t11.ts +16 -15
  97. package/src/book-topology-mixed-volume-migrators.ts +3 -6
  98. package/src/book-topology-record-class-migrators.ts +7 -10
  99. package/src/book-topology-runs-migrator.ts +4 -3
  100. package/src/collector-config.ts +7 -9
  101. package/src/collector-role.ts +5 -28
  102. package/src/compliance-transport.ts +2 -1
  103. package/src/countersign-contracts.ts +2 -1
  104. package/src/countersign-role.ts +2 -7
  105. package/src/diarist-contracts.ts +40 -2
  106. package/src/diarist-role.ts +4 -44
  107. package/src/doctor-contracts.ts +3 -1
  108. package/src/doctor-evidence.ts +8 -7
  109. package/src/doctor-role.ts +20 -13
  110. package/src/dossier-resolution.ts +2 -4
  111. package/src/engine-detour-tool.ts +0 -2
  112. package/src/engine-detour-usage.ts +2 -4
  113. package/src/external-host-turn-loop.ts +3 -1
  114. package/src/factory-board.ts +1 -8
  115. package/src/filed-submission.ts +44 -0
  116. package/src/gatekeeper-role.ts +8 -24
  117. package/src/gleaner-left-contracts.ts +29 -2
  118. package/src/gleaner-left-role.ts +4 -32
  119. package/src/headless-host/description.ts +15 -18
  120. package/src/headless-host/role-turn-host.ts +10 -9
  121. package/src/host-session-record.ts +4 -2
  122. package/src/inspector-contracts.ts +2 -1
  123. package/src/inspector-role.ts +2 -7
  124. package/src/judge-role.ts +12 -24
  125. package/src/ledger-session-read.ts +2 -5
  126. package/src/merger-contracts.ts +10 -9
  127. package/src/merger-role.ts +5 -8
  128. package/src/navigator-attendance.ts +10 -12
  129. package/src/navigator-invocation-identity.ts +3 -2
  130. package/src/navigator-public-session.ts +4 -6
  131. package/src/navigator-role.ts +2 -8
  132. package/src/navigator-session-contracts.ts +8 -11
  133. package/src/navigator-work-base.ts +5 -8
  134. package/src/navigator-work-context.ts +2 -1
  135. package/src/notary-contracts.ts +4 -6
  136. package/src/notary-role.ts +5 -20
  137. package/src/oauth-keepalive.ts +5 -18
  138. package/src/package-contracts/auditor-output.ts +2 -4
  139. package/src/package-contracts/fixer-packet.ts +4 -6
  140. package/src/package-contracts/gatekeeper-output.ts +2 -4
  141. package/src/package-contracts/judge-output.ts +0 -1
  142. package/src/package-contracts/navigator-output.ts +8 -8
  143. package/src/package-contracts/reviewer-output.ts +25 -0
  144. package/src/package-contracts/terminating-infrastructure.ts +6 -20
  145. package/src/package-contracts/terminating-tools.ts +8 -12
  146. package/src/package-contracts/worker-output.ts +43 -5
  147. package/src/pi/adapter.ts +0 -1
  148. package/src/pi/role-turn-host.ts +0 -1
  149. package/src/public-cli/analyst-run.ts +4 -2
  150. package/src/public-cli/auto-resume.ts +4 -11
  151. package/src/public-cli/cli.ts +10 -10
  152. package/src/public-cli/config.ts +8 -27
  153. package/src/public-cli/countersign-run.ts +2 -4
  154. package/src/public-cli/host-providers.ts +10 -27
  155. package/src/public-cli/instruction-seat-run.ts +14 -15
  156. package/src/public-cli/invocation.ts +11 -10
  157. package/src/public-cli/post-admission.ts +5 -3
  158. package/src/public-cli/run-lifecycle.ts +20 -41
  159. package/src/public-cli/settlement.ts +11 -33
  160. package/src/public-role-summons.ts +4 -3
  161. package/src/receipt-delivery-policy.ts +1 -4
  162. package/src/reviewer-role.ts +7 -36
  163. package/src/role-envelope.ts +5 -3
  164. package/src/role-run-placement.ts +4 -10
  165. package/src/role-run-relocation.ts +13 -23
  166. package/src/role-runtime.ts +16 -50
  167. package/src/role-submission-declarations.ts +143 -0
  168. package/src/run-terminal-artifacts.ts +2 -16
  169. package/src/run-ticket-number.ts +4 -10
  170. package/src/secretariat-contracts.ts +35 -0
  171. package/src/secretariat-role.ts +4 -40
  172. package/src/session-assistant-usage.ts +3 -1
  173. package/src/session-dialogue.ts +1 -4
  174. package/src/session-identity.ts +0 -1
  175. package/src/sitian-reader.ts +2 -4
  176. package/src/submission-correctable-error.ts +3 -1
  177. package/src/submission-errors.ts +2 -1
  178. package/src/submission-gate.ts +10 -9
  179. package/src/submission-ledger.ts +5 -6
  180. package/src/ticket-provenance-contracts.ts +1 -4
  181. package/src/ticket-provenance.ts +3 -1
  182. package/src/ticket-snapshot.ts +2 -4
  183. package/src/ticket-trajectory.ts +1 -8
  184. package/src/typed-provider-http.ts +5 -15
  185. package/src/unknown-value.ts +34 -0
  186. package/src/user-dialogue-stdin.ts +3 -1
  187. package/src/worker-role.ts +63 -83
  188. package/src/worker-submission-gates.ts +8 -7
@@ -19,6 +19,7 @@ import {
19
19
  isPublicConfigurableSeat,
20
20
  seatModelOnly
21
21
  } from "./registry.js";
22
+ import { isEnoent, isRecord } from "../unknown-value.js";
22
23
  const GATE_OFFICER_SEATS = PUBLIC_ROLE_RECORDS.flatMap(
23
24
  (record) => "provinceConfig" in record && record.provinceConfig === true ? [record.role] : []
24
25
  );
@@ -37,9 +38,7 @@ async function loadPublicCliConfig(home) {
37
38
  const raw = await readFile(path, "utf8");
38
39
  return parsePublicCliConfig(JSON.parse(raw));
39
40
  } catch (error) {
40
- if (error instanceof Error && "code" in error && error.code === "ENOENT") {
41
- return { seats: {} };
42
- }
41
+ if (isEnoent(error)) return { seats: {} };
43
42
  throw error;
44
43
  }
45
44
  }
@@ -260,7 +259,7 @@ function serializePublicCliConfig(config) {
260
259
  };
261
260
  }
262
261
  function parsePublicCliConfig(value) {
263
- if (value === null || typeof value !== "object" || Array.isArray(value)) {
262
+ if (!isRecord(value)) {
264
263
  throw new Error("public CLI config must be an object");
265
264
  }
266
265
  const record = value;
@@ -270,7 +269,7 @@ function parsePublicCliConfig(value) {
270
269
  }
271
270
  const unknownSeats = {};
272
271
  if (record.unknownSeats !== void 0) {
273
- if (record.unknownSeats === null || typeof record.unknownSeats !== "object" || Array.isArray(record.unknownSeats)) {
272
+ if (!isRecord(record.unknownSeats)) {
274
273
  throw new Error("public CLI config.unknownSeats must be an object");
275
274
  }
276
275
  Object.assign(unknownSeats, record.unknownSeats);
@@ -283,7 +282,7 @@ function parsePublicCliConfig(value) {
283
282
  if (record.seats === void 0) {
284
283
  return withOpaque({});
285
284
  }
286
- if (record.seats === null || typeof record.seats !== "object" || Array.isArray(record.seats)) {
285
+ if (!isRecord(record.seats)) {
287
286
  throw new Error("public CLI config.seats must be an object");
288
287
  }
289
288
  const seats = {};
@@ -299,7 +298,7 @@ function parsePublicCliConfig(value) {
299
298
  return withOpaque(seats);
300
299
  }
301
300
  function parseSeatModelConfig(value, seat) {
302
- if (value === null || typeof value !== "object" || Array.isArray(value)) {
301
+ if (!isRecord(value)) {
303
302
  throw new Error(`config seat ${seat} must be an object`);
304
303
  }
305
304
  const raw = value;
@@ -481,7 +480,7 @@ function listRolesForDisplay(config, credentials, invocation) {
481
480
  return effectiveSeatConfigurations(config, credentials, invocation);
482
481
  }
483
482
  function credentialProvidersFromAuthData(data) {
484
- if (data === null || typeof data !== "object" || Array.isArray(data)) {
483
+ if (!isRecord(data)) {
485
484
  return { "openai-codex": false, xai: false };
486
485
  }
487
486
  const record = data;
@@ -498,9 +497,7 @@ async function loadCredentialProviders(agentDir) {
498
497
  const raw = await readFile(join(agentDir, "auth.json"), "utf8");
499
498
  return credentialProvidersFromAuthData(JSON.parse(raw));
500
499
  } catch (error) {
501
- if (error instanceof Error && "code" in error && error.code === "ENOENT") {
502
- return { "openai-codex": false, xai: false };
503
- }
500
+ if (isEnoent(error)) return { "openai-codex": false, xai: false };
504
501
  throw error;
505
502
  }
506
503
  }
@@ -1,7 +1,5 @@
1
1
  import { readableGateItem } from "../readable-gate-item.js";
2
- function isRecord(value) {
3
- return typeof value === "object" && value !== null && !Array.isArray(value);
4
- }
2
+ import { isRecord } from "../unknown-value.js";
5
3
  function isEscalatePayload(payload) {
6
4
  if (!isRecord(payload))
7
5
  return false;
@@ -10,6 +10,7 @@
10
10
  */
11
11
  import { readFileSync } from "node:fs";
12
12
  import { join } from "node:path";
13
+ import { isEnoent, isRecord } from "../unknown-value.js";
13
14
  export function hostProvidersPath(home) {
14
15
  if (typeof home !== "string" || home.trim() === "") {
15
16
  throw new Error("home must be explicitly provided");
@@ -21,7 +22,7 @@ export function hermesProviderModelsCachePath(home) {
21
22
  return join(home, ".hermes", "provider_models_cache.json");
22
23
  }
23
24
  export function parseHostProvidersTable(value) {
24
- if (value === null || typeof value !== "object" || Array.isArray(value)) {
25
+ if (!isRecord(value)) {
25
26
  throw new Error("host-providers.json must be an object");
26
27
  }
27
28
  const out = {};
@@ -29,9 +30,7 @@ export function parseHostProvidersTable(value) {
29
30
  if (host.trim() === "") {
30
31
  throw new Error("host-providers.json host key must be non-empty");
31
32
  }
32
- if (byProvider === null ||
33
- typeof byProvider !== "object" ||
34
- Array.isArray(byProvider)) {
33
+ if (!isRecord(byProvider)) {
35
34
  throw new Error(`host-providers.json[${host}] must be an object`);
36
35
  }
37
36
  const providers = {};
@@ -61,11 +60,8 @@ export function loadHostProvidersTable(home) {
61
60
  return parseHostProvidersTable(JSON.parse(raw));
62
61
  }
63
62
  catch (error) {
64
- if (error instanceof Error &&
65
- "code" in error &&
66
- error.code === "ENOENT") {
63
+ if (isEnoent(error))
67
64
  return {};
68
- }
69
65
  throw error;
70
66
  }
71
67
  }
@@ -86,20 +82,17 @@ export function listHermesProvidersForModel(home, seatModel) {
86
82
  text = readFileSync(path, "utf8");
87
83
  }
88
84
  catch (error) {
89
- if (error instanceof Error &&
90
- "code" in error &&
91
- error.code === "ENOENT") {
85
+ if (isEnoent(error))
92
86
  return [];
93
- }
94
87
  throw error;
95
88
  }
96
89
  const raw = JSON.parse(text);
97
- if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
90
+ if (!isRecord(raw)) {
98
91
  throw new Error(`hermes provider_models_cache.json must be an object: ${path}`);
99
92
  }
100
93
  const found = [];
101
94
  for (const [provider, entry] of Object.entries(raw)) {
102
- if (entry === null || typeof entry !== "object" || Array.isArray(entry)) {
95
+ if (!isRecord(entry)) {
103
96
  continue;
104
97
  }
105
98
  const models = entry.models;
@@ -9,6 +9,8 @@ import { AUDITOR_DOSSIER_PROMPT } from "../compliance-transport.js";
9
9
  import { bookDirectOfficerRunPointer } from "../archivist-record-pointer.js";
10
10
  import { createPiDoctorAuditor } from "../doctor-auditor.js";
11
11
  import { officerConclusionReask, gateOfficerForSubject } from "../gatekeeper-role.js";
12
+ import { receivedDiscriminator } from "../submission-errors.js";
13
+ import { REVIEW_QUEUE_STATUSES } from "../review-submission.js";
12
14
  import { runJudgeGates } from "../judge-role.js";
13
15
  import { WORKER_DONE_STATUSES } from "../worker-submission-contracts.js";
14
16
  import { SECRETARIAT_GATE_OFFICER_ENTRY_TYPE } from "../secretariat-contracts.js";
@@ -34,9 +36,9 @@ import { warnMissingMethodSkills } from "./machine-method-skills.js";
34
36
  import { formatTerminalResult, isLawfulTypedTerminalOutcome, } from "./terminal.js";
35
37
  import { admittedSeatTurnDetails, projectPublicTurnAxes, projectRoleTurnRequest, requiredMethodSkills, } from "./turn-request.js";
36
38
  import { invokeCourtDiarist, latestPayloadEscalated, } from "./countersign-run.js";
39
+ import { isRecord, errorText } from "../unknown-value.js";
37
40
  const AUDITED_ROLES = new Set(["judge", "fixer", "coder", "secretariat", "countersign", "doctor"]);
38
41
  const SECRETARIAT_STATUS_REASK = "secretariatStatus 不是 converged、escalate 之一。请重新交卷,secretariatStatus 写明其一。";
39
- const COUNTERSIGN_STATUS_REASK = "status 不是 converged、continue、escalate 三态之一。请重新交卷,status 写明其一。";
40
42
  const WORKER_ROUTING_STATUSES = new Set([
41
43
  "planned", "completed", "refused", "partially_completed", "unfinished",
42
44
  ]);
@@ -45,8 +47,7 @@ const DOCTOR_ROUTING_STATUSES = new Set(["completed", "refused"]);
45
47
  const DOCTOR_STATUS_REASK = "status 不是 completed、refused 之一。请重新交卷,status 写明其一。";
46
48
  const POST_SUBMISSION_ROUTING = {
47
49
  judge: {
48
- statuses: new Set(["converged", "continue", "escalate"]),
49
- reask: "status 不是 converged、continue、escalate 三态之一。请重新交卷,status 写明其一。",
50
+ statuses: REVIEW_QUEUE_STATUSES,
50
51
  },
51
52
  coder: {
52
53
  statuses: WORKER_ROUTING_STATUSES,
@@ -74,12 +75,10 @@ function unreadablePostSubmissionStatus(admitted, terminal) {
74
75
  if (route === undefined)
75
76
  return undefined;
76
77
  const payload = terminal.roleOutcome.payloads?.at(-1);
77
- const status = payload !== null && typeof payload === "object" && !Array.isArray(payload)
78
- ? payload.status
79
- : undefined;
78
+ const status = receivedDiscriminator(payload, "status");
80
79
  return typeof status === "string" && route.statuses.has(status)
81
80
  ? undefined
82
- : route.reask;
81
+ : route.reask ?? officerConclusionReask(status);
83
82
  }
84
83
  async function reaskUnreadablePostSubmissionStatus(admitted, terminal, env, io) {
85
84
  const message = unreadablePostSubmissionStatus(admitted, terminal);
@@ -484,7 +483,7 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
484
483
  auditorTicket = await readBoardTicketNumber(resolved.runDirectory);
485
484
  }
486
485
  catch (error) {
487
- presentStructuralRejection(new CliUsageError(error instanceof Error ? error.message : String(error)), io);
486
+ presentStructuralRejection(new CliUsageError(errorText(error)), io);
488
487
  return { exitCode: 2 };
489
488
  }
490
489
  if (auditorSource === undefined)
@@ -715,7 +714,6 @@ export async function runPublicInstructionSeatResume(request, env, io) {
715
714
  }
716
715
  return resume();
717
716
  }
718
- const QUEUE_CONCLUSIONS = new Set(["converged", "continue", "escalate"]);
719
717
  const GATE_CHILD_ROLES = new Set(["notary", "auditor", "inspector", "countersign"]);
720
718
  /**
721
719
  * A gate child's conclusion outside the three states goes back to that child.
@@ -728,7 +726,7 @@ async function queueConclusionFromChild(child, env, io) {
728
726
  for (;;) {
729
727
  const terminal = await trySettlePublicSeat(current, env.principalAuthority, undefined);
730
728
  const status = latestQueueStatus(terminal);
731
- if (terminal !== undefined && status !== undefined && QUEUE_CONCLUSIONS.has(status)) {
729
+ if (terminal !== undefined && typeof status === "string" && REVIEW_QUEUE_STATUSES.has(status)) {
732
730
  return { admitted: current, terminal, status };
733
731
  }
734
732
  const reasked = await runPublicInstructionSeatResume({
@@ -739,7 +737,7 @@ async function queueConclusionFromChild(child, env, io) {
739
737
  return { stop: reasked };
740
738
  }
741
739
  const reaskedStatus = latestQueueStatus(reasked.terminal);
742
- if (reaskedStatus !== undefined && QUEUE_CONCLUSIONS.has(reaskedStatus)) {
740
+ if (typeof reaskedStatus === "string" && REVIEW_QUEUE_STATUSES.has(reaskedStatus)) {
743
741
  return { admitted: reasked.admitted, terminal: reasked.terminal, status: reaskedStatus };
744
742
  }
745
743
  if (latestPayloadEscalated(reasked.terminal.roleOutcome))
@@ -785,14 +783,14 @@ async function auditSubmittedRole(turn, env, io, passedOfficer, passedReceipt, p
785
783
  const accepted = turn.terminal.roleOutcome.payloads?.at(-1);
786
784
  if (accepted === undefined)
787
785
  throw new Error("accepted submission has no payload");
788
- const record = accepted !== null && typeof accepted === "object" && !Array.isArray(accepted)
786
+ const record = isRecord(accepted)
789
787
  ? accepted : undefined;
790
788
  const status = admitted.role === "secretariat" ? record?.secretariatStatus : record?.status;
791
789
  if (admitted.role === "secretariat" && status !== "converged" && status !== "escalate") {
792
790
  return runPublicInstructionSeatResume({ runId: admitted.runId, message: SECRETARIAT_STATUS_REASK }, { ...env, autoResumeLimit: 0 }, io);
793
791
  }
794
- if (admitted.role === "countersign" && (typeof status !== "string" || !QUEUE_CONCLUSIONS.has(status))) {
795
- return runPublicInstructionSeatResume({ runId: admitted.runId, message: COUNTERSIGN_STATUS_REASK }, { ...env, autoResumeLimit: 0 }, io);
792
+ if (admitted.role === "countersign" && (typeof status !== "string" || !REVIEW_QUEUE_STATUSES.has(status))) {
793
+ return runPublicInstructionSeatResume({ runId: admitted.runId, message: officerConclusionReask(status) }, { ...env, autoResumeLimit: 0 }, io);
796
794
  }
797
795
  // Self-escalation is a valid open routing state for these seats only.
798
796
  // Doctor's declared domain is completed|refused — an open "escalate" must
@@ -27,6 +27,7 @@ import { NotarySourceRunError, resolveNotarySourceRunLocator, } from "../notary-
27
27
  import { appendEngineSessionMaterial, } from "../package-resources/engine-material.js";
28
28
  import { CliUsageError } from "./cli-errors.js";
29
29
  import { REJECTED_PUBLIC_SPELLINGS, createTypedOptionConsumer, evaluateAnalystModeOptionContract, optionsForOwner, resolveAnalystMode, } from "./option-definitions.js";
30
+ import { errorText, isRecord } from "../unknown-value.js";
30
31
  /** Issue a principal from the single authoritative role-run placement. */
31
32
  export function issueAdmissionPlacement(authority, request) {
32
33
  const ledgerHome = resolveActivationLedgerHome(request.home);
@@ -880,7 +881,7 @@ export async function admitPublicRole(role, parsed, env, override) {
880
881
  repository = parseCollectorRepository(parsed.repo);
881
882
  }
882
883
  catch (error) {
883
- const detail = error instanceof Error ? error.message : String(error);
884
+ const detail = errorText(error);
884
885
  throw new CliUsageError(detail, { cause: error });
885
886
  }
886
887
  }
@@ -895,7 +896,7 @@ export async function admitPublicRole(role, parsed, env, override) {
895
896
  manifestCanonicalJson = manifest.canonicalJson;
896
897
  }
897
898
  catch (error) {
898
- throw new CliUsageError(error instanceof Error ? error.message : String(error), { cause: error });
899
+ throw new CliUsageError(errorText(error), { cause: error });
899
900
  }
900
901
  }
901
902
  const manifestDigest = manifest.digest;
@@ -948,7 +949,7 @@ export async function admitPublicRole(role, parsed, env, override) {
948
949
  catch (error) {
949
950
  if (error instanceof CliUsageError)
950
951
  throw error;
951
- const detail = error instanceof Error ? error.message : String(error);
952
+ const detail = errorText(error);
952
953
  throw new CliUsageError(detail, { cause: error });
953
954
  }
954
955
  if (parsed.runs === undefined) {
@@ -966,7 +967,7 @@ export async function admitPublicRole(role, parsed, env, override) {
966
967
  catch (error) {
967
968
  if (error instanceof CliUsageError)
968
969
  throw error;
969
- const detail = error instanceof Error ? error.message : String(error);
970
+ const detail = errorText(error);
970
971
  throw new CliUsageError(`doctor case could not be constructed from retained evidence: ${detail}`, { cause: error });
971
972
  }
972
973
  frozenAttachments = await freezeAttachments(attachmentPaths, placed.attachmentsDirectory);
@@ -1322,7 +1323,7 @@ export async function materializeCountersignInvocation(admitted, options) {
1322
1323
  export async function loadAdmittedJudgeRequest(runDirectory) {
1323
1324
  try {
1324
1325
  const raw = JSON.parse(await readFile(join(runDirectory, "admitted-request.json"), "utf8"));
1325
- if (raw === null || typeof raw !== "object" || Array.isArray(raw))
1326
+ if (!isRecord(raw))
1326
1327
  return undefined;
1327
1328
  const record = raw;
1328
1329
  if (!packagedPublicInstructionSubject(record.role))
@@ -1354,7 +1355,7 @@ function parsePositivePrOption(raw) {
1354
1355
  return parseCollectorPrNumber(raw);
1355
1356
  }
1356
1357
  catch (error) {
1357
- throw new CliUsageError(error instanceof Error ? error.message : String(error), { cause: error });
1358
+ throw new CliUsageError(errorText(error), { cause: error });
1358
1359
  }
1359
1360
  }
1360
1361
  function parseRepoOption(raw) {
@@ -1396,7 +1397,7 @@ export function resolveGitHubRemoteRepository(projectRoot) {
1396
1397
  return parseCollectorRepository(ownerRepo);
1397
1398
  }
1398
1399
  catch (error) {
1399
- const detail = error instanceof Error ? error.message : String(error);
1400
+ const detail = errorText(error);
1400
1401
  throw new CliUsageError(detail, { cause: error });
1401
1402
  }
1402
1403
  }
@@ -1456,7 +1457,7 @@ export async function resolveDoctorCaseRunsPath(options) {
1456
1457
  real = await realpath(resolved);
1457
1458
  }
1458
1459
  catch (error) {
1459
- const detail = error instanceof Error ? error.message : String(error);
1460
+ const detail = errorText(error);
1460
1461
  throw new CliUsageError(`doctor --runs is not a readable retained runs root: ${detail}`, { cause: error });
1461
1462
  }
1462
1463
  const normalized = real.split(sep).join("/");