@akagilnc/pi-workflow-roles 0.1.3572 → 0.1.3621

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 (129) hide show
  1. package/dist/acp-host/production-host.js +20239 -10556
  2. package/dist/analyst-gate-cycles-read.js +387 -0
  3. package/dist/archivist-record-entry.js +22 -1
  4. package/dist/audit-escalation.js +6 -0
  5. package/dist/auditor-soul.js +74 -0
  6. package/dist/collector-config.js +86 -0
  7. package/dist/collector-evidence.js +316 -0
  8. package/dist/collector-github.js +527 -0
  9. package/dist/collector-ledger.js +853 -0
  10. package/dist/collector-tool-schemas.js +33 -0
  11. package/dist/compliance-transport.js +104 -52
  12. package/dist/diarist-mechanical.js +411 -0
  13. package/dist/diarist-ticket-resolution.js +177 -0
  14. package/dist/diarist.js +311 -0
  15. package/dist/doctor-auditor.js +25 -0
  16. package/dist/doctor-contracts.js +2 -0
  17. package/dist/doctor-evidence.js +142 -0
  18. package/dist/gatekeeper-role.js +104 -81
  19. package/dist/host-transition-prior-native.js +78 -0
  20. package/dist/institutional-resolution.js +1 -95
  21. package/dist/judge-auditor.js +26 -0
  22. package/dist/ledger-session-read.js +227 -0
  23. package/dist/merger-git-state.js +78 -0
  24. package/dist/navigator-attendance.js +15 -7
  25. package/dist/navigator-public-session.js +170 -0
  26. package/dist/navigator-session-contracts.js +36 -40
  27. package/dist/notary-source-run.js +122 -0
  28. package/dist/package-contracts/auditor-output.js +66 -0
  29. package/dist/package-contracts/evidence-child-output.js +34 -0
  30. package/dist/package-contracts/judge-output.js +2 -0
  31. package/dist/package-contracts/terminating-tools.js +25 -2
  32. package/dist/package-resources/method-skill.js +254 -0
  33. package/dist/packaged-role-registry.js +36 -0
  34. package/dist/pi/durable-principal.js +61 -0
  35. package/dist/pi/in-process-session.js +25 -6
  36. package/dist/pi/known-failure.js +52 -0
  37. package/dist/pi/role-turn-host.js +430 -0
  38. package/dist/public-cli/auto-resume.js +414 -0
  39. package/dist/public-cli/cli-errors.js +8 -0
  40. package/dist/public-cli/cli-io.js +1 -0
  41. package/dist/public-cli/command-renderer.js +5 -0
  42. package/dist/public-cli/doctor-run.js +84 -0
  43. package/dist/public-cli/inspector-run.js +136 -0
  44. package/dist/public-cli/instruction-seat-run.js +256 -0
  45. package/dist/public-cli/invocation.js +2225 -0
  46. package/dist/public-cli/judge-run.js +118 -0
  47. package/dist/public-cli/load-production-acp-host.js +40 -0
  48. package/dist/public-cli/main.js +1140 -652
  49. package/dist/public-cli/notary-run.js +161 -0
  50. package/dist/public-cli/option-definitions.js +1192 -0
  51. package/dist/public-cli/post-admission.js +667 -0
  52. package/dist/public-cli/public-run-credentials.js +49 -0
  53. package/dist/public-cli/registry.js +4 -1
  54. package/dist/public-cli/reviewer-dispatch-rejection.js +77 -0
  55. package/dist/public-cli/run-lifecycle.js +1344 -0
  56. package/dist/public-cli/seat-ticket-binding.js +113 -0
  57. package/dist/public-cli/settlement.js +3441 -0
  58. package/dist/public-cli/terminal.js +135 -0
  59. package/dist/public-cli/turn-request.js +25 -0
  60. package/dist/public-role-summons.js +301 -0
  61. package/dist/receipt-delivery-policy.js +10 -0
  62. package/dist/reviewer-child-executor.js +80 -8
  63. package/dist/reviewer-execution-ledger.js +2 -1
  64. package/dist/run-terminal-artifacts.js +195 -0
  65. package/dist/run-ticket-number.js +40 -0
  66. package/dist/session-assistant-usage.js +107 -0
  67. package/dist/shape-unreadable-failure.js +34 -0
  68. package/dist/submission-errors.js +3 -0
  69. package/dist/submission-ledger.js +388 -0
  70. package/dist/ticket-provenance-contracts.js +115 -0
  71. package/dist/ticket-provenance.js +340 -0
  72. package/extensions/role-runtime.ts +7 -0
  73. package/package.json +1 -1
  74. package/scripts/build-package.mjs +2 -1
  75. package/souls/doctor-auditor.md +1 -0
  76. package/souls/evidence-child.md +1 -0
  77. package/src/acp-host/production-host.ts +3 -0
  78. package/src/acp-host/role-envelope.ts +2 -2
  79. package/src/acp-host/role-turn-host.ts +9 -1
  80. package/src/analyst-gate-cycles-read.ts +37 -2
  81. package/src/archivist-record-entry.ts +45 -1
  82. package/src/audit-escalation.ts +17 -0
  83. package/src/auditor-role.ts +22 -0
  84. package/src/auditor-soul.ts +42 -0
  85. package/src/compliance-transport.ts +177 -66
  86. package/src/doctor-auditor.ts +10 -14
  87. package/src/doctor-contracts.ts +1 -0
  88. package/src/doctor-role.ts +2 -2
  89. package/src/evidence-child-role.ts +22 -0
  90. package/src/gatekeeper-pass-envelope.ts +109 -0
  91. package/src/gatekeeper-role.ts +145 -104
  92. package/src/host-contracts.ts +9 -1
  93. package/src/institutional-resolution.ts +8 -163
  94. package/src/judge-auditor.ts +10 -15
  95. package/src/judge-role.ts +8 -0
  96. package/src/navigator-attendance.ts +25 -9
  97. package/src/navigator-public-session.ts +217 -0
  98. package/src/navigator-session-contracts.ts +61 -54
  99. package/src/notary-source-run.ts +3 -1
  100. package/src/package-contracts/auditor-output.ts +82 -0
  101. package/src/package-contracts/evidence-child-output.ts +51 -0
  102. package/src/package-contracts/judge-output.ts +1 -0
  103. package/src/package-contracts/reviewer-output.ts +2 -2
  104. package/src/package-contracts/terminating-tools.ts +31 -1
  105. package/src/packaged-role-registry.ts +43 -0
  106. package/src/pi/adapter.ts +2 -1
  107. package/src/pi/in-process-session.ts +38 -12
  108. package/src/pi/role-turn-host.ts +34 -0
  109. package/src/public-cli/cli.ts +14 -6
  110. package/src/public-cli/instruction-seat-run.ts +289 -50
  111. package/src/public-cli/invocation.ts +66 -25
  112. package/src/public-cli/option-definitions.ts +61 -0
  113. package/src/public-cli/post-admission.ts +7 -1
  114. package/src/public-cli/registry.ts +4 -1
  115. package/src/public-cli/run-lifecycle.ts +22 -3
  116. package/src/public-cli/settlement.ts +120 -5
  117. package/src/public-cli/terminal.ts +2 -0
  118. package/src/public-role-summons.ts +431 -0
  119. package/src/receipt-delivery-policy.ts +10 -0
  120. package/src/reviewer-agent.ts +1 -1
  121. package/src/reviewer-child-executor.ts +114 -12
  122. package/src/reviewer-execution-ledger.ts +3 -2
  123. package/src/role-runtime.ts +158 -17
  124. package/src/session-assistant-usage.ts +119 -0
  125. package/src/session-opening-materials.ts +1 -1
  126. package/src/shape-unreadable-failure.ts +49 -0
  127. package/src/submission-errors.ts +3 -0
  128. package/dist/evidence-child-executor.js +0 -829
  129. package/src/evidence-child-executor.ts +0 -1140
