@akagilnc/pi-workflow-roles 0.1.3663 → 0.1.3671

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.
@@ -4658,6 +4658,7 @@ async function collectBookRunTicketNumbers(input) {
4658
4658
  }
4659
4659
  const known = /* @__PURE__ */ new Set();
4660
4660
  for (const entry of entries) {
4661
+ if (!entry.includes("@")) continue;
4661
4662
  const ticketNumber = await readRunTicketNumber(join11(runsDir, entry));
4662
4663
  if (ticketNumber !== void 0) known.add(ticketNumber);
4663
4664
  }
@@ -1,13 +1,16 @@
1
1
  /**
2
2
  * 系统随案递送本票起居录的中立指针段(ADR 0081 `automatic-case-material`;
3
+ * #709 公共入口通用递送与 #742 给事中受理链路同源;
3
4
  * 指针输入沿 ADR 0079 `summons-pointer-input`,不把卷宗正文塞进提示词)。
4
5
  * 只读已有案卷:不刷新、不生成、不校验内容、不新增拒收或停工条件。
5
6
  * 机器文本仅中立标识材料(ADR 0073),用途说明归角色材料所有。
7
+ *
8
+ * 递送挂载点唯一:`post-admission` 在 beforeDispatch 之后为每个公共入口追加本段。
6
9
  */
7
10
  import { stat } from "node:fs/promises";
8
11
  import { resolveTicketProvenanceVolume } from "../ticket-provenance.js";
9
- /** Section heading of the system-delivered dossier pointer. */
10
- export const CASE_DOSSIER_SECTION_HEADING = "## 本票起居录(系统随案提供)";
12
+ /** Section heading of the system-delivered dossier pointer (presentation only). */
13
+ const CASE_DOSSIER_SECTION_HEADING = "## 本票起居录(系统随案提供)";
11
14
  /** Honest one-line state of one dossier file: present, absent, or unreadable. */
