@akagilnc/pi-workflow-roles 0.1.3558 → 0.1.3572

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 (49) hide show
  1. package/README.md +4 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/acp-host/description.js +21 -0
  4. package/dist/{grok → acp-host}/mcp-relay.mjs +5 -5
  5. package/dist/{grok → acp-host}/production-host.js +860 -162
  6. package/dist/diarist-contracts.js +72 -0
  7. package/dist/host-descriptions.js +34 -0
  8. package/dist/package-contracts/terminating-tools.js +20 -2
  9. package/dist/packaged-role-registry.js +20 -0
  10. package/dist/public-cli/config.js +23 -10
  11. package/dist/public-cli/main.js +622 -671
  12. package/dist/public-cli/registry.js +4 -1
  13. package/extensions/role-runtime.ts +1 -0
  14. package/package.json +1 -1
  15. package/resources/diarist-collect.md +6 -10
  16. package/scripts/build-package.mjs +8 -8
  17. package/souls/diarist.md +9 -0
  18. package/src/acp-host/description.ts +43 -0
  19. package/src/{grok → acp-host}/mcp-relay.mjs +5 -5
  20. package/src/{grok → acp-host}/production-host.ts +31 -37
  21. package/src/{grok → acp-host}/role-envelope.ts +25 -24
  22. package/src/{grok → acp-host}/role-turn-host.ts +46 -81
  23. package/src/{grok → acp-host}/session-identity.ts +7 -7
  24. package/src/diarist-contracts.ts +88 -0
  25. package/src/diarist-role.ts +60 -0
  26. package/src/diarist.ts +253 -179
  27. package/src/host-contracts.ts +11 -4
  28. package/src/host-descriptions.ts +51 -0
  29. package/src/host-transition-prior-native.ts +12 -24
  30. package/src/package-contracts/terminating-tools.ts +22 -2
  31. package/src/packaged-role-registry.ts +20 -0
  32. package/src/pi/role-turn-host.ts +10 -1
  33. package/src/public-cli/cli.ts +39 -9
  34. package/src/public-cli/config.ts +24 -11
  35. package/src/public-cli/countersign-run.ts +9 -129
  36. package/src/public-cli/diarist-run.ts +312 -0
  37. package/src/public-cli/invocation.ts +23 -2
  38. package/src/public-cli/load-production-acp-host.ts +52 -0
  39. package/src/public-cli/option-definitions.ts +18 -0
  40. package/src/public-cli/post-admission.ts +3 -3
  41. package/src/public-cli/registry.ts +3 -0
  42. package/src/public-cli/run-lifecycle.ts +31 -3
  43. package/src/public-cli/settlement.ts +50 -2
  44. package/src/public-cli/terminal.ts +2 -1
  45. package/src/role-runtime.ts +117 -6
  46. package/src/ticket-provenance-contracts.ts +1 -0
  47. package/src/ticket-provenance.ts +0 -23
  48. package/src/diarist-llm-collector.ts +0 -324
  49. package/src/public-cli/load-production-grok-host.ts +0 -35
@@ -1,19 +1,13 @@
1
1
  /**
2
2
  * Single authority for #617 DK-4 cross-host prior-native projection.
3
- * Closed host discriminators only; unknown previous/live hosts never inject.
3
+ * Classifies the prior volume into the two record families that exist
4
+ * (Pi native session file / sitian run records), never by host name.
4
5
  */
5
6
  import { access, readdir } from "node:fs/promises";
6
7
  import { dirname, join } from "node:path";
7
8
 
8
9
  import type { RoleTurnHostTransition } from "./host-contracts.ts";
9
10
 
10
- const KNOWN_ROLE_TURN_HOSTS = ["pi", "grok-build"] as const;
11
- type KnownRoleTurnHost = (typeof KNOWN_ROLE_TURN_HOSTS)[number];
12
-
13
- function isKnownRoleTurnHost(value: string): value is KnownRoleTurnHost {
14
- return (KNOWN_ROLE_TURN_HOSTS as readonly string[]).includes(value);
15
- }
16
-
17
11
  function isEnoent(error: unknown): boolean {
18
12
  return typeof error === "object" && error !== null && (error as NodeJS.ErrnoException).code === "ENOENT";
19
13
  }
@@ -59,13 +53,12 @@ async function listSitianRecordPaths(sessionParent: string): Promise<string[]> {
59
53
  }
