@akagilnc/pi-workflow-roles 0.1.3565 → 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 (142) hide show
  1. package/README.md +4 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/acp-host/production-host.js +18477 -8099
  4. package/dist/analyst-gate-cycles-read.js +387 -0
  5. package/dist/archivist-record-entry.js +22 -1
  6. package/dist/audit-escalation.js +6 -0
  7. package/dist/auditor-soul.js +74 -0
  8. package/dist/collector-config.js +86 -0
  9. package/dist/collector-evidence.js +316 -0
  10. package/dist/collector-github.js +527 -0
  11. package/dist/collector-ledger.js +853 -0
  12. package/dist/collector-tool-schemas.js +33 -0
  13. package/dist/compliance-transport.js +104 -52
  14. package/dist/diarist-contracts.js +72 -0
  15. package/dist/diarist-mechanical.js +411 -0
  16. package/dist/diarist-ticket-resolution.js +177 -0
  17. package/dist/diarist.js +311 -0
  18. package/dist/doctor-auditor.js +25 -0
  19. package/dist/doctor-contracts.js +2 -0
  20. package/dist/doctor-evidence.js +142 -0
  21. package/dist/gatekeeper-role.js +104 -81
  22. package/dist/host-transition-prior-native.js +78 -0
  23. package/dist/institutional-resolution.js +1 -95
  24. package/dist/judge-auditor.js +26 -0
  25. package/dist/ledger-session-read.js +227 -0
  26. package/dist/merger-git-state.js +78 -0
  27. package/dist/navigator-attendance.js +15 -7
  28. package/dist/navigator-public-session.js +170 -0
  29. package/dist/navigator-session-contracts.js +36 -40
  30. package/dist/notary-source-run.js +122 -0
  31. package/dist/package-contracts/auditor-output.js +66 -0
  32. package/dist/package-contracts/evidence-child-output.js +34 -0
  33. package/dist/package-contracts/judge-output.js +2 -0
  34. package/dist/package-contracts/terminating-tools.js +44 -3
  35. package/dist/package-resources/method-skill.js +254 -0
  36. package/dist/packaged-role-registry.js +56 -0
  37. package/dist/pi/durable-principal.js +61 -0
  38. package/dist/pi/in-process-session.js +25 -6
  39. package/dist/pi/known-failure.js +52 -0
  40. package/dist/pi/role-turn-host.js +430 -0
  41. package/dist/public-cli/auto-resume.js +414 -0
  42. package/dist/public-cli/cli-errors.js +8 -0
  43. package/dist/public-cli/cli-io.js +1 -0
  44. package/dist/public-cli/command-renderer.js +5 -0
  45. package/dist/public-cli/doctor-run.js +84 -0
  46. package/dist/public-cli/inspector-run.js +136 -0
  47. package/dist/public-cli/instruction-seat-run.js +256 -0
  48. package/dist/public-cli/invocation.js +2225 -0
  49. package/dist/public-cli/judge-run.js +118 -0
  50. package/dist/public-cli/load-production-acp-host.js +40 -0
  51. package/dist/public-cli/main.js +1511 -1124
  52. package/dist/public-cli/notary-run.js +161 -0
  53. package/dist/public-cli/option-definitions.js +1192 -0
  54. package/dist/public-cli/post-admission.js +667 -0
  55. package/dist/public-cli/public-run-credentials.js +49 -0
  56. package/dist/public-cli/registry.js +8 -2
  57. package/dist/public-cli/reviewer-dispatch-rejection.js +77 -0
  58. package/dist/public-cli/run-lifecycle.js +1344 -0
  59. package/dist/public-cli/seat-ticket-binding.js +113 -0
  60. package/dist/public-cli/settlement.js +3441 -0
  61. package/dist/public-cli/terminal.js +135 -0
  62. package/dist/public-cli/turn-request.js +25 -0
  63. package/dist/public-role-summons.js +301 -0
  64. package/dist/receipt-delivery-policy.js +10 -0
  65. package/dist/reviewer-child-executor.js +80 -8
  66. package/dist/reviewer-execution-ledger.js +2 -1
  67. package/dist/run-terminal-artifacts.js +195 -0
  68. package/dist/run-ticket-number.js +40 -0
  69. package/dist/session-assistant-usage.js +107 -0
  70. package/dist/shape-unreadable-failure.js +34 -0
  71. package/dist/submission-errors.js +3 -0
  72. package/dist/submission-ledger.js +388 -0
  73. package/dist/ticket-provenance-contracts.js +115 -0
  74. package/dist/ticket-provenance.js +340 -0
  75. package/extensions/role-runtime.ts +8 -0
  76. package/package.json +1 -1
  77. package/resources/diarist-collect.md +6 -10
  78. package/scripts/build-package.mjs +2 -1
  79. package/souls/diarist.md +9 -0
  80. package/souls/doctor-auditor.md +1 -0
  81. package/souls/evidence-child.md +1 -0
  82. package/src/acp-host/production-host.ts +4 -0
  83. package/src/acp-host/role-envelope.ts +4 -3
  84. package/src/acp-host/role-turn-host.ts +9 -1
  85. package/src/analyst-gate-cycles-read.ts +37 -2
  86. package/src/archivist-record-entry.ts +45 -1
  87. package/src/audit-escalation.ts +17 -0
  88. package/src/auditor-role.ts +22 -0
  89. package/src/auditor-soul.ts +42 -0
  90. package/src/compliance-transport.ts +177 -66
  91. package/src/diarist-contracts.ts +88 -0
  92. package/src/diarist-role.ts +60 -0
  93. package/src/diarist.ts +253 -179
  94. package/src/doctor-auditor.ts +10 -14
  95. package/src/doctor-contracts.ts +1 -0
  96. package/src/doctor-role.ts +2 -2
  97. package/src/evidence-child-role.ts +22 -0
  98. package/src/gatekeeper-pass-envelope.ts +109 -0
  99. package/src/gatekeeper-role.ts +145 -104
  100. package/src/host-contracts.ts +15 -2
  101. package/src/institutional-resolution.ts +8 -163
  102. package/src/judge-auditor.ts +10 -15
  103. package/src/judge-role.ts +8 -0
  104. package/src/navigator-attendance.ts +25 -9
  105. package/src/navigator-public-session.ts +217 -0
  106. package/src/navigator-session-contracts.ts +61 -54
  107. package/src/notary-source-run.ts +3 -1
  108. package/src/package-contracts/auditor-output.ts +82 -0
  109. package/src/package-contracts/evidence-child-output.ts +51 -0
  110. package/src/package-contracts/judge-output.ts +1 -0
  111. package/src/package-contracts/reviewer-output.ts +2 -2
  112. package/src/package-contracts/terminating-tools.ts +53 -3
  113. package/src/packaged-role-registry.ts +63 -0
  114. package/src/pi/adapter.ts +2 -1
  115. package/src/pi/in-process-session.ts +38 -12
  116. package/src/pi/role-turn-host.ts +42 -0
  117. package/src/public-cli/cli.ts +43 -6
  118. package/src/public-cli/countersign-run.ts +9 -129
  119. package/src/public-cli/diarist-run.ts +312 -0
  120. package/src/public-cli/instruction-seat-run.ts +289 -50
  121. package/src/public-cli/invocation.ts +87 -25
  122. package/src/public-cli/option-definitions.ts +79 -0
  123. package/src/public-cli/post-admission.ts +9 -3
  124. package/src/public-cli/registry.ts +7 -1
  125. package/src/public-cli/run-lifecycle.ts +53 -6
  126. package/src/public-cli/settlement.ts +170 -7
  127. package/src/public-cli/terminal.ts +4 -1
  128. package/src/public-role-summons.ts +431 -0
  129. package/src/receipt-delivery-policy.ts +10 -0
  130. package/src/reviewer-agent.ts +1 -1
  131. package/src/reviewer-child-executor.ts +114 -12
  132. package/src/reviewer-execution-ledger.ts +3 -2
  133. package/src/role-runtime.ts +275 -23
  134. package/src/session-assistant-usage.ts +119 -0
  135. package/src/session-opening-materials.ts +1 -1
  136. package/src/shape-unreadable-failure.ts +49 -0
  137. package/src/submission-errors.ts +3 -0
  138. package/src/ticket-provenance-contracts.ts +1 -0
  139. package/src/ticket-provenance.ts +0 -23
  140. package/dist/evidence-child-executor.js +0 -829
  141. package/src/diarist-llm-collector.ts +0 -324
  142. package/src/evidence-child-executor.ts +0 -1140
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Shared submit-path envelope for 门下省 gates (ADR 0018 / #675).
3
+ * Owns officer-pointer book + host abort/non-pass faces.
4
+ * Role modules only project via projectGatekeeperRun / runGatekeeper — no book, no catch.
5
+ */
6
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
7
+ import { bookDirectOfficerRunPointer } from "./archivist-record-entry.ts";
8
+ import type { HostContext } from "./host-contracts.ts";
9
+ import {
10
+ GatekeeperDecisionError,
11
+ GatekeeperEscalationError,
12
+ projectGatekeeperRun,
13
+ type GatekeeperPassHostActions,
14
+ type GatekeeperResult,
15
+ type GatekeeperSubject,
16
+ type GateOfficerSummon,
17
+ } from "./gatekeeper-role.ts";
18
+ import type { PublicSummonResult } from "./public-role-summons.ts";
19
+ import { sessionFileFromPublicSummon } from "./session-assistant-usage.ts";
20
+
21
+ /**
22
+ * Book a typed pointer under parent session/auditor-roles (archivist-owned write).
23
+ * Offline mocks without a real session leave no nested volume (lawful zero).
24
+ */
25
+ function bookDirectOfficerPointer(
26
+ context: ExtensionContext | HostContext,
27
+ officer: "inspector" | "notary",
28
+ result: GatekeeperResult,
29
+ summoned: PublicSummonResult,
30
+ ): void {
31
+ if (
32
+ result.status !== "pass"
33
+ && result.status !== "bounce"
34
+ && result.status !== "escalate"
35
+ && result.status !== "unreadable"
36
+ ) {
37
+ return;
38
+ }
39
+ const parentFile = context.sessionManager?.getSessionFile?.();
40
+ if (typeof parentFile !== "string" || parentFile.trim() === "") return;
41
+ const sessionFile = sessionFileFromPublicSummon(summoned);
42
+ if (sessionFile === undefined) {
43
+ // No independent 正本 to point at — do not synthesize a parallel session.
44
+ return;
45
+ }
46
+ bookDirectOfficerRunPointer({
47
+ parentSessionFile: parentFile,
48
+ officer,
49
+ sessionFile,
50
+ ...(typeof summoned.runDirectory === "string" && summoned.runDirectory.trim() !== ""
51
+ ? { runDirectory: summoned.runDirectory }
52
+ : {}),
53
+ });
54
+ }
55
+
56
+ /**
57
+ * Shared envelope: project gate, book officer pointer, map onto host actions.
58
+ * unreadable = parent stands (ADR 0055); never mechanical NonPass reject.
59
+ */
60
+ export async function requireGatekeeperPass(options: {
61
+ readonly context: ExtensionContext | HostContext;
62
+ readonly subject: GatekeeperSubject;
63
+ readonly signal?: AbortSignal;
64
+ readonly hostActions: GatekeeperPassHostActions;
65
+ readonly toolCallId: string;
66
+ /** Lowest seam: same as runGatekeeper options.summonOfficer — offline tracers only. */
67
+ readonly summonOfficer?: GateOfficerSummon;
68
+ }): Promise<void> {
69
+ const projected = await projectGatekeeperRun({
70
+ context: options.context,
71
+ subject: options.subject,
72
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
73
+ ...(options.summonOfficer === undefined ? {} : { summonOfficer: options.summonOfficer }),
74
+ });
75
+ const gatekeeper = projected.result;
76
+ // Envelope-owned pointer book. Failure is host infrastructure — single face.
77
+ if (projected.summoned !== undefined) {
78
+ try {
79
+ bookDirectOfficerPointer(
80
+ options.context,
81
+ projected.officer,
82
+ gatekeeper,
83
+ projected.summoned,
84
+ );
85
+ } catch (error) {
86
+ options.hostActions.failInfrastructure(error, options.context, options.toolCallId);
87
+ }
88
+ }
89
+ if (gatekeeper.status === "pass") return;
90
+ // ADR 0055 / #675: shape-unreadable officer output must not mechanically reject parent.
91
+ if (gatekeeper.status === "unreadable") return;
92
+ if (gatekeeper.status === "transport_failure") {
93
+ const error = new Error(`交卷闸 transport_failure(${gatekeeper.stage}):${gatekeeper.reason}`) as Error & {
94
+ stage: typeof gatekeeper.stage;
95
+ reason: string;
96
+ submission?: unknown;
97
+ };
98
+ error.stage = gatekeeper.stage;
99
+ error.reason = gatekeeper.reason;
100
+ if (gatekeeper.submission !== undefined) error.submission = gatekeeper.submission;
101
+ options.hostActions.failInfrastructure(error, options.context, options.toolCallId);
102
+ }
103
+ if (gatekeeper.status === "escalate") {
104
+ throw new GatekeeperEscalationError(gatekeeper);
105
+ }
106
+ // bounce / no_receipt: typed non-pass — envelope owns execute→tool_result bridge.
107
+ options.hostActions.bindSubmissionNonPass(options.toolCallId, gatekeeper);
108
+ throw new GatekeeperDecisionError(gatekeeper);
109
+ }
@@ -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
  }