12
15
  async function describeDossierFile(path) {
13
16
  try {
@@ -17831,6 +17831,7 @@ async function collectBookRunTicketNumbers(input) {
17831
17831
  }
17832
17832
  const known = /* @__PURE__ */ new Set();
17833
17833
  for (const entry of entries) {
17834
+ if (!entry.includes("@")) continue;
17834
17835
  const ticketNumber = await readRunTicketNumber(join11(runsDir, entry));
17835
17836
  if (ticketNumber !== void 0) known.add(ticketNumber);
17836
17837
  }
@@ -28326,167 +28327,6 @@ var init_collector_run = __esm({
28326
28327
  }
28327
28328
  });
28328
28329
 
28329
- // src/public-cli/countersign-run.ts
28330
- function buildCountersignTurnRequest(admitted, options) {
28331
- return projectRoleTurnRequest(
28332
- admitted,
28333
- {
28334
- activation: {
28335
- role: "countersign",
28336
- // Admitted typed binding rides the turn activation seam to the Notary gate.
28337
- ...admitted.ticketNumber === void 0 ? {} : { ticketNumber: admitted.ticketNumber }
28338
- }
28339
- },
28340
- options
28341
- );
28342
- }
28343
- async function runPublicCountersign(argv, env, io, parseCountersignArgv2) {
28344
- let parsed;
28345
- try {
28346
- parsed = parseCountersignArgv2(argv);
28347
- } catch (error) {
28348
- if (error instanceof CliUsageError) {
28349
- presentStructuralRejection(error, io);
28350
- return { exitCode: 2 };
28351
- }
28352
- throw error;
28353
- }
28354
- const projectRoot = parsed.project ?? env.cwd;
28355
- const reusedTicketNumber = await resolveKnownTicketNumber({
28356
- instruction: parsed.instruction,
28357
- projectRoot,
28358
- home: env.home
28359
- });
28360
- if (reusedTicketNumber !== void 0) {
28361
- const summons = {
28362
- instruction: parsed.instruction,
28363
- instructionEmpty: parsed.instruction.trim() === "",
28364
- attachmentPaths: parsed.attachmentPaths
28365
- };
28366
- const resumed = await tryResumeSameTicketSeatRun({
28367
- home: env.home,
28368
- projectRoot,
28369
- role: "countersign",
28370
- ticketNumber: reusedTicketNumber,
28371
- freshSummons: env.freshSummons,
28372
- summons,
28373
- resume: (runId, materials) => runPublicCountersignResume(
28374
- { runId, ...materials === void 0 ? {} : { summons: materials } },
28375
- env,
28376
- io
28377
- )
28378
- });
28379
- if (resumed !== void 0) return resumed;
28380
- }
28381
- let admitted;
28382
- try {
28383
- admitted = await admitCountersignInvocation({
28384
- home: env.home,
28385
- principalAuthority: env.principalAuthority,
28386
- cwd: env.cwd,
28387
- instruction: parsed.instruction,
28388
- attachmentPaths: parsed.attachmentPaths,
28389
- ...parsed.project === void 0 ? {} : { project: parsed.project },
28390
- ...env.createRunId === void 0 ? {} : { createRunId: env.createRunId },
28391
- ...env.model === void 0 ? {} : { model: env.model },
28392
- ...env.correlationId === void 0 ? {} : { correlationId: env.correlationId }
28393
- });
28394
- } catch (error) {
28395
- if (error instanceof CliUsageError) {
28396
- presentStructuralRejection(error, io);
28397
- return { exitCode: 2 };
28398
- }
28399
- throw error;
28400
- }
28401
- await markRunAdmitted(admitted, env.principalAuthority);
28402
- const turnProjection = {
28403
- packageRoot: env.packageRoot,
28404
- home: env.home,
28405
- agentDir: env.agentDir,
28406
- ...env.model === void 0 ? {} : { model: env.model },
28407
- ...env.engine === void 0 ? {} : { engine: env.engine },
28408
- ...env.timeoutMs === void 0 ? {} : { timeoutMs: env.timeoutMs },
28409
- ...env.correlationId === void 0 || env.correlationId.trim() === "" ? {} : { correlationId: env.correlationId },
28410
- continuation: {
28411
- kind: "initial",
28412
- prompt: buildCountersignTransportPrompt(
28413
- admitted,
28414
- engineSessionMaterialFromOptions({
28415
- ...env.engine === void 0 ? {} : { engine: env.engine },
28416
- packageRoot: env.packageRoot
28417
- })
28418
- )
28419
- }
28420
- };
28421
- const turnRequest = buildCountersignTurnRequest(admitted, turnProjection);
28422
- return await runPostAdmissionOneShot({
28423
- admitted,
28424
- env,
28425
- io,
28426
- request: turnRequest,
28427
- adapters: countersignAdapters({
28428
- beforeDispatch: async (admitted2) => {
28429
- await bindReusedTicketNumber(admitted2, reusedTicketNumber);
28430
- Object.assign(
28431
- turnRequest,
28432
- buildCountersignTurnRequest(admitted2, turnProjection)
28433
- );
28434
- }
28435
- }),
28436
- ...env.engine === void 0 ? {} : { effectiveEngine: env.engine }
28437
- });
28438
- }
28439
- function countersignAdapters(options) {
28440
- return {
28441
- trySettle: (admitted, authority, scope) => trySettleCountersignTerminalResult(admitted, authority, scope),
28442
- // Accepted receipts and failure terminals both present via shared path.
28443
- shouldPresentSettled: () => true,
28444
- ...options?.beforeDispatch === void 0 ? {} : { beforeDispatch: options.beforeDispatch }
28445
- };
28446
- }
28447
- async function runPublicCountersignResume(request, env, io) {
28448
- return await runPostAdmissionSeatResume({
28449
- request,
28450
- env,
28451
- io,
28452
- load: (effective) => loadResumableCountersignRun(
28453
- env.home,
28454
- effective.runId,
28455
- env.principalAuthority
28456
- ),
28457
- buildTurnRequest: async (admitted, effective) => {
28458
- const summonsPrepared = await prepareSummonsResumeMaterials(
28459
- admitted.runDirectory,
28460
- effective.summons
28461
- );
28462
- return buildCountersignTurnRequest(
28463
- admitted,
28464
- resumeTurnRequestProjectionOptions(
28465
- admitted,
28466
- effective,
28467
- env,
28468
- summonsPrepared
28469
- )
28470
- );
28471
- },
28472
- adapters: countersignAdapters(),
28473
- ...env.engine === void 0 ? {} : { effectiveEngine: env.engine }
28474
- });
28475
- }
28476
- var init_countersign_run = __esm({
28477
- "src/public-cli/countersign-run.ts"() {
28478
- "use strict";
28479
- init_engine_material();
28480
- init_cli_errors();
28481
- init_invocation();
28482
- init_seat_ticket_binding();
28483
- init_post_admission();
28484
- init_run_lifecycle();
28485
- init_settlement();
28486
- init_turn_request();
28487
- }
28488
- });
28489
-
28490
28330
  // src/diarist-mechanical.ts
28491
28331
  import { createHash as createHash6 } from "node:crypto";
28492
28332
  import { existsSync as existsSync9, readdirSync as readdirSync2, readFileSync as readFileSync3, realpathSync as realpathSync3, statSync as statSync2 } from "node:fs";
@@ -29013,6 +28853,12 @@ var init_diarist = __esm({
29013
28853
  });
29014
28854
 
29015
28855
  // src/public-cli/diarist-run.ts
28856
+ var diarist_run_exports = {};
28857
+ __export(diarist_run_exports, {
28858
+ buildDiaristTurnRequest: () => buildDiaristTurnRequest,
28859
+ runPublicDiarist: () => runPublicDiarist,
28860
+ runPublicDiaristResume: () => runPublicDiaristResume
28861
+ });
29016
28862
  import { existsSync as existsSync10 } from "node:fs";
29017
28863
  import { writeFile as writeFile7 } from "node:fs/promises";
29018
28864
  import { join as join26 } from "node:path";
@@ -29209,6 +29055,218 @@ var init_diarist_run = __esm({
29209
29055
  }
29210
29056
  });