60
54
 
61
55
  /**
62
- * Project one hostTransition only for a real switch between known hosts.
63
- * Unknown host names → undefined (no inject). Empty native volume still
64
- * yields a typed switch (empty path list).
56
+ * Project one hostTransition only for a real host switch. Empty native volume
57
+ * still yields a typed switch (empty path list).
65
58
  *
66
- * Pi previous → Pi session.jsonl path.
67
- * Grok previous → sitian records on the live run (#717). Grok CLI journals
68
- * stay in the operator grok home; they are not copied here.
59
+ * Pi wrote its own session.jsonl; every other host's run volume is the sitian
60
+ * record set on the live run (ADR 0077 `record-scope-phase-two`, #717) — the CLI's
61
+ * own journals stay in the operator home and are never copied here.
69
62
  */
70
63
  export async function projectHostTransitionPriorNative(input: {
71
64
  readonly previousHost: string;
@@ -73,20 +66,15 @@ export async function projectHostTransitionPriorNative(input: {
73
66
  readonly piSessionFile: string;
74
67
  }): Promise<RoleTurnHostTransition | undefined> {
75
68
  if (input.previousHost === input.liveHost) return undefined;
76
- if (!isKnownRoleTurnHost(input.previousHost) || !isKnownRoleTurnHost(input.liveHost)) {
77
- return undefined;
78
- }
79
69
  if (input.previousHost === "pi") {
80
- const paths = await listPiNativeRecordPaths(input.piSessionFile);
81
70
  return {
82
- previousHost: "pi",
83
- priorNativePaths: paths,
71
+ priorNativeKind: "pi-native",
72
+ priorNativePaths: await listPiNativeRecordPaths(input.piSessionFile),
84
73
  };
85
74
  }
86
- // previousHost === "grok-build": sitian path handoff only — do not read bytes.
87
- const paths = await listSitianRecordPaths(input.piSessionFile);
75
+ // Sitian path handoff only — do not read bytes.
88
76
  return {
89
- previousHost: "grok-build",
90
- priorNativePaths: paths,
77
+ priorNativeKind: "sitian",
78
+ priorNativePaths: await listSitianRecordPaths(input.piSessionFile),
91
79
  };
92
80
  }
@@ -34,6 +34,7 @@ import { NOTARY_ACCEPTED_TEXT, NOTARY_OUTPUT_TOOL_NAME, validateRecordedNotaryOu
34
34
  import { COUNTERSIGN_ACCEPTED_TEXT, COUNTERSIGN_OUTPUT_TOOL_NAME, validateRecordedCountersignOutput, type CountersignVerdict } from "../countersign-contracts.ts";
35
35
  import { GLEANER_LEFT_ACCEPTED_TEXT, GLEANER_LEFT_OUTPUT_TOOL_NAME, validateRecordedGleanerLeftOutput, type GleanerLeftOutput } from "../gleaner-left-contracts.ts";
36
36
  import { INSPECTOR_ACCEPTED_TEXT, INSPECTOR_OUTPUT_TOOL_NAME, validateRecordedInspectorOutput, type InspectorOutput } from "../inspector-contracts.ts";
37
+ import { DIARIST_ACCEPTED_TEXT, DIARIST_OUTPUT_TOOL_NAME, validateRecordedDiaristOutput, type DiaristOutput } from "../diarist-contracts.ts";
37
38
  import {
38
39
  CODER_ACCEPTED_TEXT,
39
40
  CODER_OUTPUT_TOOL_NAME,
@@ -71,6 +72,7 @@ export {
71
72
  validateRecordedInspectorOutput,
72
73
  validateRecordedGatekeeperOutput,
73
74
  validateRecordedNavigatorOutput,
75
+ validateRecordedDiaristOutput,
74
76
  };
75
77
  export type {
76
78
  CollectorReceipt,
@@ -87,6 +89,7 @@ export type {
87
89
  InspectorOutput,
88
90
  GatekeeperDirectOutput,
89
91
  NavigatorAdvice,
92
+ DiaristOutput,
90
93
  };
91
94
 
92
95
  export const TERMINATING_TOOL_NAMES = [
@@ -103,6 +106,7 @@ export const TERMINATING_TOOL_NAMES = [
103
106
  INSPECTOR_OUTPUT_TOOL_NAME,
104
107
  GATEKEEPER_OUTPUT_TOOL_NAME,
105
108
  NAVIGATOR_OUTPUT_TOOL_NAME,
109
+ DIARIST_OUTPUT_TOOL_NAME,
106
110
  ] as const;
107
111
 
108
112
  export type TerminatingToolName = (typeof TERMINATING_TOOL_NAMES)[number];
@@ -119,7 +123,8 @@ export type AcceptedDetails =
119
123
  | GleanerLeftOutput
120
124
  | InspectorOutput
121
125
  | GatekeeperDirectOutput
122
- | NavigatorAdvice;
126
+ | NavigatorAdvice
127
+ | DiaristOutput;
123
128
 
124
129
  export function isTerminatingToolName(
125
130
  name: string,
@@ -155,6 +160,8 @@ export function acceptedTextFor(toolName: TerminatingToolName): string {
155
160
  return GATEKEEPER_ACCEPTED_TEXT;
156
161
  case NAVIGATOR_OUTPUT_TOOL_NAME:
157
162
  return NAVIGATOR_ACCEPTED_TEXT;
163
+ case DIARIST_OUTPUT_TOOL_NAME:
164
+ return DIARIST_ACCEPTED_TEXT;
158
165
  }
159
166
  }
160
167
 
@@ -213,6 +220,7 @@ export function validateAcceptedDetails(
213
220
  [INSPECTOR_OUTPUT_TOOL_NAME]: ["pass", "bounce"],
214
221
  [GATEKEEPER_OUTPUT_TOOL_NAME]: ["dispatch", "pass"],
215
222
  [NAVIGATOR_OUTPUT_TOOL_NAME]: ["advice"],
223
+ [DIARIST_OUTPUT_TOOL_NAME]: ["completed"],
216
224
  };
217
225
  const collectorDiscriminator = toolName === COLLECTOR_OUTPUT_TOOL && Array.isArray(candidate?.groups);
218
226
  const baseDiscriminator = discriminator;
@@ -253,6 +261,8 @@ export function validateAcceptedDetails(
253
261
  return validateRecordedGatekeeperOutput(details);
254
262
  case NAVIGATOR_OUTPUT_TOOL_NAME:
255
263
  return validateRecordedNavigatorOutput(details);
264
+ case DIARIST_OUTPUT_TOOL_NAME:
265
+ return validateRecordedDiaristOutput(details);
256
266
  }
257
267
  } catch (error) {
258
268
  if (error instanceof Error && error.constructor === Error) throw new AcceptedDetailsContractError(error.message, { cause: error });
@@ -280,6 +290,15 @@ export function validateAcceptedLifecycle(
280
290
  if (!deepEqual(testimony, projected)) throw new Error("accepted tool lifecycle details mismatch");
281
291
  return details;
282
292
  }
293
+ if (toolName === DIARIST_OUTPUT_TOOL_NAME) {
294
+ // Envelope-owned mechanical sitian facts are runtime-bound on details only
295
+ // (same shape as the Doctor runtime cost); the submitted arguments carry the
296
+ // role's own selections. Machine facts never come from model self-report.
297
+ const { sitian: _mechanical, ...submitted } = details as DiaristOutput & { sitian?: unknown };
298
+ const testimony = validateAcceptedDetails(toolName, argumentsValue);
299
+ if (!deepEqual(testimony, submitted)) throw new Error("accepted tool lifecycle details mismatch");
300
+ return details;
301
+ }
283
302
  const argumentsDetails = validateAcceptedDetails(toolName, argumentsValue);
284
303
  if (!deepEqual(argumentsDetails, details)) throw new Error("accepted tool lifecycle details mismatch");
285
304
  return details;
@@ -301,7 +320,8 @@ export function acceptedFacts(toolName: TerminatingToolName, details: AcceptedDe
301
320
  case GLEANER_LEFT_OUTPUT_TOOL_NAME:
302
321
  case INSPECTOR_OUTPUT_TOOL_NAME:
303
322
  case GATEKEEPER_OUTPUT_TOOL_NAME:
304
- case NAVIGATOR_OUTPUT_TOOL_NAME: return { status: (details as { status: string }).status };
323
+ case NAVIGATOR_OUTPUT_TOOL_NAME:
324
+ case DIARIST_OUTPUT_TOOL_NAME: return { status: (details as { status: string }).status };
305
325
  case JUDGE_OUTPUT_TOOL_NAME: return { status: (details as { judgeStatus: string }).judgeStatus };
306
326
  case COUNTERSIGN_OUTPUT_TOOL_NAME: return { status: (details as { countersignStatus: string }).countersignStatus };
307
327
  case MERGER_OUTPUT_TOOL_NAME: {
@@ -11,6 +11,10 @@ import { NOTARY_OUTPUT_TOOL_NAME } from "./notary-contracts.ts";
11
11
  import { COUNTERSIGN_OUTPUT_TOOL_NAME } from "./countersign-contracts.ts";
12
12
  import { GLEANER_LEFT_OUTPUT_TOOL_NAME } from "./gleaner-left-contracts.ts";
13
13
  import { INSPECTOR_OUTPUT_TOOL_NAME } from "./inspector-contracts.ts";
14
+ import {
15
+ DIARIST_OUTPUT_TOOL_NAME,
16
+ DIARIST_SOURCES_FLAG,
17
+ } from "./diarist-contracts.ts";
14
18
 
15
19
  /** Shared by public notary and gatekeeper-province notary. */
16
20
  export const NOTARY_SESSION_MATERIALS = [
@@ -185,6 +189,22 @@ export const PUBLIC_ROLE_RECORDS = [
185
189
  activationStage: "load-and-install",
186
190
  sessionMaterials: ["CLAUDE.md", "souls/navigator.md"],
187
191
  },
192
+ // #708 / ADR 0075 `diarist-is-role`: 起居郎 is a seat like any other. The
193
+ // frozen source catalog rides the shared input-flag seam; it is absent for a
194
+ // true-unbound summons (no ticket → no diary).
195
+ {
196
+ role: "diarist",
197
+ phases: [null],
198
+ outputTool: DIARIST_OUTPUT_TOOL_NAME,
199
+ inputFlag: DIARIST_SOURCES_FLAG.name,
200
+ phaseFlag: undefined,
201
+ activationStage: "load-and-install",
202
+ sessionMaterials: [
203
+ "CLAUDE.md",
204
+ "souls/diarist.md",
205
+ "resources/diarist-collect.md",
206
+ ],
207
+ },
188
208
  ] as const;
189
209
 
190
210
  export type PublicRoleRecord = (typeof PUBLIC_ROLE_RECORDS)[number];
@@ -146,6 +146,14 @@ function buildActivationFlagArgs(activation: RoleTurnActivation): string[] {
146
146
  return ["--ak-role", "gatekeeper"];
147
147
  case "navigator":
148
148
  return ["--ak-role", "navigator"];
149
+ case "diarist":
150
+ return [
151
+ "--ak-role",
152
+ "diarist",
153
+ ...(activation.sourcesPath === undefined
154
+ ? []
155
+ : ["--ak-diarist-sources", activation.sourcesPath]),
156
+ ];
149
157
  default: {
150
158
  const _exhaustive: never = activation;
151
159
  return _exhaustive;
@@ -406,9 +414,10 @@ export function createPiRoleTurnHost(config: PiRoleTurnHostConfig): RoleTurnHost
406
414
  return {
407
415
  async executeTurn(request: RoleTurnRequest): Promise<RoleTurnResult> {
408
416
  // #617 DK-7: Pi argv gets projected native paths once; never record bytes.
417
+ // Pi already owns its own session file, so only sitian prior volume rides in.
409
418
  let turnRequest = request;
410
419
  const paths =
411
- request.hostTransition?.previousHost === "grok-build"
420
+ request.hostTransition?.priorNativeKind === "sitian"
412
421
  ? request.hostTransition.priorNativePaths
413
422
  : undefined;
414
423
  if (
@@ -38,8 +38,9 @@ import { seatModelOnly } from "./registry.ts";
38
38
  import { CliUsageError } from "./cli-errors.ts";
39
39
  import type { CliIo } from "./cli-io.ts";
40
40
  import type { PostAdmissionEnv } from "./post-admission.ts";
41
- import type { RoleTurnHost } from "../host-contracts.ts";
42
- import { loadProductionGrokHostFactory } from "./load-production-grok-host.ts";
41
+ import type { RoleTurnHost, RoleTurnRequest } from "../host-contracts.ts";
42
+ import { packagedExternalHostNames } from "../host-descriptions.ts";
43
+ import { loadProductionAcpHostFactory } from "./load-production-acp-host.ts";
43
44
  import {
44
45
  createPiRoleTurnHost,
45
46
  appendPiSessionCustomEntry,
@@ -50,6 +51,7 @@ import {
50
51
  parseCoderArgv,
51
52
  parseCollectorArgv,
52
53
  parseCountersignArgv,
54
+ parseDiaristArgv,
53
55
  parseGleanerLeftArgv,
54
56
  parseDoctorArgv,
55
57
  parseFixerArgv,
@@ -77,6 +79,7 @@ import { runPublicCoder, runPublicCoderResume } from "./coder-run.ts";
77
79
  import { runPublicInstructionSeat, runPublicInstructionSeatResume } from "./instruction-seat-run.ts";
78
80
  import { runPublicCollector, runPublicCollectorResume } from "./collector-run.ts";
79
81
  import { runPublicCountersign, runPublicCountersignResume } from "./countersign-run.ts";
82
+ import { runPublicDiarist, runPublicDiaristResume } from "./diarist-run.ts";
80
83
  import { runPublicGleanerLeft, runPublicGleanerLeftResume } from "./gleaner-left-run.ts";
81
84
  import { runPublicDoctor, runPublicDoctorResume } from "./doctor-run.ts";
82
85
  import { runPublicFixer, runPublicFixerResume } from "./fixer-run.ts";
@@ -125,6 +128,7 @@ const RESUME_SEAT_DISPATCH: Record<
125
128
  inspector: { seat: "inspector", run: runPublicInspectorResume },
126
129
  gatekeeper: { seat: "gatekeeper", run: runPublicInstructionSeatResume },
127
130
  navigator: { seat: "navigator", run: runPublicInstructionSeatResume },
131
+ diarist: { seat: "diarist", run: runPublicDiaristResume },
128
132
  };
129
133
  import {
130
134
  INTERNAL_ROLE_ENTRYPOINT_RELATIVE,
@@ -167,6 +171,7 @@ export const PUBLIC_ROLE_ARGV = {
167
171
  reviewer: { parse: parseReviewerArgv, options: optionsForOwner("reviewer") },
168
172
  gatekeeper: { parse: parseGatekeeperArgv, options: optionsForOwner("gatekeeper") },
169
173
  navigator: { parse: parseNavigatorArgv, options: optionsForOwner("navigator") },
174
+ diarist: { parse: parseDiaristArgv, options: optionsForOwner("diarist") },
170
175
  /** Deterministic analysis seat (#336) — argv parse only; no LLM admission. */
171
176
  analyst: { parse: parseAnalystArgv, options: optionsForOwner("analyst") },
172
177
  } as const;
@@ -321,19 +326,19 @@ function resolveRoleTurnHost(
321
326
  recordLaunchedRolePackageIdentity,
322
327
  observeLaunchedRolePackageIdentity,
323
328
  });
324
- // Composition-root unique adapter table (#522 / #580): pi + S6 grok-build true adapter.
325
- const adapters = env.hostAdapters ?? [
329
+ // Composition-root adapter table: pi (in-process default) + one ACP adapter per description-table key.
330
+ const adapters: readonly NamedRoleTurnHostAdapter[] = env.hostAdapters ?? [
326
331
  { name: "pi", create: () => ({ ok: true as const, host: piHost }) },
327
- {
328
- name: "grok-build",
332
+ ...packagedExternalHostNames().map((name) => ({
333
+ name,
329
334
  create: () => {
330
335
  // Factory loads outside the public bin static graph (ADR 0052 peer-free discovery).
331
336
  let hostPromise: Promise<RoleTurnHost> | undefined;
332
337
  return {
333
338
  ok: true as const,
334
339
  host: {
335
- executeTurn: async (request) => {
336
- hostPromise ??= loadProductionGrokHostFactory(env.packageRoot).then((create) =>
340
+ executeTurn: async (request: RoleTurnRequest) => {
341
+ hostPromise ??= loadProductionAcpHostFactory(env.packageRoot, name).then((create) =>
337
342
  create({
338
343
  packageRoot: env.packageRoot,
339
344
  principalAuthority: options.principalAuthority,
@@ -344,7 +349,7 @@ function resolveRoleTurnHost(
344
349
  },
345
350
  };
346
351
  },
347
- },
352
+ })),
348
353
  ];
349
354
  const hostName = options.seat.host;
350
355
  const adapter = adapters.find((candidate) => candidate.name === hostName);
@@ -1310,6 +1315,31 @@ export async function runAkRole(
1310
1315
  };
1311
1316
  }
1312
1317
 
1318
+ // Diarist public run path (#708): 起居郎 is summoned like any other seat.
1319
+ if (parsed.command === "diarist") {
1320
+ const agentDir = resolveAgentDir(env, home);
1321
+ const cwd = env.cwd ?? process.cwd();
1322
+ const config = await loadAndValidateConfig(home, env.packageRoot);
1323
+ const credentials =
1324
+ env.credentials ?? (await loadCredentialProviders(agentDir));
1325
+ const seat = resolveEffectiveSeat(
1326
+ config,
1327
+ "diarist",
1328
+ credentials,
1329
+ invocationFromParsed(parsed),
1330
+ );
1331
+ const result = await runPublicDiarist(
1332
+ parsed.args,
1333
+ createRoleEnvironment(env, { role: "diarist", home, agentDir, cwd, credentials, seat, config }),
1334
+ io,
1335
+ PUBLIC_ROLE_ARGV.diarist.parse,
1336
+ );
1337
+ return {
1338
+ exitCode: result.exitCode,
1339
+ ...(result.terminal === undefined ? {} : { terminal: result.terminal }),
1340
+ };
1341
+ }
1342
+
1313
1343
  // Coder public run path with package-owned TDD method (#109).
1314
1344
  if (parsed.command === "coder") {
1315
1345
  const agentDir = resolveAgentDir(env, home);
@@ -4,6 +4,7 @@
4
4
  import { mkdir, readFile, writeFile } from "node:fs/promises";
5
5
  import { dirname, join } from "node:path";
6
6
 
7
+ import { assertRegisteredHostName, DEFAULT_ROLE_TURN_HOST } from "../host-descriptions.ts";
7
8
  import { assertLegalEngineName } from "../package-resources/engine-material.ts";
8
9
  import { resolveConfiguredProvinceOfficer } from "../institutional-resolution.ts";
9
10
  import {
@@ -218,7 +219,8 @@ export function setPersistentSeatHost(
218
219
  }
219
220
  return { ...config, seats: { ...config.seats, [seat]: rest } };
220
221
  }
221
- return { ...config, seats: { ...config.seats, [seat]: { ...previous, host } } };
222
+ const registered = assertRegisteredHostName(host);
223
+ return { ...config, seats: { ...config.seats, [seat]: { ...previous, host: registered } } };
222
224
  }
223
225
 
224
226
  /**
@@ -295,7 +297,7 @@ export function setAutoResumeLimit(
295
297
  * Config-parse seam: persistent call axes belong to PUBLIC_CALLABLE_ROLES;
296
298
  * engine names need only path-safety syntax (no closed material catalog;
297
299
  * #376 / #378 / #391 / ADR 0069). Syntax authority = assertLegalEngineName
298
- * (no injected duplicate).
300
+ * (no injected duplicate). Persistent host must be pi or a description-table key (#510 / #731).
299
301
  */
300
302
  export function validatePublicCliConfigAxes(
301
303
  config: PublicCliConfig,
@@ -303,14 +305,25 @@ export function validatePublicCliConfigAxes(
303
305
  ): void {
304
306
  for (const seat of Object.keys(config.seats) as PublicConfigurableSeat[]) {
305
307
  const row = config.seats[seat];
306
- if (row?.engine === undefined) continue;
307
- try {
308
- assertLegalEngineName(row.engine);
309
- } catch (error) {
310
- throw new Error(
311
- `config seat ${seat} engine is illegal: ${row.engine}`,
312
- { cause: error },
313
- );
308
+ if (row?.engine !== undefined) {
309
+ try {
310
+ assertLegalEngineName(row.engine);
311
+ } catch (error) {
312
+ throw new Error(
313
+ `config seat ${seat} engine is illegal: ${row.engine}`,
314
+ { cause: error },
315
+ );
316
+ }
317
+ }
318
+ if (row?.host !== undefined) {
319
+ try {
320
+ assertRegisteredHostName(row.host);
321
+ } catch (error) {
322
+ throw new Error(
323
+ `config seat ${seat} host is unregistered: ${row.host}`,
324
+ { cause: error },
325
+ );
326
+ }
314
327
  }
315
328
  }
316
329
  }
@@ -550,7 +563,7 @@ function attachHostAxis(
550
563
  if (invocation?.host !== undefined) return { ...seat, host: invocation.host, hostSource: "invocation" };
551
564
  const persistent = config.seats[seat.seat]?.host;
552
565
  if (persistent !== undefined) return { ...seat, host: persistent, hostSource: "persistent" };
553
- return { ...seat, host: "pi", hostSource: "default" };
566
+ return { ...seat, host: DEFAULT_ROLE_TURN_HOST, hostSource: "default" };
554
567
  }
555
568
 
556
569
  function attachEngineAxis(
@@ -1,20 +1,11 @@
1
1
  /**
2
- * Public Countersign Role run: admit ticket materials → diarist pipeline step →
3
- * shared post-admission coordinator → settle Terminal result
4
- * (#572 / ADR 0074 / ADR 0075). #599: manual resume continues the exact session.
5
- * Diarist is a prior station on the court pipeline, not a countersign call.
6
- * Unbound admission resolves ticket via shared seat LLM bind (#635) before the diary station.
2
+ * Public Countersign Role run: admit ticket materials → shared post-admission
3
+ * coordinator → settle Terminal result (#572 / ADR 0074). #599: manual resume
4
+ * continues the exact session. Unbound admission resolves its ticket via the
5
+ * shared seat LLM bind (#635). Who runs 起居郎, and when, is the caller's
6
+ * composition (ADR 0010 / ADR 0075 `no-call-rule`) — not this seat's business.
7
7
  */
8
8
  import type { DurablePrincipalAuthority, RoleTurnRequest } from "../host-contracts.ts";
9
- import {
10
- createDiaristIssueFaceFetcher,
11
- DiaristIssueSourceError,
12
- resolveDiaristGithubOrigin,
13
- runDiarist,
14
- type DiaristIssueFace,
15
- type DiaristRunResult,
16
- } from "../diarist.ts";
17
- import { appendIssueSourceFailureDiagnostic } from "../ticket-provenance.ts";
18
9
  import { engineSessionMaterialFromOptions } from "../package-resources/engine-material.ts";
19
10
  import { CliUsageError } from "./cli-errors.ts";
20
11
  import {
@@ -29,7 +20,6 @@ import {
29
20
  ticketNumberFromProbe,
30
21
  tryResumeSameTicketSeatRun,
31
22
  } from "./seat-ticket-binding.ts";
32
- import { tryHomeFromAkRolesPath } from "../activation-ledger-topology.ts";
33
23
  import {
34
24
  prepareSummonsResumeMaterials,
35
25
  runPostAdmissionOneShot,
@@ -194,7 +184,6 @@ export async function runPublicCountersign(
194
184
  turnRequest,
195
185
  buildCountersignTurnRequest(admitted, turnProjection),
196
186
  );
197
- await runCountersignDiaristStation(admitted, env);
198
187
  },
199
188
  }),
200
189
  ...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
@@ -222,10 +211,9 @@ function countersignAdapters(options?: {
222
211
 
223
212
  /**
224
213
  * Resume a previously admitted Countersign run (#599 / DK-3 / #637).
225
- * Restores role/ticket/session identity. Every court re-entry runs the diarist
226
- * station first (ADR 0075 refresh-every-court). Same-ticket summons deliver this
227
- * turn's instruction + frozen attachments on the resume prompt; manual resume
228
- * keeps package-envelope / caller-message semantics and birth attachments.
214
+ * Restores role/ticket/session identity. Same-ticket summons deliver this turn's
215
+ * instruction + frozen attachments on the resume prompt; manual resume keeps
216
+ * package-envelope / caller-message semantics and birth attachments.
229
217
  */
230
218
  export async function runPublicCountersignResume(
231
219
  request: PublicResumeRequest,
@@ -261,115 +249,7 @@ export async function runPublicCountersignResume(
261
249
  ),
262
250
  );
263
251
  },
264
- adapters: countersignAdapters({
265
- beforeDispatch: async (admitted) => {
266
- await runCountersignDiaristStation(admitted, env);
267
- },
268
- }),
252
+ adapters: countersignAdapters(),
269
253
  ...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
270
254
  });
271
255
  }
272
-
273
- /**
274
- * Court-pipeline prior station: refresh ticket-provenance before countersign turn.
275
- * Caller-invisible — collector failures append durable volume diagnostics and the
276
- * station continues; issue-source / ADR source-read / watermark honesty failures
277
- * leave typed durable diagnostics and propagate (失败诚实).
278
- * Missing ticketNumber (true-unbound after pre-court resolution) skips the station
279
- * — no diary is minted for a true-unbound run.
280
- *
281
- * Issue body/comments come from the shared GitHub seam only. Attachments stay
282
- * attachments — never merged and mislabeled as issue-body-comment.
283
- * Bound ticket never silently degrades to “no issue face”.
284
- */
285
- export async function runCountersignDiaristStation(
286
- admitted: AdmittedCountersignInvocation,
287
- env: Pick<CountersignRunEnv, "cwd" | "packageRoot">,
288
- ): Promise<DiaristRunResult | undefined> {
289
- if (admitted.ticketNumber === undefined) return undefined;
290
-
291
- const issueFace = await loadBoundIssueFace(admitted);
292
- const home = tryHomeFromAkRolesPath(admitted.runDirectory);
293
-
294
- const result = await runDiarist({
295
- ticketNumber: admitted.ticketNumber,
296
- cwd: admitted.projectRoot,
297
- ...(home === undefined ? {} : { home }),
298
- issueFace,
299
- sessionCwds: [admitted.projectRoot, env.cwd],
300
- ...(env.packageRoot === undefined ? {} : { packageRoot: env.packageRoot }),
301
- });
302
- return result;
303
- }
304
-
305
- /**
306
- * Acquire issue face for a bound ticket. Failures are typed + durable on the
307
- * ticket-provenance volume, then propagated — never washed into empty face.
308
- */
309
- function persistIssueSourceFailure(
310
- admitted: AdmittedCountersignInvocation,
311
- ticketNumber: number,
312
- error: DiaristIssueSourceError,
313
- ): never {
314
- const home = tryHomeFromAkRolesPath(admitted.runDirectory);
315
- appendIssueSourceFailureDiagnostic({
316
- ticketNumber,
317
- cwd: admitted.projectRoot,
318
- ...(home === undefined ? {} : { home }),
319
- cause: error.message,
320
- reason: error.reason,
321
- });
322
- throw error;
323
- }
324
-
325
- async function loadBoundIssueFace(
326
- admitted: AdmittedCountersignInvocation,
327
- ): Promise<DiaristIssueFace> {
328
- const ticketNumber = admitted.ticketNumber;
329
- if (ticketNumber === undefined) {
330
- throw new Error("loadBoundIssueFace requires a bound ticketNumber");
331
- }
332
-
333
- const origin = resolveDiaristGithubOrigin(admitted.projectRoot);
334
- if (origin === undefined) {
335
- persistIssueSourceFailure(
336
- admitted,
337
- ticketNumber,
338
- new DiaristIssueSourceError(
339
- "origin-unresolved",
340
- `bound ticket #${ticketNumber} issue face requires a resolvable github.com origin remote`,
341
- ),
342
- );
343
- }
344
-
345
- const fetcher = createDiaristIssueFaceFetcher();
346
- let face: DiaristIssueFace | undefined;
347
- try {
348
- face = await fetcher({
349
- owner: origin.owner,
350
- repo: origin.repo,
351
- ticketNumber,
352
- });
353
- } catch (error) {
354
- const typed =
355
- error instanceof DiaristIssueSourceError
356
- ? error
357
- : new DiaristIssueSourceError(
358
- "issue-unavailable",
359
- `issue face fetch failed for ${origin.owner}/${origin.repo}#${ticketNumber}`,
360
- { cause: error },
361
- );
362
- persistIssueSourceFailure(admitted, ticketNumber, typed);
363
- }
364
- if (face === undefined) {
365
- persistIssueSourceFailure(
366
- admitted,
367
- ticketNumber,
368
- new DiaristIssueSourceError(
369
- "issue-unavailable",
370
- `issue face unavailable for ${origin.owner}/${origin.repo}#${ticketNumber}`,
371
- ),
372
- );
373
- }
374
- return face;
375
- }