@@ -1,16 +1,11 @@
1
- import { Type } from "typebox";
2
1
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
3
2
  import type { HostContext } from "./host-contracts.ts";
4
3
 
5
4
  import {
6
5
  auditorRunDirectory,
7
- createAuditorDossierTool,
8
6
  persistGateSubmissionCandidate,
9
7
  } from "./auditor-dossier-tool.ts";
10
- import { executeAuditorChild, type AuditorDecisionTool } from "./evidence-child-executor.ts";
11
- import { openToolObject } from "./open-tool-schema.ts";
12
8
  import type { NoReceiptLifecycleFacts } from "./receipt-delivery-policy.ts";
13
- import { loadGatekeeperSessionMaterials } from "./session-opening-materials.ts";
14
9
  import { GatekeeperDecisionError } from "./submission-errors.ts";
15
10
  import { INSPECTOR_OUTPUT_TOOL_NAME } from "./inspector-contracts.ts";
16
11
  import {
@@ -18,6 +13,9 @@ import {
18
13
  gatekeeperDecisionSchema,
19
14
  gatekeeperOutputSchema,
20
15
  } from "./package-contracts/gatekeeper-output.ts";
16
+ import type { PublicSummonResult } from "./public-role-summons.ts";
17
+ import type { TerminalResult } from "./public-cli/terminal.ts";
18
+ import { retainedShapeUnreadable } from "./shape-unreadable-failure.ts";
21
19
  export const INSPECTOR_OUTPUT_TOOL = INSPECTOR_OUTPUT_TOOL_NAME;
22
20
  export const NOTARY_OUTPUT_TOOL = "ak_notary_output";
23
21
 
@@ -47,6 +45,13 @@ export type GatekeeperResult =
47
45
  readonly submission: unknown;
48
46
  }
49
47
  | { readonly status: "no_receipt"; readonly stage: "inspector" | "notary"; readonly reason: string; readonly facts: NoReceiptLifecycleFacts }