@@ -125,7 +125,14 @@ export type RoleTurnActivation =
125
125
  }
126
126
  | { readonly role: "inspector" }
127
127
  | { readonly role: "gatekeeper" }
128
- | { readonly role: "navigator" };
128
+ | { readonly role: "navigator" }
129
+ | { readonly role: "auditor" }
130
+ | { readonly role: "evidence-child" }
131
+ | {
132
+ readonly role: "diarist";
133
+ /** Frozen source-catalog path; absent for a true-unbound summons (#708). */
134
+ readonly sourcesPath?: string;
135
+ };
129
136
 
130
137
  export type RoleTurnContinuation =
131
138
  | { readonly kind: "initial"; readonly prompt: string }
@@ -165,6 +172,12 @@ export type RoleTurnRequest = {
165
172
  readonly runDirectory: string;
166
173
  readonly correlationId?: string;
167
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;
168
181
  /** Set by post-admission only on a real host switch; never on same-host resume. */
169
182
  readonly hostTransition?: RoleTurnHostTransition;
170
183
  /**
@@ -290,7 +303,7 @@ type HostGatekeeperSubject = {
290
303
  readonly kind: "worker_completion" | "judge_draft" | "countersign_verdict";
291
304
  };
292
305
  /** Gatekeeper bounce/no_receipt plus other correct submission rejects share one projection map. */
293
- type HostGatekeeperNonPass = { readonly status: "bounce" | "no_receipt" } & Record<string, unknown>;
306
+ type HostGatekeeperNonPass = { readonly status: "bounce" | "no_receipt" | "unreadable" } & Record<string, unknown>;
294
307
  export type HostSubmissionNonPass =
295
308
  | HostGatekeeperNonPass
296
309
  | { readonly code: "coder_skill_expansion_evidence_missing" };