29211
29057
 
29058
+ // src/public-cli/countersign-run.ts
29059
+ function buildCountersignTurnRequest(admitted, options) {
29060
+ return projectRoleTurnRequest(
29061
+ admitted,
29062
+ {
29063
+ activation: {
29064
+ role: "countersign",
29065
+ // Admitted typed binding rides the turn activation seam to the Notary gate.
29066
+ ...admitted.ticketNumber === void 0 ? {} : { ticketNumber: admitted.ticketNumber }
29067
+ }
29068
+ },
29069
+ options
29070
+ );
29071
+ }
29072
+ async function runCountersignCourtDiaristStation(admitted, env, io) {
29073
+ if (admitted.ticketNumber === void 0) return;
29074
+ if (env.runCourtDiaristStation !== void 0) {
29075
+ await env.runCourtDiaristStation(admitted);
29076
+ return;
29077
+ }
29078
+ const quietIo = {
29079
+ stdout() {
29080
+ },
29081
+ stderr(text) {
29082
+ if (text.trim() !== "") io.stderr(text);
29083
+ }
29084
+ };
29085
+ const { runPublicDiarist: runPublicDiarist2 } = await Promise.resolve().then(() => (init_diarist_run(), diarist_run_exports));
29086
+ const result2 = await runPublicDiarist2(
29087
+ [
29088
+ "--project",
29089
+ admitted.projectRoot,
29090
+ `\u6574\u7406 #${admitted.ticketNumber} \u7684\u672C\u6848\u4F9D\u636E\u3002`
29091
+ ],
29092
+ {
29093
+ home: env.home,
29094
+ agentDir: env.agentDir,
29095
+ packageRoot: env.packageRoot,
29096
+ cwd: env.cwd,
29097
+ principalAuthority: env.principalAuthority,
29098
+ roleTurnHost: env.roleTurnHost,
29099
+ sessionAppender: env.sessionAppender,
29100
+ ...env.model === void 0 ? {} : { model: env.model },
29101
+ ...env.engine === void 0 ? {} : { engine: env.engine },
29102
+ ...env.host === void 0 ? {} : { host: env.host },
29103
+ ...env.timeoutMs === void 0 ? {} : { timeoutMs: env.timeoutMs },
29104
+ ...env.credentials === void 0 ? {} : { credentials: env.credentials },
29105
+ ...env.correlationId === void 0 ? {} : { correlationId: env.correlationId },
29106
+ ...env.signal === void 0 ? {} : { signal: env.signal }
29107
+ },
29108
+ quietIo,
29109
+ parseDiaristArgv
29110
+ );
29111
+ if (result2.exitCode !== 0) {
29112
+ const detail = result2.terminal?.roleOutcome.kind === "failure" ? ` (roleOutcome=failure)` : "";
29113
+ throw new Error(
29114
+ `court diarist station failed for ticket #${admitted.ticketNumber}: exit ${result2.exitCode}${detail}`
29115
+ );
29116
+ }
29117
+ }
29118
+ async function runPublicCountersign(argv, env, io, parseCountersignArgv2) {
29119
+ let parsed;
29120
+ try {
29121
+ parsed = parseCountersignArgv2(argv);
29122
+ } catch (error) {
29123
+ if (error instanceof CliUsageError) {
29124
+ presentStructuralRejection(error, io);
29125
+ return { exitCode: 2 };
29126
+ }
29127
+ throw error;
29128
+ }
29129
+ const projectRoot = parsed.project ?? env.cwd;
29130
+ const reusedTicketNumber = await resolveKnownTicketNumber({
29131
+ instruction: parsed.instruction,
29132
+ projectRoot,
29133
+ home: env.home
29134
+ });
29135
+ if (reusedTicketNumber !== void 0) {
29136
+ const summons = {
29137
+ instruction: parsed.instruction,
29138
+ instructionEmpty: parsed.instruction.trim() === "",
29139
+ attachmentPaths: parsed.attachmentPaths
29140
+ };
29141
+ const resumed = await tryResumeSameTicketSeatRun({
29142
+ home: env.home,
29143
+ projectRoot,
29144
+ role: "countersign",
29145
+ ticketNumber: reusedTicketNumber,
29146
+ freshSummons: env.freshSummons,
29147
+ summons,
29148
+ resume: (runId, materials) => runPublicCountersignResume(
29149
+ { runId, ...materials === void 0 ? {} : { summons: materials } },
29150
+ env,
29151
+ io
29152
+ )
29153
+ });
29154
+ if (resumed !== void 0) return resumed;
29155
+ }
29156
+ let admitted;
29157
+ try {
29158
+ admitted = await admitCountersignInvocation({
29159
+ home: env.home,
29160
+ principalAuthority: env.principalAuthority,
29161
+ cwd: env.cwd,
29162
+ instruction: parsed.instruction,
29163
+ attachmentPaths: parsed.attachmentPaths,
29164
+ ...parsed.project === void 0 ? {} : { project: parsed.project },
29165
+ ...env.createRunId === void 0 ? {} : { createRunId: env.createRunId },
29166
+ ...env.model === void 0 ? {} : { model: env.model },
29167
+ ...env.correlationId === void 0 ? {} : { correlationId: env.correlationId }
29168
+ });
29169
+ } catch (error) {
29170
+ if (error instanceof CliUsageError) {
29171
+ presentStructuralRejection(error, io);
29172
+ return { exitCode: 2 };
29173
+ }
29174
+ throw error;
29175
+ }
29176
+ await markRunAdmitted(admitted, env.principalAuthority);
29177
+ const turnProjection = {
29178
+ packageRoot: env.packageRoot,
29179
+ home: env.home,
29180
+ agentDir: env.agentDir,
29181
+ ...env.model === void 0 ? {} : { model: env.model },
29182
+ ...env.engine === void 0 ? {} : { engine: env.engine },
29183
+ ...env.timeoutMs === void 0 ? {} : { timeoutMs: env.timeoutMs },
29184
+ ...env.correlationId === void 0 || env.correlationId.trim() === "" ? {} : { correlationId: env.correlationId },
29185
+ continuation: {
29186
+ kind: "initial",
29187
+ prompt: buildCountersignTransportPrompt(
29188
+ admitted,
29189
+ engineSessionMaterialFromOptions({
29190
+ ...env.engine === void 0 ? {} : { engine: env.engine },
29191
+ packageRoot: env.packageRoot
29192
+ })
29193
+ )
29194
+ }
29195
+ };
29196
+ const turnRequest = buildCountersignTurnRequest(admitted, turnProjection);
29197
+ return await runPostAdmissionOneShot({
29198
+ admitted,
29199
+ env,
29200
+ io,
29201
+ request: turnRequest,
29202
+ adapters: countersignAdapters({
29203
+ beforeDispatch: async (admittedSeat) => {
29204
+ await bindReusedTicketNumber(admittedSeat, reusedTicketNumber);
29205
+ Object.assign(
29206
+ turnRequest,
29207
+ buildCountersignTurnRequest(admittedSeat, turnProjection)
29208
+ );
29209
+ await runCountersignCourtDiaristStation(admittedSeat, env, io);
29210
+ }
29211
+ }),
29212
+ ...env.engine === void 0 ? {} : { effectiveEngine: env.engine }
29213
+ });
29214
+ }
29215
+ function countersignAdapters(options) {
29216
+ return {
29217
+ trySettle: (admitted, authority, scope) => trySettleCountersignTerminalResult(admitted, authority, scope),
29218
+ // Accepted receipts and failure terminals both present via shared path.
29219
+ shouldPresentSettled: () => true,
29220
+ ...options?.beforeDispatch === void 0 ? {} : { beforeDispatch: options.beforeDispatch }
29221
+ };
29222
+ }
29223
+ async function runPublicCountersignResume(request, env, io) {
29224
+ return await runPostAdmissionSeatResume({
29225
+ request,
29226
+ env,
29227
+ io,
29228
+ load: (effective) => loadResumableCountersignRun(
29229
+ env.home,
29230
+ effective.runId,
29231
+ env.principalAuthority
29232
+ ),
29233
+ buildTurnRequest: async (admitted, effective) => {
29234
+ const summonsPrepared = await prepareSummonsResumeMaterials(
29235
+ admitted.runDirectory,
29236
+ effective.summons
29237
+ );
29238
+ return buildCountersignTurnRequest(
29239
+ admitted,
29240
+ resumeTurnRequestProjectionOptions(
29241
+ admitted,
29242
+ effective,
29243
+ env,
29244
+ summonsPrepared
29245
+ )
29246
+ );
29247
+ },
29248
+ adapters: countersignAdapters({
29249
+ beforeDispatch: async (admitted) => {
29250
+ await runCountersignCourtDiaristStation(admitted, env, io);
29251
+ }
29252
+ }),
29253
+ ...env.engine === void 0 ? {} : { effectiveEngine: env.engine }
29254
+ });
29255
+ }
29256
+ var init_countersign_run = __esm({
29257
+ "src/public-cli/countersign-run.ts"() {
29258
+ "use strict";
29259
+ init_engine_material();
29260
+ init_cli_errors();
29261
+ init_invocation();
29262
+ init_seat_ticket_binding();
29263
+ init_post_admission();
29264
+ init_run_lifecycle();
29265
+ init_settlement();
29266
+ init_turn_request();
29267
+ }
29268
+ });
29269
+
29212
29270
  // src/public-cli/gleaner-left-run.ts