48
+ | {
49
+ /** Shape-unreadable officer output — typed fact, not a forged bounce (ADR 0055 / §0). */
50
+ readonly status: "unreadable";
51
+ readonly officer: "inspector" | "notary";
52
+ readonly reason: string;
53
+ readonly submission: unknown;
54
+ }
50
55
  | {
51
56
  readonly status: "transport_failure";
52
57
  readonly stage: "inspector" | "notary";
@@ -57,7 +62,7 @@ export type GatekeeperResult =
57
62
 
58
63
  export type GatekeeperNonPassResult = Extract<
59
64
  GatekeeperResult,
60
- { status: "bounce" | "no_receipt" }
65
+ { status: "bounce" | "no_receipt" | "unreadable" }
61
66
  >;
62
67
 
63
68
  function gateSeatLabel(stage: "inspector" | "notary"): string {
@@ -75,14 +80,24 @@ export class GatekeeperEscalationError extends Error {
75
80
  }
76
81
  }
77
82
 
83
+ export type GateOfficerSummon = (
84
+ officer: "inspector" | "notary",
85
+ sourceRunDirectory: string,
86
+ /** Parent cancellation forwarded to the nested activation (#675). */
87
+ signal?: AbortSignal,
88
+ ) => Promise<PublicSummonResult>;
89
+
78
90
  export type RunGatekeeperOptions = {
79
91
  readonly context: ExtensionContext | HostContext;
80
92
  readonly subject: GatekeeperSubject;
81
93
  readonly signal?: AbortSignal;
82
- readonly loadSoul?: (role: "inspector" | "notary") => Promise<string>;
83
- /** Run directory carrying the institutional resolution page (#518). Derives
84
- * from context when absent. */
94
+ /** Run directory of the parent role (pointer-only summons, ADR 0079). */
85
95
  readonly runDirectory?: string;
96
+ /**
97
+ * Test seam for public-role summons. Production calls the shared public
98
+ * activation path (#675); inject only in offline tracers.
99
+ */
100
+ readonly summonOfficer?: GateOfficerSummon;
86
101
  };
87
102
 
88
103
  export type GatekeeperPassHostActions = {
@@ -91,15 +106,6 @@ export type GatekeeperPassHostActions = {
91
106
  bindSubmissionNonPass(toolCallId: string, result: GatekeeperNonPassResult): void;
92
107
  };
93
108
 
94
- // Unknown fields so wrong types/spellings still reach projection (ADR 0055/0057; 仓第 0 条).
95
- // Opening goes through the sole openToolObject owner — no parallel transport helper.
96
- const officerDecisionSchema = openToolObject(Type.Object({
97
- status: Type.Unknown({ description: "pass | bounce | escalate — 形状指引,非 schema 闸" }),
98
- reason: Type.Optional(Type.Unknown({ description: "status 为 escalate 时的理由" })),
99
- findings: Type.Unknown({ description: "string[] findings,随 pass、bounce 或 escalate 留存" }),
100
- }));
101
-
102
-
103
109
  /**
104
110
  * Direct-seat decision tool spec (#639). Lifecycle assembly stays on the
105
111
  * registration envelope — src/role-runtime.ts (ADR 0018). Schema authority is
@@ -121,30 +127,18 @@ function result(content: string, details: unknown) {
121
127
  return { content: [{ type: "text" as const, text: content }], details };
122
128
  }
123
129
 
124
- /** Shared officer decision tool — open transport; projection owns legality. */
125
- export function createOfficerDecisionTool(name: string): AuditorDecisionTool {
126
- return {
127
- name,
128
- description: "提交一份 typed pass/bounce/escalate 决议。",
129
- parameters: officerDecisionSchema,
130
- async execute(_id, args) { return result(`已收 ${String((args as { status?: unknown })?.status)}`, args); },
131
- };
132
- }
133
-
134
130
  /** Gatekeeper province decision tool — open transport; package-contract projection owns legality. */
135
- export function createGatekeeperOutputTool(): AuditorDecisionTool {
131
+ export function createGatekeeperOutputTool() {
136
132
  return {
137
133
  name: GATEKEEPER_OUTPUT_TOOL_NAME,
138
134
  description: "提交门下省派官决定。",
139
135
  parameters: gatekeeperDecisionSchema,
140
- async execute(_id, args) { return result(`已收 ${String((args as { status?: unknown })?.status)}`, args); },
136
+ async execute(_id: string, args: unknown) {
137
+ return result(`已收 ${String((args as { status?: unknown })?.status)}`, args);
138
+ },
141
139
  };
142
140
  }
143
141
 
144
- async function defaultLoadSoul(role: "inspector" | "notary"): Promise<string> {
145
- return loadGatekeeperSessionMaterials(role);
146
- }
147
-
148
142
  function failureReason(error: unknown): string {
149
143
  if (error instanceof AggregateError) return error.errors.map(failureReason).join("; ");
150
144
  return error instanceof Error ? `${error.name}: ${error.message}` : String(error);
@@ -173,17 +167,19 @@ function retainedSubmission(decision: unknown): unknown {
173
167
  }
174
168
 
175
169
  /**
176
- * No usable explicit release is infrastructure failure, not a judgment status.
177
- * Original submission is retained for the failure channel (#475).
170
+ * Shape-unreadable officer decision — retain original candidate + typed reason.
171
+ * Not a forged bounce, not transport abort (CLAUDE.md §0 / ADR 0055).
172
+ * Real provider/engine/disk failures stay transport_failure elsewhere.
178
173
  */
179
- function noUsableReleaseFailure(
180
- stage: "inspector" | "notary",
174
+ function shapeUnreadable(
175
+ officer: "inspector" | "notary",
181
176
  decision: unknown,
182
- ): Extract<GatekeeperResult, { status: "transport_failure" }> {
177
+ reason = "decision 无显式 pass/bounce/escalate",
178
+ ): Extract<GatekeeperResult, { status: "unreadable" }> {
183
179
  return {
184
- status: "transport_failure",
185
- stage,
186
- reason: "decision 无显式 pass/bounce/escalate",
180
+ status: "unreadable",
181
+ officer,
182
+ reason,
187
183
  submission: retainedSubmission(decision),
188
184
  };
189
185
  }
@@ -198,7 +194,7 @@ function projectOfficerDecision(
198
194
  decision: unknown,
199
195
  ): GatekeeperResult {
200
196
  const record = readRecord(decision);
201
- if (record === undefined) return noUsableReleaseFailure(officer, decision);
197
+ if (record === undefined) return shapeUnreadable(officer, decision);
202
198
  if (record.status === "bounce") {
203
199
  return {
204
200
  status: "bounce",
@@ -224,76 +220,121 @@ function projectOfficerDecision(
224
220
  submission: retainedSubmission(decision),
225
221
  };
226
222
  }
227
- return noUsableReleaseFailure(officer, decision);
223
+ return shapeUnreadable(officer, decision);
228
224
  }
229
225
 
230
- /** Submission-gate summons: subject kind determines the officer without a province child. */
231
- export async function runGatekeeper(options: RunGatekeeperOptions): Promise<GatekeeperResult> {
232
- const loadSoul = options.loadSoul ?? defaultLoadSoul;
226
+ /** Map a public-role terminal onto the gate officer result surface. */
227
+ function projectOfficerTerminal(
228
+ officer: "inspector" | "notary",
229
+ summoned: PublicSummonResult,
230
+ ): GatekeeperResult {
231
+ const terminal: TerminalResult | undefined = summoned.terminal;
232
+ const outcome = terminal?.roleOutcome;
233
+ if (outcome === undefined) {
234
+ const detail = summoned.stderr?.trim();
235
+ return {
236
+ status: "transport_failure",
237
+ stage: officer,
238
+ reason: detail && detail.length > 0
239
+ ? `${gateSeatLabel(officer)} public summon exit ${summoned.exitCode}: ${detail}`
240
+ : `${gateSeatLabel(officer)} public summon produced no terminal (exit ${summoned.exitCode})`,
241
+ submission: summoned,
242
+ };
243
+ }
244
+ if (outcome.kind === "no_receipt") {
245
+ return {
246
+ status: "no_receipt",
247
+ stage: officer,
248
+ reason: `${gateSeatLabel(officer)}未产生已接受回执即散局`,
249
+ facts: outcome,
250
+ };
251
+ }
252
+ if (outcome.kind === "failure") {
253
+ // Single settlement marker only (ADR 0055 / #675) — no cause=output re-derivation.
254
+ const shape = retainedShapeUnreadable(outcome.decisiveFacts);
255
+ if (shape !== undefined) {
256
+ return shapeUnreadable(officer, shape.candidate, outcome.diagnostic);
257
+ }
258
+ return {
259
+ status: "transport_failure",
260
+ stage: officer,
261
+ reason: outcome.diagnostic,
262
+ submission: outcome.decisiveFacts,
263
+ };
264
+ }
265
+ if (outcome.kind === "accepted") {
266
+ return projectOfficerDecision(officer, {
267
+ status: outcome.status,
268
+ ...outcome.decisiveFacts,
269
+ });
270
+ }
271
+ return {
272
+ status: "transport_failure",
273
+ stage: officer,
274
+ reason: `${gateSeatLabel(officer)} public summon returned unusable terminal kind`,
275
+ submission: outcome,
276
+ };
277
+ }
278
+
279
+ /** Projection carrier for the shared submit envelope (ADR 0018). No lifecycle book here. */
280
+ export type GatekeeperProjection = {
281
+ readonly officer: "inspector" | "notary";
282
+ readonly result: GatekeeperResult;
283
+ /** Present only when a public summon actually returned (not transport pre-summon failure). */
284
+ readonly summoned?: PublicSummonResult;
285
+ };
286
+
287
+ /**
288
+ * Summon + project only. Lifecycle book and host abort face live on the shared
289
+ * submit envelope (`gatekeeper-pass-envelope.ts`, ADR 0018 / #675).
290
+ */
291
+ export async function projectGatekeeperRun(
292
+ options: RunGatekeeperOptions,
293
+ ): Promise<GatekeeperProjection> {
233
294
  const officer = options.subject.kind === "worker_completion" ? "inspector" : "notary";
234
- // Resolve once so dossier tool and child session share the same run binding (#632).
235
295
  const runDirectory = options.runDirectory ?? auditorRunDirectory(options.context);
296
+ if (runDirectory === undefined) {
297
+ return {
298
+ officer,
299
+ result: {
300
+ status: "transport_failure",
301
+ stage: officer,
302
+ reason: `${gateSeatLabel(officer)} requires a parent run directory pointer`,
303
+ },
304
+ };
305
+ }
236
306
  // Pointer-only summons need a resolvable leaf: Grok session.jsonl is header-only
237
307
  // (#617 DK-4); write the in-memory tool-call candidate as a run artifact first (#632).
238
- const submissionCandidate =
239
- runDirectory === undefined
240
- ? undefined
241
- : persistGateSubmissionCandidate(runDirectory, options.context);
308
+ persistGateSubmissionCandidate(runDirectory, options.context);
309
+ let summoned: PublicSummonResult;
242
310
  try {
243
- const officerRun = await executeAuditorChild({
244
- context: options.context,
245
- roleLabel: officer === "inspector" ? "Inspector" : "Notary",
246
- gateSeat: officer,
247
- systemPrompt: await loadSoul(officer),
248
- prompt: "卷宗已受理。",
249
- tool: createOfficerDecisionTool(officer === "inspector" ? INSPECTOR_OUTPUT_TOOL : NOTARY_OUTPUT_TOOL),
250
- // Same locator as 审刑院 — run directory + path identifiers only; no receipt body (#632).
251
- dossierTool: createAuditorDossierTool(
252
- runDirectory,
253
- submissionCandidate === undefined ? undefined : { submissionCandidate },
254
- ),
255
- ...(options.signal === undefined ? {} : { signal: options.signal }),
256
- ...(runDirectory === undefined ? {} : { runDirectory }),
257
- });
258
- if (officerRun.noReceiptLifecycle !== undefined) {
259
- return { status: "no_receipt", stage: officer, reason: `${gateSeatLabel(officer)}未产生已接受回执即散局`, facts: officerRun.noReceiptLifecycle };
260
- }
261
- return projectOfficerDecision(officer, officerRun.decision);
311
+ const summon =
312
+ options.summonOfficer
313
+ ?? (async (nextOfficer, sourceRunDirectory, officerSignal) => {
314
+ const { summonGateOfficer } = await import("./public-role-summons.ts");
315
+ return summonGateOfficer({
316
+ officer: nextOfficer,
317
+ sourceRunDirectory,
318
+ cwd: options.context.cwd ?? process.cwd(),
319
+ ...(officerSignal === undefined ? {} : { signal: officerSignal }),
320
+ });
321
+ });
322
+ summoned = await summon(officer, runDirectory, options.signal);
262
323
  } catch (error) {
263
- return { status: "transport_failure", stage: officer, reason: failureReason(error) };
324
+ return {
325
+ officer,
326
+ result: { status: "transport_failure", stage: officer, reason: failureReason(error) },
327
+ };
264
328
  }
329
+ return {
330
+ officer,
331
+ result: projectOfficerTerminal(officer, summoned),
332
+ summoned,
333
+ };
265
334
  }
266
335
 
267
- /** Project GatekeeperResult onto a submit path: transport→failInfrastructure; bounce/no_receipt→typed throw; pass silent. */
268
- export async function requireGatekeeperPass(options: {
269
- readonly context: ExtensionContext | HostContext;
270
- readonly subject: GatekeeperSubject;
271
- readonly signal?: AbortSignal;
272
- readonly hostActions: GatekeeperPassHostActions;
273
- readonly toolCallId: string;
274
- }): Promise<void> {
275
- const gatekeeper = await runGatekeeper({
276
- context: options.context,
277
- subject: options.subject,
278
- ...(options.signal === undefined ? {} : { signal: options.signal }),
279
- });
280
- if (gatekeeper.status === "pass") return;
281
- if (gatekeeper.status === "transport_failure") {
282
- // Typed stage/reason/submission ride failInfrastructure → durable tool_result (#475).
283
- const error = new Error(`交卷闸 transport_failure(${gatekeeper.stage}):${gatekeeper.reason}`) as Error & {
284
- stage: typeof gatekeeper.stage;
285
- reason: string;
286
- submission?: unknown;
287
- };
288
- error.stage = gatekeeper.stage;
289
- error.reason = gatekeeper.reason;
290
- if (gatekeeper.submission !== undefined) error.submission = gatekeeper.submission;
291
- options.hostActions.failInfrastructure(error, options.context, options.toolCallId);
292
- }
293
- if (gatekeeper.status === "escalate") {
294
- throw new GatekeeperEscalationError(gatekeeper);
295
- }
296
- // Envelope owns the execute→tool_result bridge; this module only projects + throws.
297
- options.hostActions.bindSubmissionNonPass(options.toolCallId, gatekeeper);
298
- throw new GatekeeperDecisionError(gatekeeper);
336
+ /** Submission-gate summons: subject kind → officer; activation is public role path (#675). Projection only. */
337
+ export async function runGatekeeper(options: RunGatekeeperOptions): Promise<GatekeeperResult> {
338
+ const { result } = await projectGatekeeperRun(options);
339
+ return result;
299
340
  }
@@ -126,6 +126,8 @@ export type RoleTurnActivation =
126
126
  | { readonly role: "inspector" }
127
127
  | { readonly role: "gatekeeper" }
128
128
  | { readonly role: "navigator" }
129
+ | { readonly role: "auditor" }
130
+ | { readonly role: "evidence-child" }
129
131
  | {
130
132
  readonly role: "diarist";
131
133
  /** Frozen source-catalog path; absent for a true-unbound summons (#708). */
@@ -170,6 +172,12 @@ export type RoleTurnRequest = {
170
172
  readonly runDirectory: string;
171
173
  readonly correlationId?: string;
172
174
  readonly timeoutMs?: number;
175
+ /**
176
+ * Parent cancellation for a nested activation (role-inside-role public summons,
177
+ * #675). The host terminates its child when this aborts; a public CLI process
178
+ * has no parent to observe and leaves it absent.
179
+ */
180
+ readonly signal?: AbortSignal;
173
181
  /** Set by post-admission only on a real host switch; never on same-host resume. */
174
182
  readonly hostTransition?: RoleTurnHostTransition;
175
183
  /**
@@ -295,7 +303,7 @@ type HostGatekeeperSubject = {
295
303
  readonly kind: "worker_completion" | "judge_draft" | "countersign_verdict";
296
304
  };
297
305
  /** Gatekeeper bounce/no_receipt plus other correct submission rejects share one projection map. */
298
- type HostGatekeeperNonPass = { readonly status: "bounce" | "no_receipt" } & Record<string, unknown>;
306
+ type HostGatekeeperNonPass = { readonly status: "bounce" | "no_receipt" | "unreadable" } & Record<string, unknown>;
299
307
  export type HostSubmissionNonPass =
300
308
  | HostGatekeeperNonPass
301
309
  | { readonly code: "coder_skill_expansion_evidence_missing" };
@@ -1,6 +1,9 @@
1
- import { readFile, writeFile } from "node:fs/promises";
2
- import { join } from "node:path";
3
- import type { RoleTurnModelConfig } from "./host-contracts.ts";
1
+ /**
2
+ * Province-officer model authority (#453/#620) and shared seatModelOnly projection.
3
+ * #675 deleted the independent institutional-resolution.json seat page and the
4
+ * child-session open path that consumed it. Seat resolution for every activation
5
+ * is the live public seat table (model/host/engine).
6
+ */
4
7
  import type {
5
8
  GateOfficerSeat,
6
9
  PublicCliConfig,
@@ -8,70 +11,20 @@ import type {
8
11
  } from "./public-cli/config.ts";
9
12
  import { seatModelOnly } from "./public-cli/registry.ts";
10
13
 
11
- export const INSTITUTIONAL_RESOLUTION_FILE = "institutional-resolution.json" as const;
12
-
13
- /** Non-secret per-seat model selection — alias of the single host-neutral RoleTurnModelConfig. */
14
- export type InstitutionalSeatSelection = RoleTurnModelConfig;
15
-
16
14
  /**
17
15
  * Configured province-officer resolution (#453/#620 authority seam):
18
16
  * officer persistent > gatekeeper persistent > unconfigured.
19
- * No startup candidates and no parent-session fallback — parent compose stays
20
- * in resolveInstitutionalSeatSelections; direct call and config display consume
21
- * this typed result as-is.
17
+ * No startup candidates and no parent-session fallback — direct call and config
18
+ * display consume this typed result as-is.
22
19
  */
23
20
  export type ConfiguredProvinceOfficerResolution = {
24
21
  readonly selection?: SeatModelConfig;
25
22
  readonly source: "persistent" | "inherit-gatekeeper" | "unconfigured";
26
23
  };
27
24
 
28
- export type InstitutionalResolutionPage = {
29
- readonly version: 1;
30
- readonly seats: {
31
- readonly gatekeeper?: InstitutionalSeatSelection;
32
- readonly inspector?: InstitutionalSeatSelection;
33
- readonly notary?: InstitutionalSeatSelection;
34
- readonly auditor?: InstitutionalSeatSelection;
35
- readonly evidenceChild?: InstitutionalSeatSelection;
36
- readonly [seat: string]: InstitutionalSeatSelection | undefined;
37
- };
38
- };
39
-
40
- /** Why institutional seat read failed — structured, not free-text. */
41
- export type InstitutionalResolutionFailureReason =
42
- | "missing-page"
43
- | "missing-seat"
44
- | "corrupted"
45
- | "invalid-format";
46
-
47
- export class InstitutionalResolutionError extends Error {
48
- readonly reason: InstitutionalResolutionFailureReason;
49
- constructor(
50
- message: string,
51
- reason: InstitutionalResolutionFailureReason,
52
- options?: ErrorOptions,
53
- ) {
54
- super(message, options);
55
- this.name = "InstitutionalResolutionError";
56
- this.reason = reason;
57
- }
58
- }
59
-
60
- function cleanSelection(model?: { provider?: string; model?: string; thinking?: string }): InstitutionalSeatSelection | undefined {
61
- if (model === undefined) return undefined;
62
- if (typeof model.provider !== "string" || typeof model.model !== "string") return undefined;
63
- return {
64
- provider: model.provider,
65
- model: model.model,
66
- ...(model.thinking === undefined ? {} : { thinking: model.thinking }),
67
- };
68
- }
69
-
70
25
  /**
71
26
  * Authority for configured province-officer model+source (#453/#620):
72
27
  * own persistent > gatekeeper persistent > unconfigured.
73
- * Shared by resolveInstitutionalSeatSelections (plus parent fallback),
74
- * resolveEffectiveSeat subordinate branch, and roles/config display.
75
28
  * Model-axis projection stays seatModelOnly (single implementation in registry).
76
29
  */
77
30
  export function resolveConfiguredProvinceOfficer(
@@ -91,111 +44,3 @@ export function resolveConfiguredProvinceOfficer(
91
44
  }
92
45
  return { source: "unconfigured" };
93
46
  }
94
-
95
- /**
96
- * Resolve effective per-seat institutional selections (#518 §2 Hop 1; #620):
97
- * - gatekeeper / inspector / notary: resolveConfiguredProvinceOfficer then
98
- * parent effective on the province path only
99
- * - auditor / evidenceChild: parent effective
100
- * - navigator: explicit config seat only. Navigator model authority stays
101
- * `navigator-model.json`; the page never carries a parent-inherited navigator
102
- * seat, so host-neutral transport cannot shadow that setting (#590 Out of Scope:
103
- * 席位表/模型路由变更归 owner 域).
104
- */
105
- export function resolveInstitutionalSeatSelections(
106
- config: PublicCliConfig,
107
- parentEffectiveModel?: { provider: string; model: string; thinking?: string },
108
- ): InstitutionalResolutionPage {
109
- const parentSelection = cleanSelection(parentEffectiveModel);
110
-
111
- const gatekeeper =
112
- resolveConfiguredProvinceOfficer(config, "gatekeeper").selection ?? parentSelection;
113
- const inspector =
114
- resolveConfiguredProvinceOfficer(config, "inspector").selection ?? parentSelection;
115
- const notary =
116
- resolveConfiguredProvinceOfficer(config, "notary").selection ?? parentSelection;
117
- const auditor = parentSelection;
118
- const evidenceChild = parentSelection;
119
- const navigator = cleanSelection(seatModelOnly(config.seats?.navigator));
120
-
121
- return {
122
- version: 1,
123
- seats: {
124
- ...(gatekeeper === undefined ? {} : { gatekeeper }),
125
- ...(inspector === undefined ? {} : { inspector }),
126
- ...(notary === undefined ? {} : { notary }),
127
- ...(auditor === undefined ? {} : { auditor }),
128
- ...(evidenceChild === undefined ? {} : { evidenceChild }),
129
- ...(navigator === undefined ? {} : { navigator }),
130
- },
131
- };
132
- }
133
-
134
- /**
135
- * Persist institutional resolution page to run directory (#518 §2 Hop 2).
136
- * Rewritten on each dispatch and resume. Zero secrets inside page.
137
- */
138
- export async function writeInstitutionalResolutionPage(
139
- runDirectory: string,
140
- page: InstitutionalResolutionPage,
141
- ): Promise<void> {
142
- const filePath = join(runDirectory, INSTITUTIONAL_RESOLUTION_FILE);
143
- await writeFile(filePath, `${JSON.stringify(page, null, 2)}\n`, "utf8");
144
- }
145
-
146
- /**
147
- * Read institutional seat selection from run directory (#518 §2 Hop 3).
148
- * Fails loud if page is missing, corrupted, or does not contain a resolution for the seat.
149
- */
150
- export async function readInstitutionalSeatSelection(
151
- runDirectory: string,
152
- seat: string,
153
- ): Promise<InstitutionalSeatSelection> {
154
- const filePath = join(runDirectory, INSTITUTIONAL_RESOLUTION_FILE);
155
- let raw: string;
156
- try {
157
- raw = await readFile(filePath, "utf8");
158
- } catch (error) {
159
- throw new InstitutionalResolutionError(
160
- `institutional resolution page is missing at ${filePath}`,
161
- "missing-page",
162
- { cause: error },
163
- );
164
- }
165
-
166
- let page: unknown;
167
- try {
168
- page = JSON.parse(raw);
169
- } catch (error) {
170
- throw new InstitutionalResolutionError(
171
- `institutional resolution page is corrupted at ${filePath}`,
172
- "corrupted",
173
- { cause: error },
174
- );
175
- }
176
-
177
- if (typeof page !== "object" || page === null || (page as { version?: unknown }).version !== 1) {
178
- throw new InstitutionalResolutionError(
179
- `institutional resolution page format is invalid at ${filePath}`,
180
- "invalid-format",
181
- );
182
- }
183
-
184
- const seats = (page as { seats?: Record<string, unknown> }).seats;
185
- if (typeof seats !== "object" || seats === null) {
186
- throw new InstitutionalResolutionError(
187
- `institutional resolution page missing seats object at ${filePath}`,
188
- "invalid-format",
189
- );
190
- }
191
-
192
- const selection = cleanSelection(seats[seat] as InstitutionalSeatSelection | undefined);
193
- if (selection === undefined) {
194
- throw new InstitutionalResolutionError(
195
- `institutional resolution page has no resolution for seat "${seat}" at ${filePath}`,
196
- "missing-seat",
197
- );
198
- }
199
-
200
- return selection;
201
- }
@@ -1,10 +1,9 @@
1
1
  import {
2
- createComplianceDecisionTool,
3
2
  runComplianceAudit,
3
+ type AuditorSummon,
4
4
  type ComplianceDecision,
5
5
  } from "./compliance-transport.ts";
6
6
  import { auditorRunDirectory } from "./auditor-dossier-tool.ts";
7
- import { loadAuditorSoul } from "./auditor-soul.ts";
8
7
  import {
9
8
  readJudgeAuditSubjects,
10
9
  requireAuditMaterials,
@@ -18,17 +17,13 @@ export const SOUL_AUDIT_TOOL_NAME = JUDGE_AUDIT_TOOL_NAME;
18
17
  export type JudgeAuditOptions = {
19
18
  context: HostContext;
20
19
  signal?: AbortSignal;
20
+ /** Same seam as runComplianceAudit options — offline tracers only. */
21
+ summonAuditor?: AuditorSummon;
21
22
  };
22
23
 
23
- const auditDecisionTool = createComplianceDecisionTool(
24
- JUDGE_AUDIT_TOOL_NAME,
25
- "提交 typed pass/revise/escalate 决议(大理寺审刑)。",
26
- );
27
-
28
24
  /**
29
- * Judge auditor: zero hand-delivered materials. Subjects recovered from the
30
- * parent-session books. Public CLI injects AK_ROLE_RUN_DIR; bare Pi (ADR 0052)
31
- * proceeds without that pointer and self-locates per soul.
25
+ * Judge compliance via public auditor activation (#675 / ADR 0062 / owner r11).
26
+ * 审刑院 is an independent role; subject=judge selects souls/judge-auditor.md.
32
27
  */
33
28
  export function createPiJudgeAuditor(): (options: JudgeAuditOptions) => Promise<ComplianceDecision> {
34
29
  return async (options) => {
@@ -38,13 +33,13 @@ export function createPiJudgeAuditor(): (options: JudgeAuditOptions) => Promise<
38
33
  requireAuditMaterials(subjects);
39
34
 
40
35
  return runComplianceAudit({
41
- tool: auditDecisionTool,
42
- systemPrompt: await loadAuditorSoul("judge"),
43
- roleLabel: "Judge compliance audit",
44
- invalidDecisionLabel: "invalid judge audit decision",
36
+ subject: "judge",
45
37
  context: options.context,
46
- ...(auditorRunDirectory(options.context) === undefined ? {} : { runDirectory: auditorRunDirectory(options.context) }),
38
+ ...(auditorRunDirectory(options.context) === undefined
39
+ ? {}
40
+ : { runDirectory: auditorRunDirectory(options.context) }),
47
41
  ...(options.signal === undefined ? {} : { signal: options.signal }),
42
+ ...(options.summonAuditor === undefined ? {} : { summonAuditor: options.summonAuditor }),
48
43
  });
49
44
  };
50
45
  }
package/src/judge-role.ts CHANGED
@@ -8,6 +8,7 @@ import { withInfrastructureFailureDeclaration } from "./package-contracts/termin
8
8
 
9
9
  import {
10
10
  JUDGE_ACCEPTED_AUDIT_NO_RECEIPT_TEXT,
11
+ JUDGE_ACCEPTED_AUDIT_UNREADABLE_TEXT,
11
12
  JUDGE_ACCEPTED_TEXT,
12
13
  JUDGE_OUTPUT_TOOL_NAME,
13
14
  validateAcceptedJudgeDetails,
@@ -127,6 +128,13 @@ export function createJudgeRoleRuntime(
127
128
  terminate: true as const,
128
129
  ...usageProjection,
129
130
  }),
131
+ // ADR 0055: parent stands with typed unreadable audit fact — not forged pass.
132
+ unreadable: (auditUnreadable, usageProjection) => ({
133
+ content: [{ type: "text" as const, text: JUDGE_ACCEPTED_AUDIT_UNREADABLE_TEXT }],
134
+ details: { ...acceptedDetails, auditUnreadable },
135
+ terminate: true as const,
136
+ ...usageProjection,
137
+ }),
130
138
  revise: (violations) => {
131
139
  throw new Error(
132
140
  `大理寺回执违 soul:${violations.join("; ")}`,