29213
29271
  function buildGleanerLeftTurnRequest(admitted, options) {
29214
29272
  return projectRoleTurnRequest(
@@ -163,11 +163,11 @@ export async function dispatchPostAdmissionTurn(input) {
163
163
  }
164
164
  }
165
165
  // Turn request is assembled after beforeDispatch so this turn sees whatever it
166
- // settled — the seat's ticket bind re-projection. Case dossier delivery
167
- // (ADR 0081) rides here for every public entry: first call, same-ticket
168
- // re-summons and manual resume alike. System refs append their own neutral
169
- // section; caller frozen attachments and the seat's own prompt bytes are
170
- // never rewritten.
166
+ // settled — the seat's ticket bind re-projection and any court diarist station
167
+ // writes (#742). Case dossier delivery (ADR 0081 / #709) rides here once for
168
+ // every public entry: first call, same-ticket re-summons and manual resume
169
+ // alike. System refs append their own neutral section; caller frozen
170
+ // attachments and the seat's own prompt bytes are never rewritten.
171
171
  let turnRequest = env.signal === undefined ? request : { ...request, signal: env.signal };
172
172
  if (hostTransition !== undefined) {
173
173
  turnRequest = { ...turnRequest, hostTransition };
@@ -845,6 +845,10 @@ export async function collectBookRunTicketNumbers(input) {
845
845
  }
846
846
  const known = new Set();
847
847
  for (const entry of entries) {
848
+ // runs/ also holds non-run files (institutional-resolution.json, Finder
849
+ // .DS_Store); only `<runId>@<role>` directories are runs (#769).
850
+ if (!entry.includes("@"))
851
+ continue;
848
852
  const ticketNumber = await readRunTicketNumber(join(runsDir, entry));
849
853
  if (ticketNumber !== undefined)
850
854
  known.add(ticketNumber);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akagilnc/pi-workflow-roles",
3
- "version": "0.1.3663",
3
+ "version": "0.1.3671",
4
4
  "description": "Soul-bound workflow roles for Pi",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -1,15 +1,18 @@
1
1
  /**
2
2
  * 系统随案递送本票起居录的中立指针段(ADR 0081 `automatic-case-material`;
3
+ * #709 公共入口通用递送与 #742 给事中受理链路同源;
3
4
  * 指针输入沿 ADR 0079 `summons-pointer-input`,不把卷宗正文塞进提示词)。
4
5
  * 只读已有案卷:不刷新、不生成、不校验内容、不新增拒收或停工条件。
5
6
  * 机器文本仅中立标识材料(ADR 0073),用途说明归角色材料所有。
7
+ *
8
+ * 递送挂载点唯一:`post-admission` 在 beforeDispatch 之后为每个公共入口追加本段。
6
9
  */
7
10
  import { stat } from "node:fs/promises";
8
11
 
9
12
  import { resolveTicketProvenanceVolume } from "../ticket-provenance.ts";
10
13
 
11
- /** Section heading of the system-delivered dossier pointer. */
12
- export const CASE_DOSSIER_SECTION_HEADING = "## 本票起居录(系统随案提供)" as const;
14
+ /** Section heading of the system-delivered dossier pointer (presentation only). */
15
+ const CASE_DOSSIER_SECTION_HEADING = "## 本票起居录(系统随案提供)" as const;
13
16
 
14
17
  /** Honest one-line state of one dossier file: present, absent, or unreadable. */
15
18
  async function describeDossierFile(path: string): Promise<string> {
@@ -1,9 +1,16 @@
1
1
  /**
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 reuses a known ticket identity
5
- * (#709). Who runs 起居郎, and when, is the caller's composition
6
- * (ADR 0010 / ADR 0075 `no-call-rule`) — not this seat's business.
2
+ * Public Countersign Role run: admit ticket materials → court-pipeline prior
3
+ * station (起居郎) → shared post-admission coordinator → settle Terminal result
4
+ * (#572 / ADR 0074 / ADR 0075 / #742). #599: manual resume continues the exact
5
+ * session. Unbound admission reuses a known ticket identity (#709) before the
6
+ * diary station.
7
+ *
8
+ * Court admission auto-runs 起居郎 first, then the countersign body — caller
9
+ * adds no diarist argv (L66672). This is the admission pipeline's prior station,
10
+ * not the countersign role calling 起居郎 (L66958 / L66966). Who may call 起居郎
11
+ * and in what order is not written into law (ADR 0075 `no-call-rule`); the
12
+ * present admission effect is what this seat currently does (L108315 目前是这样用的).
13
+ * 起居录 path delivery is owned once by post-admission (#709 / ADR 0081).
7
14
  */
8
15
  import type { DurablePrincipalAuthority, RoleTurnRequest } from "../host-contracts.ts";
9
16
  import { engineSessionMaterialFromOptions } from "../package-resources/engine-material.ts";
@@ -11,6 +18,7 @@ import { CliUsageError } from "./cli-errors.ts";
11
18
  import {
12
19
  admitCountersignInvocation,
13
20
  buildCountersignTransportPrompt,
21
+ parseDiaristArgv,
14
22
  type AdmittedCountersignInvocation,
15
23
  type ParseCountersignArgvResult,
16
24
  } from "./invocation.ts";
@@ -46,6 +54,13 @@ import {
46
54
  export type CountersignRunEnv = PostAdmissionEnv & {
47
55
  principalAuthority: DurablePrincipalAuthority;
48
56
  createRunId?: () => string;
57
+ /**
58
+ * Test seam: replace the court-pipeline 起居郎 station.
59
+ * Production leaves this unset and runs the public diarist seat.
60
+ */
61
+ runCourtDiaristStation?: (
62
+ admitted: AdmittedCountersignInvocation,
63
+ ) => Promise<void>;
49
64
  };
50
65
 
51
66
  /** Project admitted invocation onto the host-neutral turn request. */
@@ -68,6 +83,75 @@ export function buildCountersignTurnRequest(
68
83
  );
69
84
  }
70
85
 
86
+ /**
87
+ * Court-pipeline prior station: refresh this ticket's 起居录 before the
88
+ * countersign body turn (ADR 0075 `refresh-every-court`; #742 restore).
89
+ * Caller-invisible — no diarist argv on the countersign command line.
90
+ * Missing ticketNumber (true-unbound) skips the station — no diary is minted
91
+ * for a true-unbound run.
92
+ *
93
+ * Invokes the public 起居郎 seat so the book carries an `@diarist` run ahead of
94
+ * the countersign body. Station failure propagates (失败诚实 — no wash).
95
+ * Path delivery onto materials is not this station's job — post-admission owns it.
96
+ */
97
+ export async function runCountersignCourtDiaristStation(
98
+ admitted: AdmittedCountersignInvocation,
99
+ env: CountersignRunEnv,
100
+ io: CliIo,
101
+ ): Promise<void> {
102
+ if (admitted.ticketNumber === undefined) return;
103
+ if (env.runCourtDiaristStation !== undefined) {
104
+ await env.runCourtDiaristStation(admitted);
105
+ return;
106
+ }
107
+
108
+ // Quiet face: the countersign caller must not see diarist CLI chatter.
109
+ const quietIo: CliIo = {
110
+ stdout() {},
111
+ stderr(text: string) {
112
+ // Surface nested failures onto the parent stderr only; no success noise.
113
+ if (text.trim() !== "") io.stderr(text);
114
+ },
115
+ };
116
+
117
+ const { runPublicDiarist } = await import("./diarist-run.ts");
118
+ const result = await runPublicDiarist(
119
+ [
120
+ "--project",
121
+ admitted.projectRoot,
122
+ `整理 #${admitted.ticketNumber} 的本案依据。`,
123
+ ],
124
+ {
125
+ home: env.home,
126
+ agentDir: env.agentDir,
127
+ packageRoot: env.packageRoot,
128
+ cwd: env.cwd,
129
+ principalAuthority: env.principalAuthority,
130
+ roleTurnHost: env.roleTurnHost,
131
+ sessionAppender: env.sessionAppender,
132
+ ...(env.model === undefined ? {} : { model: env.model }),
133
+ ...(env.engine === undefined ? {} : { engine: env.engine }),
134
+ ...(env.host === undefined ? {} : { host: env.host }),
135
+ ...(env.timeoutMs === undefined ? {} : { timeoutMs: env.timeoutMs }),
136
+ ...(env.credentials === undefined ? {} : { credentials: env.credentials }),
137
+ ...(env.correlationId === undefined ? {} : { correlationId: env.correlationId }),
138
+ ...(env.signal === undefined ? {} : { signal: env.signal }),
139
+ },
140
+ quietIo,
141
+ parseDiaristArgv,
142
+ );
143
+
144
+ if (result.exitCode !== 0) {
145
+ const detail =
146
+ result.terminal?.roleOutcome.kind === "failure"
147
+ ? ` (roleOutcome=failure)`
148
+ : "";
149
+ throw new Error(
150
+ `court diarist station failed for ticket #${admitted.ticketNumber}: exit ${result.exitCode}${detail}`,
151
+ );
152
+ }
153
+ }
154
+
71
155
  export async function runPublicCountersign(
72
156
  argv: readonly string[],
73
157
  env: CountersignRunEnv,
@@ -174,13 +258,16 @@ export async function runPublicCountersign(
174
258
  io,
175
259
  request: turnRequest,
176
260
  adapters: countersignAdapters({
177
- beforeDispatch: async (admitted) => {
261
+ beforeDispatch: async (admittedSeat) => {
178
262
  // #635/#709: bind the reused identity inside the controlled-failure boundary.
179
- await bindReusedTicketNumber(admitted, reusedTicketNumber);
263
+ await bindReusedTicketNumber(admittedSeat, reusedTicketNumber);
180
264
  Object.assign(
181
265
  turnRequest,
182
- buildCountersignTurnRequest(admitted, turnProjection),
266
+ buildCountersignTurnRequest(admittedSeat, turnProjection),
183
267
  );
268
+ // Court station after bind so the diarist round sees the ticket.
269
+ // Dossier pointer delivery rides post-admission after this hook (#709).
270
+ await runCountersignCourtDiaristStation(admittedSeat, env, io);
184
271
  },
185
272
  }),
186
273
  ...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
@@ -208,9 +295,11 @@ function countersignAdapters(options?: {
208
295
 
209
296
  /**
210
297
  * Resume a previously admitted Countersign run (#599 / DK-3 / #637).
211
- * Restores role/ticket/session identity. Same-ticket summons deliver this turn's
212
- * instruction + frozen attachments on the resume prompt; manual resume keeps
213
- * package-envelope / caller-message semantics and birth attachments.
298
+ * Restores role/ticket/session identity. Every court re-entry runs the diarist
299
+ * station first (ADR 0075 `refresh-every-court`). Same-ticket summons deliver this
300
+ * turn's instruction + frozen attachments on the resume prompt; manual resume
301
+ * keeps package-envelope / caller-message semantics and birth attachments.
302
+ * 起居录 path delivery remains post-admission's single mount (#709).
214
303
  */
215
304
  export async function runPublicCountersignResume(
216
305
  request: PublicResumeRequest,
@@ -246,7 +335,11 @@ export async function runPublicCountersignResume(
246
335
  ),
247
336
  );
248
337
  },
249
- adapters: countersignAdapters(),
338
+ adapters: countersignAdapters({
339
+ beforeDispatch: async (admitted) => {
340
+ await runCountersignCourtDiaristStation(admitted, env, io);
341
+ },
342
+ }),
250
343
  ...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
251
344
  });
252
345
  }
@@ -362,11 +362,11 @@ export async function dispatchPostAdmissionTurn<
362
362
  }
363
363
 
364
364
  // Turn request is assembled after beforeDispatch so this turn sees whatever it
365
- // settled — the seat's ticket bind re-projection. Case dossier delivery
366
- // (ADR 0081) rides here for every public entry: first call, same-ticket
367
- // re-summons and manual resume alike. System refs append their own neutral
368
- // section; caller frozen attachments and the seat's own prompt bytes are
369
- // never rewritten.
365
+ // settled — the seat's ticket bind re-projection and any court diarist station
366
+ // writes (#742). Case dossier delivery (ADR 0081 / #709) rides here once for
367
+ // every public entry: first call, same-ticket re-summons and manual resume
368
+ // alike. System refs append their own neutral section; caller frozen
369
+ // attachments and the seat's own prompt bytes are never rewritten.
370
370
  let turnRequest: RoleTurnRequest =
371
371
  env.signal === undefined ? request : { ...request, signal: env.signal };
372
372
  if (hostTransition !== undefined) {
@@ -1161,6 +1161,9 @@ export async function collectBookRunTicketNumbers(input: {
1161
1161
  }
1162
1162
  const known = new Set<number>();
1163
1163
  for (const entry of entries) {
1164
+ // runs/ also holds non-run files (institutional-resolution.json, Finder
1165
+ // .DS_Store); only `<runId>@<role>` directories are runs (#769).
1166
+ if (!entry.includes("@")) continue;
1164
1167
  const ticketNumber = await readRunTicketNumber(join(runsDir, entry));
1165
1168
  if (ticketNumber !== undefined) known.add(ticketNumber);
1166
1169
  }