@akagilnc/pi-workflow-roles 0.1.5762 → 0.1.5797

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 (52) hide show
  1. package/dist/acp-host/production-host.js +685 -285
  2. package/dist/external-host-turn-loop.js +3 -0
  3. package/dist/headless-host/production-host.js +658 -258
  4. package/dist/migrate-book-topology.js +70 -29
  5. package/dist/navigator-attendance.js +1 -1
  6. package/dist/packaged-role-registry.js +3 -2
  7. package/dist/pi/role-turn-host.js +6 -1
  8. package/dist/public-cli/instruction-seat-run.js +152 -60
  9. package/dist/public-cli/invocation.js +98 -46
  10. package/dist/public-cli/main.js +482 -222
  11. package/dist/public-cli/option-definitions.js +4 -4
  12. package/dist/public-cli/post-admission.js +19 -28
  13. package/dist/public-cli/run-lifecycle.js +10 -21
  14. package/dist/public-cli/turn-request.js +3 -0
  15. package/dist/public-role-summons.js +11 -8
  16. package/dist/report-ticket-tool.js +3 -3
  17. package/dist/role-run-placement.js +14 -2
  18. package/dist/role-run-relocation.js +2 -2
  19. package/dist/run-dossier.js +5 -4
  20. package/dist/run-terminal-artifacts.js +0 -7
  21. package/dist/session-identity.js +3 -2
  22. package/dist/sitian-appender.js +12 -3
  23. package/dist/submission-ledger.js +24 -0
  24. package/dist/ticket-progress.js +306 -0
  25. package/dist/user-dialogue-stdin.js +23 -17
  26. package/package.json +1 -1
  27. package/src/external-host-turn-loop.ts +7 -1
  28. package/src/host-contracts.ts +1 -1
  29. package/src/navigator-attendance.ts +2 -1
  30. package/src/packaged-role-registry.ts +3 -2
  31. package/src/pi/adapter.ts +57 -45
  32. package/src/pi/role-turn-host.ts +6 -1
  33. package/src/prepared-role-turn.ts +6 -0
  34. package/src/public-cli/instruction-seat-run.ts +166 -68
  35. package/src/public-cli/invocation.ts +120 -46
  36. package/src/public-cli/option-definitions.ts +5 -4
  37. package/src/public-cli/post-admission.ts +19 -30
  38. package/src/public-cli/run-lifecycle.ts +10 -27
  39. package/src/public-cli/turn-request.ts +3 -0
  40. package/src/public-role-summons.ts +27 -16
  41. package/src/report-ticket-tool.ts +7 -3
  42. package/src/role-envelope.ts +5 -0
  43. package/src/role-run-placement.ts +19 -2
  44. package/src/role-run-relocation.ts +2 -2
  45. package/src/role-runtime.ts +19 -2
  46. package/src/run-dossier.ts +5 -3
  47. package/src/run-terminal-artifacts.ts +0 -8
  48. package/src/session-identity.ts +3 -2
  49. package/src/sitian-appender.ts +14 -3
  50. package/src/submission-ledger.ts +27 -0
  51. package/src/ticket-progress.ts +383 -0
  52. package/src/user-dialogue-stdin.ts +21 -14
@@ -958,6 +958,12 @@ import { basename as basename3, dirname as dirname3, join as join2 } from "node:
958
958
  function resolveSitianVolumeCategory(kind) {
959
959
  return kind;
960
960
  }
961
+ function runDirectoryOwningSessionParent(sessionParentDir) {
962
+ if (basename3(sessionParentDir) !== "session") return void 0;
963
+ const runDirectory = dirname3(sessionParentDir);
964
+ if (parseRunLeaf(basename3(runDirectory)) === void 0) return void 0;
965
+ return runDirectory;
966
+ }
961
967
  function sitianVolumeDirectory(sessionDirectory, category) {
962
968
  return join2(sessionDirectory, category);
963
969
  }
@@ -989,8 +995,8 @@ function resolveSitianRecordPathInLedger(input, ledgerHome) {
989
995
  throw new Error("Sitian record ownership requires a parent session inside the ledger home");
990
996
  }
991
997
  const sessionParentDir = dirname3(input.sessionParent);
992
- const runDirectory = dirname3(sessionParentDir);
993
- if (basename3(sessionParentDir) === "session" && parseRunLeaf(basename3(runDirectory)) !== void 0) {
998
+ const runDirectory = runDirectoryOwningSessionParent(sessionParentDir);
999
+ if (runDirectory !== void 0) {
994
1000
  sessionDir = runDirectory;
995
1001
  recordFile = join2(runDirectory, RUN_HISTORY_KINDS.has(input.kind) ? RUN_HISTORY_FILE : RUN_STATE_KINDS.has(input.kind) ? RUN_STATE_FILE : RUN_LOG_FILE);
996
1002
  } else {
@@ -1183,7 +1189,9 @@ function render(runDirectory, history, state, log, unreadable) {
1183
1189
  original = payload.landingPath;
1184
1190
  }
1185
1191
  }
1186
- if (typeof original === "string") original = join3(runDirectory, "session", basename4(original));
1192
+ if (typeof original === "string") {
1193
+ original = join3(sessionDirectoryOf(runDirectory), basename4(original));
1194
+ }
1187
1195
  whole.host = { sessions, ...original === void 0 ? {} : { original } };
1188
1196
  whole.submission = latest === void 0 ? {} : { latest };
1189
1197
  if (Object.keys(unreadable).length > 0) whole.unreadable = unreadable;
@@ -9455,9 +9463,8 @@ var init_packaged_role_registry = __esm({
9455
9463
  bareCommand: false,
9456
9464
  outputTool: NOTARY_OUTPUT_TOOL_NAME,
9457
9465
  settlement: "accepted",
9458
- /** Court reask replaces the initial prompt. Otherwise the fixed kickoff. */
9466
+ /** Dialogue via shared resolveReviewSeatDialogueBody (review = fixed dispatch). */
9459
9467
  reaskPrompt: true,
9460
- transportPrompt: "fixed-kickoff",
9461
9468
  gateStageLabel: "\u7B26\u5B9D\u90CE",
9462
9469
  /** Gate summon binds --source-run and keeps dialogue off the argv. */
9463
9470
  gateSummon: "source-run",
@@ -9480,6 +9487,8 @@ var init_packaged_role_registry = __esm({
9480
9487
  phases: [null],
9481
9488
  outputTool: COUNTERSIGN_OUTPUT_TOOL_NAME,
9482
9489
  settlement: "accepted",
9490
+ /** Same shared dialogue resolver as other review seats (#1208 C1). */
9491
+ reaskPrompt: true,
9483
9492
  gateStageLabel: "\u7ED9\u4E8B\u4E2D",
9484
9493
  /** Gate summon carries the parent payload as the instruction and a parent run id. */
9485
9494
  gateSummon: "parent-instruction",
@@ -10175,7 +10184,7 @@ async function rewriteSessionTranscriptBindings(path, rewrites) {
10175
10184
  );
10176
10185
  }
10177
10186
  async function rewriteNestedMachinePathPages(pagesDirectory, rewrites) {
10178
- const sessionRoot = join6(pagesDirectory, "session");
10187
+ const sessionRoot = sessionDirectoryOf(pagesDirectory);
10179
10188
  async function walk(directory) {
10180
10189
  let entries;
10181
10190
  try {
@@ -10272,13 +10281,30 @@ var init_role_run_relocation = __esm({
10272
10281
  }
10273
10282
  });
10274
10283
 
10275
- // src/sha256.ts
10276
- import { createHash } from "node:crypto";
10277
- function sha256Hex(bytes) {
10278
- return createHash("sha256").update(bytes).digest("hex");
10279
- }
10280
- var init_sha256 = __esm({
10281
- "src/sha256.ts"() {
10284
+ // src/sitian-facade.ts
10285
+ var init_sitian_facade = __esm({
10286
+ "src/sitian-facade.ts"() {
10287
+ "use strict";
10288
+ init_role_run_placement();
10289
+ init_sitian_appender();
10290
+ init_sitian_contracts();
10291
+ init_sitian_appender();
10292
+ init_sitian_reader();
10293
+ }
10294
+ });
10295
+
10296
+ // src/host-session-record.ts
10297
+ var init_host_session_record = __esm({
10298
+ "src/host-session-record.ts"() {
10299
+ "use strict";
10300
+ init_sitian_facade();
10301
+ init_unknown_value();
10302
+ }
10303
+ });
10304
+
10305
+ // src/host-contracts.ts
10306
+ var init_host_contracts = __esm({
10307
+ "src/host-contracts.ts"() {
10282
10308
  "use strict";
10283
10309
  }
10284
10310
  });
@@ -10327,6 +10353,32 @@ var init_terminating_tools = __esm({
10327
10353
  }
10328
10354
  });
10329
10355
 
10356
+ // src/ticket-progress.ts
10357
+ var init_ticket_progress = __esm({
10358
+ "src/ticket-progress.ts"() {
10359
+ "use strict";
10360
+ init_host_session_record();
10361
+ init_host_contracts();
10362
+ init_terminating_tools();
10363
+ init_packaged_role_registry();
10364
+ init_role_run_placement();
10365
+ init_run_dossier();
10366
+ init_sitian_reader();
10367
+ init_unknown_value();
10368
+ }
10369
+ });
10370
+
10371
+ // src/sha256.ts
10372
+ import { createHash } from "node:crypto";
10373
+ function sha256Hex(bytes) {
10374
+ return createHash("sha256").update(bytes).digest("hex");
10375
+ }
10376
+ var init_sha256 = __esm({
10377
+ "src/sha256.ts"() {
10378
+ "use strict";
10379
+ }
10380
+ });
10381
+
10330
10382
  // src/doctor-evidence.ts
10331
10383
  var init_doctor_evidence = __esm({
10332
10384
  "src/doctor-evidence.ts"() {
@@ -10381,18 +10433,6 @@ var init_session_dialogue = __esm({
10381
10433
  }
10382
10434
  });
10383
10435
 
10384
- // src/sitian-facade.ts
10385
- var init_sitian_facade = __esm({
10386
- "src/sitian-facade.ts"() {
10387
- "use strict";
10388
- init_role_run_placement();
10389
- init_sitian_appender();
10390
- init_sitian_contracts();
10391
- init_sitian_appender();
10392
- init_sitian_reader();
10393
- }
10394
- });
10395
-
10396
10436
  // src/ticket-provenance.ts
10397
10437
  var init_ticket_provenance = __esm({
10398
10438
  "src/ticket-provenance.ts"() {
@@ -10772,8 +10812,8 @@ var init_option_definitions = __esm({
10772
10812
  repeatable: false,
10773
10813
  form: "option",
10774
10814
  description: {
10775
- en: "Required source run locator (runId@role under the book home, or path to that run directory). Zero prompt/attachment projection.",
10776
- zh: "\u5FC5\u586B\u6E90 run \u5B9A\u4F4D\u7B26\uFF08\u7C3F\u5185 runId@role\uFF0C\u6216\u8BE5 run \u76EE\u5F55\u8DEF\u5F84\uFF09\u3002\u96F6 prompt/\u9644\u4EF6\u6295\u5F71\u3002"
10815
+ en: "Required source run locator (runId@role under the book home, or path to that run directory). Binds the audited identity; review dialogue is the fixed Notary dispatch (verdict stays in the ledger). Missing-ticket and unreadable-status reasks keep their existing diagnostic text.",
10816
+ zh: "\u5FC5\u586B\u6E90 run \u5B9A\u4F4D\u7B26\uFF08\u7C3F\u5185 runId@role\uFF0C\u6216\u8BE5 run \u76EE\u5F55\u8DEF\u5F84\uFF09\u3002\u7ED1\u5B9A\u53D7\u5BA1\u8EAB\u4EFD\uFF1B\u5BA1\u7A3F\u5BF9\u8BDD\u4E3A\u7B26\u5B9D\u90CE\u56FA\u5B9A\u6D3E\u5355\u6587\uFF08\u5224\u8BCD\u7559\u5728\u5377\u5B97\u81EA\u884C\u8BFB\u53D6\uFF09\u3002\u7F3A\u7968\u53F7\u4E0E\u8BFB\u4E0D\u51FA status \u7684\u91CD\u4EA4\u4ECD\u9012\u65E2\u6709\u8BCA\u65AD\u539F\u6587\u3002"
10777
10817
  }
10778
10818
  }
10779
10819
  ];
@@ -11107,7 +11147,7 @@ var init_option_definitions = __esm({
11107
11147
  },
11108
11148
  notary: {
11109
11149
  command: "notary",
11110
- summary: "Direct Notary summons; duties: souls/notary.md. Zero prompt/attachment.",
11150
+ summary: "Direct Notary summons; duties: souls/notary.md. Review uses fixed dispatch \u2014 binds --source-run identity; does not accept caller review prompt; verdict stays in the ledger. Missing-ticket (#1171) and unreadable-status reasks deliver their existing diagnostic text on the same seat, not the review sentence.",
11111
11151
  usage: ["ak-role notary --source-run <runId@role|path> [options]"],
11112
11152
  examples: [
11113
11153
  "ak-role notary --source-run 01a034f1-75bf-71a6-bcf5-d1299145b1a5@judge",
@@ -11168,7 +11208,7 @@ var init_option_definitions = __esm({
11168
11208
  },
11169
11209
  resume: {
11170
11210
  command: "resume",
11171
- summary: "Resume a role run under the live seat table (model/host/engine); session principal must still exist. Every callable seat, including Notary/\u7B26\u5B9D\u90CE, accepts the optional caller message and passes it through unchanged as the continuation prompt. Notary explicit new still accepts only its source-run locator, not a caller prompt. Global --model/--thinking/--host/--engine must be placed before <runId> (either before `resume` or between `resume` and <runId>); the one argv after <runId> is the opaque message, not a flag position (#471).",
11211
+ summary: "Resume a role run under the live seat table (model/host/engine); session principal must still exist. Every callable seat accepts the optional caller message and passes it through unchanged as the continuation prompt. Notary new/direct review calls use only --source-run plus the fixed review dispatch (no caller review prompt); public manual resume still passes the caller message. Global --model/--thinking/--host/--engine must be placed before <runId> (either before `resume` or between `resume` and <runId>); the one argv after <runId> is the opaque message, not a flag position (#471).",
11172
11212
  usage: ["ak-role resume <runId> [message]"],
11173
11213
  examples: [
11174
11214
  "ak-role resume 01abc\u2026",
@@ -11204,6 +11244,7 @@ var init_invocation = __esm({
11204
11244
  init_activation_ledger_topology();
11205
11245
  init_activation_ledger_git();
11206
11246
  init_role_run_placement();
11247
+ init_ticket_progress();
11207
11248
  init_packaged_role_registry();
11208
11249
  init_run_ticket_number();
11209
11250
  init_role_run_relocation();
@@ -268,7 +268,7 @@ function createNavigatorAttendance(options) {
268
268
  await promptAllowingRejectedPrepare(request);
269
269
  while (output === void 0 && prepareBatchRejected && delivery.nextAction() === "request-delivery") {
270
270
  delivery.recordDeliveryRequest();
271
- await promptAllowingRejectedPrepare(JSON.stringify(delivery.deliveryState()));
271
+ await promptAllowingRejectedPrepare(request);
272
272
  }
273
273
  if (output === void 0 && delivery.nextAction() === "request-delivery") {
274
274
  delivery.closeBudget();
@@ -267,9 +267,8 @@ const PUBLIC_ROLE_RECORDS = [
267
267
  bareCommand: false,
268
268
  outputTool: NOTARY_OUTPUT_TOOL_NAME,
269
269
  settlement: "accepted",
270
- /** Court reask replaces the initial prompt. Otherwise the fixed kickoff. */
270
+ /** Dialogue via shared resolveReviewSeatDialogueBody (review = fixed dispatch). */
271
271
  reaskPrompt: true,
272
- transportPrompt: "fixed-kickoff",
273
272
  gateStageLabel: "\u7B26\u5B9D\u90CE",
274
273
  /** Gate summon binds --source-run and keeps dialogue off the argv. */
275
274
  gateSummon: "source-run",
@@ -292,6 +291,8 @@ const PUBLIC_ROLE_RECORDS = [
292
291
  phases: [null],
293
292
  outputTool: COUNTERSIGN_OUTPUT_TOOL_NAME,
294
293
  settlement: "accepted",
294
+ /** Same shared dialogue resolver as other review seats (#1208 C1). */
295
+ reaskPrompt: true,
295
296
  gateStageLabel: "\u7ED9\u4E8B\u4E2D",
296
297
  /** Gate summon carries the parent payload as the instruction and a parent run id. */
297
298
  gateSummon: "parent-instruction",
@@ -336,7 +336,9 @@ export function createPiRoleTurnHost(config) {
336
336
  const roleEntry = await realpath(resolveInternalRoleEntrypoint(config.packageRoot));
337
337
  const extraArgs = buildPiTurnExtraArgs(turnRequest, config.principalAuthority, config.extraPiArgs ?? []);
338
338
  const args = buildExplicitInternalActivationArgs(roleEntry, extraArgs);
339
- const stdin = encodeUserDialogueStdin(piUserDialogueBody(turnRequest));
339
+ const transportBody = piUserDialogueBody(turnRequest);
340
+ // #1199: progress instruction is these same body bytes (actual seat input).
341
+ const stdin = encodeUserDialogueStdin(transportBody);
340
342
  // Shared envelope isolates this call's court identity: omitting courtAttemptId
341
343
  // must not inherit a parent process.env.AK_ROLE_COURT_ATTEMPT (#637).
342
344
  const env = {
@@ -362,6 +364,9 @@ export function createPiRoleTurnHost(config) {
362
364
  delete env.AK_ROLE_HOST;
363
365
  else
364
366
  env.AK_ROLE_HOST = request.host.trim();
367
+ // #1199: 传召词 is unknown-length body — never argv/env (execve ARG_MAX / E2BIG).
368
+ // Typed stdin envelope carries dialogue + optional progress 传召词.
369
+ delete env.AK_ROLE_SUMMONS_INSTRUCTION;
365
370
  // #1132: the one effective delivery-request ceiling travels to the child
366
371
  // on its own env so the in-child role runtime and the worker submission
367
372
  // gate count with the same configured number the AK seam resolved. Omit
@@ -29,7 +29,7 @@ import { CliUsageError } from "./cli-errors.js";
29
29
  import { admitPublicRole, admittedSourceRunPath, bindAdmittedTicketNumber, appendCallerFileFlagPaths, buildInstructionTransportPrompt, materializeCountersignInvocation, persistAdmittedAuditedSubmissionToolCallId, persistAdmittedSourceRunPath, readAdmittedAuditedSubmissionToolCallId, recordAdmittedCorrelation, recordChildDiaristRun, relocateAdmittedRunToTicket, } from "./invocation.js";
30
30
  import { bindDiaristAssertedTicketNumber, presentControlledFailure, packageFaultNoteFor, prepareSummonsResumeMaterials, roleTurnOptions, runPostAdmissionOneShot, runPostAdmissionResumable, runPostAdmissionSeatResume, showResumeErrorPointer, resumeTurnRequestProjectionOptions, } from "./post-admission.js";
31
31
  import { presentTerminal } from "./auto-resume.js";
32
- import { buildAutoResumeContinuationPrompt, loadResumablePublicRole, markRunAdmitted, readCurrentCourt, } from "./run-lifecycle.js";
32
+ import { loadResumablePublicRole, markRunAdmitted, readCurrentCourt, } from "./run-lifecycle.js";
33
33
  import { tryResumeSameTicketSeatRun } from "./seat-ticket-binding.js";
34
34
  import { presentStructuralRejection, readBoundSessionEntries, trySettlePublicSeat, } from "./settlement.js";
35
35
  import { warnMissingMethodSkills } from "./machine-method-skills.js";
@@ -49,6 +49,20 @@ const POST_SUBMISSION_ROUTING = {
49
49
  judge: {
50
50
  statuses: REVIEW_QUEUE_STATUSES,
51
51
  },
52
+ // #1208 C1 / ADR 0055: every review-queue seat uses the shared
53
+ // unreadable-status seam (officerConclusionReask). Independent public
54
+ // entry for notary/auditor/inspector was the same miss — route here, do
55
+ // not invent a parallel loop. Gate summons still own the outer budget
56
+ // via nestedIncomplete when this seat exhausts unsettled.
57
+ notary: {
58
+ statuses: REVIEW_QUEUE_STATUSES,
59
+ },
60
+ auditor: {
61
+ statuses: REVIEW_QUEUE_STATUSES,
62
+ },
63
+ inspector: {
64
+ statuses: REVIEW_QUEUE_STATUSES,
65
+ },
52
66
  coder: {
53
67
  statuses: WORKER_ROUTING_STATUSES,
54
68
  reask: WORKER_STATUS_REASK,
@@ -160,10 +174,13 @@ async function reaskUnreadablePostSubmissionStatus(admitted, terminal, env, io,
160
174
  const next = withUnreadableReask(env);
161
175
  if (next === undefined)
162
176
  return UNREADABLE_REASK_EXHAUSTED;
177
+ // Named status-reask: summons opens a new court; message carries the diagnostic
178
+ // (not arbitrary summons.instruction passthrough through the review resolver).
163
179
  return runPublicInstructionSeatResume({
164
180
  runId: admitted.runId,
165
181
  runDirectory: admitted.runDirectory,
166
- summons: { instruction: message },
182
+ message,
183
+ summons: { instructionEmpty: true },
167
184
  }, next, io);
168
185
  }
169
186
  /**
@@ -201,10 +218,13 @@ async function reaskMissingTicketOnce(admitted, terminal, env, io) {
201
218
  softTicketReaskTurn: true,
202
219
  softTicketReaskOwnership,
203
220
  };
221
+ // Named #1171 missing-ticket: summons opens a new court; message carries the
222
+ // package reask copy (not arbitrary summons.instruction passthrough).
204
223
  const result = await runPublicInstructionSeatResume({
205
224
  runId: admitted.runId,
206
225
  runDirectory: admitted.runDirectory,
207
- summons: { instruction },
226
+ message: instruction,
227
+ summons: { instructionEmpty: true },
208
228
  }, softReaskEnv, io);
209
229
  if (softTicketReaskOwnership.sealedSubstitute
210
230
  || terminalCarriesSealedSubmission(result.terminal)) {
@@ -323,8 +343,8 @@ export function buildInstructionSeatTurnRequest(admitted, options) {
323
343
  }
324
344
  /**
325
345
  * Direct review-seat call: reconnect ledger peer-body delivery (ADR 0085 / #1166).
326
- * Gate summons already ride gateReviewInstruction; direct --source-run must not
327
- * leave a blank fixed-kickoff.
346
+ * Gate summons already ride gateReviewInstruction; direct --source-run falls
347
+ * through the shared dialogue resolver (notary: fixed dispatch).
328
348
  */
329
349
  async function loadLedgerPeerBody(sourceRunDirectory, projectRoot, home) {
330
350
  const runId = runIdFromRunDirectory(sourceRunDirectory);
@@ -344,40 +364,50 @@ async function loadLedgerPeerBody(sourceRunDirectory, projectRoot, home) {
344
364
  return undefined;
345
365
  return readableGateItem(latest.accepted);
346
366
  }
347
- /** Owner-approved fixed Notary dispatch (2026-10-09); never the parent verdict. */
367
+ /** Owner-approved fixed Notary review dispatch (2026-10-09 #1208); never the parent verdict. */
348
368
  export const NOTARY_FIXED_DISPATCH = "请审本轮受审对象,按符宝郎职掌交卷。";
349
369
  /**
350
370
  * One delivery rule for review-seat dialogue on new turns and same-parent resume
351
- * (#1166 / ADR 0085 / ADR 0087): explicit reask → non-empty caller dispatch →
352
- * source-only ledger peer body. Identity stays in startup materials.
353
- * #1195: ticket-court notary skips peer-body preload (countersign source only) and
354
- * receives the fixed dispatch instead (codex rejects an empty prompt).
371
+ * (#1166 / ADR 0085 / ADR 0087 / #1208): notary review uses fixed dispatch;
372
+ * named operational reasks ride reviewReask (first-mint / gate) or the resume
373
+ * request message seam — never arbitrary non-empty caller/summons passthrough.
374
+ * Other seats prefer format reask, then gate body, then caller dispatch, then
375
+ * ledger peer. Identity stays in startup materials. Notary never takes
376
+ * gate/parent verdict as dialogue (gateReviewInstruction stays unread for notary).
355
377
  */
356
- async function resolveReviewSeatDialogueBody(input) {
357
- const reask = input.reask;
358
- if (reask !== undefined && reask.length > 0)
359
- return reask;
378
+ export async function resolveReviewSeatDialogueBody(input) {
379
+ // #1208: fixed sentence is the review function only (owner 37bfe80d /
380
+ // 393daf98). reviewReask is the named operational override; caller/summons
381
+ // body is not a free passthrough (countersign rejected that prescription).
382
+ if (input.role === "notary") {
383
+ if (input.reviewReask !== undefined)
384
+ return input.reviewReask;
385
+ return NOTARY_FIXED_DISPATCH;
386
+ }
387
+ const preferred = input.reviewReask ?? input.gateReviewInstruction;
388
+ if (preferred !== undefined && preferred.length > 0)
389
+ return preferred;
360
390
  const caller = input.callerInstruction ?? "";
361
391
  if (caller.length > 0)
362
392
  return caller;
363
393
  const sourceRunPath = admittedSourceRunPath(input)?.trim();
364
394
  if (sourceRunPath === undefined)
365
395
  return undefined;
366
- if (input.role === "notary") {
367
- const { isTicketCourtCountersignSource } = await import("../run-terminal-artifacts.js");
368
- if (isTicketCourtCountersignSource(sourceRunPath))
369
- return NOTARY_FIXED_DISPATCH;
370
- }
371
396
  return await loadLedgerPeerBody(sourceRunPath, input.projectRoot, input.home);
372
397
  }
373
- async function resolveInitialPrompt(admitted, env) {
398
+ /**
399
+ * Initial host prompt for this turn (#1199: progress instruction = these bytes).
400
+ */
401
+ async function resolveInitialTurnPrompt(admitted, env) {
374
402
  const record = roleRecord(admitted.role);
375
403
  if ("reaskPrompt" in record && record.reaskPrompt === true) {
376
404
  const sourceRunPath = admittedSourceRunPath(admitted);
377
- const reask = env.reviewReask ?? env.gateReviewInstruction;
378
405
  const body = await resolveReviewSeatDialogueBody({
379
406
  role: admitted.role,
380
- ...(reask === undefined ? {} : { reask }),
407
+ ...(env.reviewReask === undefined ? {} : { reviewReask: env.reviewReask }),
408
+ ...(env.gateReviewInstruction === undefined
409
+ ? {}
410
+ : { gateReviewInstruction: env.gateReviewInstruction }),
381
411
  callerInstruction: admitted.instruction,
382
412
  ...(sourceRunPath === undefined ? {} : { sourceRunPath }),
383
413
  projectRoot: admitted.projectRoot,
@@ -486,20 +516,21 @@ async function dispatchAdmitted(admitted, env, io) {
486
516
  if (activeEnv.correlationId !== undefined && activeEnv.correlationId.trim() !== "") {
487
517
  await recordAdmittedCorrelation(admitted, activeEnv.correlationId);
488
518
  }
489
- const initialPromptText = await resolveInitialPrompt(admitted, activeEnv);
519
+ const initialPrompt = await resolveInitialTurnPrompt(admitted, activeEnv);
490
520
  const auto = "inCallAutoResume" in record && record.inCallAutoResume === true;
491
521
  if (auto) {
522
+ // #1208: auto-resume resends this turn's actual content, never a filler phrase.
492
523
  return await runPostAdmissionResumable({
493
524
  admitted,
494
525
  env: activeEnv,
495
526
  io: turnIo,
496
527
  buildInitialRequest: () => buildInstructionSeatTurnRequest(admitted, roleTurnOptions(activeEnv, admitted, {
497
528
  kind: "initial",
498
- prompt: initialPromptText,
529
+ prompt: initialPrompt,
499
530
  })),
500
531
  buildResumeRequest: () => buildInstructionSeatTurnRequest(admitted, roleTurnOptions(activeEnv, admitted, {
501
532
  kind: "resume",
502
- prompt: buildAutoResumeContinuationPrompt(),
533
+ prompt: initialPrompt,
503
534
  })),
504
535
  adapters,
505
536
  ...(activeEnv.engine === undefined ? {} : { effectiveEngine: activeEnv.engine }),
@@ -511,7 +542,7 @@ async function dispatchAdmitted(admitted, env, io) {
511
542
  io: turnIo,
512
543
  request: buildInstructionSeatTurnRequest(admitted, roleTurnOptions(activeEnv, admitted, {
513
544
  kind: "initial",
514
- prompt: initialPromptText,
545
+ prompt: initialPrompt,
515
546
  })),
516
547
  adapters,
517
548
  ...(activeEnv.engine === undefined ? {} : { effectiveEngine: activeEnv.engine }),
@@ -607,22 +638,31 @@ function resumeSameParentInstructionSeat(input) {
607
638
  ...(input.ticketNumber === undefined ? {} : { ticketNumber: input.ticketNumber }),
608
639
  freshSummons: input.env.freshSummons,
609
640
  summons: input.summons,
610
- resume: (runId, materials, runDirectory) => runPublicInstructionSeatResume({
611
- runId,
612
- runDirectory,
613
- ...(materials === undefined ? {} : { summons: materials }),
614
- }, input.env, input.io),
641
+ resume: (runId, materials, runDirectory) => {
642
+ // Named operational reask on this summon: explicit message seam.
643
+ // Review dialogue stays on summons → shared resolver (notary: fixed).
644
+ // Do not stuff reask into summons.instruction for later passthrough.
645
+ const reask = input.env.reviewReask;
646
+ return runPublicInstructionSeatResume({
647
+ runId,
648
+ runDirectory,
649
+ ...(reask === undefined ? {} : { message: reask }),
650
+ ...(materials === undefined ? {} : { summons: materials }),
651
+ }, input.env, input.io);
652
+ },
615
653
  });
616
654
  }
617
655
  /**
618
- * Same delivery rule as resolveInitialPrompt for same-parent resume summons.
619
- * Reask / explicit caller text / source-only ledger peer — one authority.
656
+ * Same delivery rule as resolveInitialTurnPrompt for same-parent resume summons.
657
+ * Shared resolver owns the input choice — callers only pass env fields.
620
658
  */
621
659
  async function sameParentDialogue(env, fallback, sourceRunPath, projectRoot, role) {
622
- const reask = env.reviewReask ?? env.gateReviewInstruction;
623
660
  const body = await resolveReviewSeatDialogueBody({
624
661
  role,
625
- ...(reask === undefined ? {} : { reask }),
662
+ ...(env.reviewReask === undefined ? {} : { reviewReask: env.reviewReask }),
663
+ ...(env.gateReviewInstruction === undefined
664
+ ? {}
665
+ : { gateReviewInstruction: env.gateReviewInstruction }),
626
666
  callerInstruction: fallback.instruction,
627
667
  sourceRunPath,
628
668
  projectRoot,
@@ -638,15 +678,20 @@ async function sameParentDialogue(env, fallback, sourceRunPath, projectRoot, rol
638
678
  * then the same one-shot settlement as every other seat.
639
679
  */
640
680
  async function runCountersignBody(parsed, env, io) {
681
+ const projectRoot = resolve(parsed.project ?? env.cwd);
641
682
  const gateParentRunPath = typeof env.parentRunPath === "string" && env.parentRunPath.trim() !== ""
642
683
  ? env.parentRunPath
643
684
  : undefined;
644
685
  if (gateParentRunPath !== undefined) {
645
- const resumeInstruction = env.reviewReask ?? env.gateReviewInstruction ?? parsed.instruction ?? "";
686
+ // #1208 C1: same shared dialogue resolver as every other same-parent seat.
687
+ const dialogue = await sameParentDialogue(env, {
688
+ instruction: parsed.instruction ?? "",
689
+ instructionEmpty: (parsed.instruction ?? "").length === 0,
690
+ }, gateParentRunPath, projectRoot, "countersign");
646
691
  const resumed = await resumeSameParentInstructionSeat({
647
692
  env,
648
693
  io,
649
- projectRoot: resolve(parsed.project ?? env.cwd),
694
+ projectRoot,
650
695
  role: "countersign",
651
696
  parentRunPath: gateParentRunPath,
652
697
  ...(isSafePositiveTicketNumber(env.boundTicketNumber)
@@ -654,8 +699,8 @@ async function runCountersignBody(parsed, env, io) {
654
699
  : {}),
655
700
  summons: {
656
701
  sourceRunPath: gateParentRunPath,
657
- instruction: resumeInstruction,
658
- instructionEmpty: resumeInstruction.length === 0,
702
+ instruction: dialogue.instruction,
703
+ instructionEmpty: dialogue.instructionEmpty,
659
704
  // #1166 J11: same-parent resume delivers this call's file-flag paths
660
705
  // through the shared summons → appendCallerFileFlagPaths rule.
661
706
  attachmentPaths: parsed.attachmentPaths ?? [],
@@ -691,14 +736,10 @@ async function runCountersignBody(parsed, env, io) {
691
736
  await persistAdmittedSourceRunPath(admitted, gateParentRunPath);
692
737
  admitted = { ...admitted, sourceRunPath: gateParentRunPath };
693
738
  }
694
- // #1166 J11: review-body branch must merge the same file-flag delivery as
695
- // resolveInitialPrompt / resumeTurnRequestProjectionOptions — never body alone.
696
- const reviewBody = env.reviewReask ?? env.gateReviewInstruction;
739
+ // #1208 C1 / #1166 J11: one shared initial-prompt seam (incl. file flags).
697
740
  const turnProjection = roleTurnOptions(env, admitted, {
698
741
  kind: "initial",
699
- prompt: reviewBody !== undefined
700
- ? appendCallerFileFlagPaths(reviewBody, admitted.attachments)
701
- : buildInstructionTransportPrompt(admitted),
742
+ prompt: await resolveInitialTurnPrompt(admitted, env),
702
743
  });
703
744
  const turnRequest = buildInstructionSeatTurnRequest(admitted, turnProjection);
704
745
  const result = await runPostAdmissionOneShot({
@@ -902,6 +943,10 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
902
943
  await persistAdmittedSourceRunPath(admitted, auditorSource, auditorSubject);
903
944
  admitted = { ...admitted, sourceRunPath: auditorSource };
904
945
  }
946
+ // #1208 C2: same helper as resume — known board ticket on the audited
947
+ // source binds and relocates before the missing-ticket soft reask can
948
+ // fire. True no-ticket sources stay unbound (#1171 asks once).
949
+ admitted = await inheritKnownTicketFromSource(admitted, env.principalAuthority);
905
950
  const runAdmitted = async () => {
906
951
  await markRunAdmitted(admitted, env.principalAuthority);
907
952
  if (role === "secretariat") {
@@ -946,6 +991,24 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
946
991
  }
947
992
  return runAdmitted();
948
993
  }
994
+ /**
995
+ * #1208 C2: same as new source-locator admission — when this leg is still
996
+ * unbound, inherit the board ticket already on the audited source and relocate.
997
+ * True no-ticket sources stay unbound so #1171 still asks once.
998
+ */
999
+ async function inheritKnownTicketFromSource(admitted, authority) {
1000
+ if (admitted.ticketNumber !== undefined)
1001
+ return admitted;
1002
+ const sourcePath = admittedSourceRunPath(admitted);
1003
+ if (sourcePath === undefined)
1004
+ return admitted;
1005
+ const sourceTicket = await readBoardTicketNumber(sourcePath);
1006
+ if (sourceTicket === undefined)
1007
+ return admitted;
1008
+ await bindAdmittedTicketNumber(admitted, sourceTicket);
1009
+ await relocateAdmittedRunToTicket(admitted, authority);
1010
+ return admitted;
1011
+ }
949
1012
  export async function runPublicInstructionSeatResume(request, env, io) {
950
1013
  let selectedRequest = request;
951
1014
  const resume = async () => {
@@ -960,24 +1023,48 @@ export async function runPublicInstructionSeatResume(request, env, io) {
960
1023
  io: turnIo,
961
1024
  load: async (effective) => {
962
1025
  const loaded = await loadResumablePublicRole(env.home, effective.runId, env.principalAuthority, effective.runDirectory === undefined ? undefined : { runDirectory: effective.runDirectory });
963
- if (packagedRebindSourceOnResume(loaded.admitted.role)
1026
+ let admitted = loaded.admitted;
1027
+ if (packagedRebindSourceOnResume(admitted.role)
964
1028
  && effective.summons?.sourceRunPath !== undefined
965
1029
  && effective.summons.sourceRun !== undefined) {
966
- return {
967
- ...loaded,
968
- admitted: {
969
- ...loaded.admitted,
970
- sourceRunPath: effective.summons.sourceRunPath,
971
- sourceRun: effective.summons.sourceRun,
972
- },
1030
+ // source-locator seats only (notary); sourceRun is on that admission face.
1031
+ admitted = {
1032
+ ...admitted,
1033
+ sourceRunPath: effective.summons.sourceRunPath,
1034
+ sourceRun: effective.summons.sourceRun,
973
1035
  };
974
1036
  }
975
- return loaded;
1037
+ admitted = await inheritKnownTicketFromSource(admitted, env.principalAuthority);
1038
+ return { ...loaded, admitted };
976
1039
  },
977
1040
  buildTurnRequest: async (admitted, effective) => {
978
1041
  const summonsPrepared = await prepareSummonsResumeMaterials(admitted.runDirectory, effective.summons);
1042
+ const projection = resumeTurnRequestProjectionOptions(admitted, effective, env, summonsPrepared);
1043
+ // #1208: internal summons only — shared resolver owns review dialogue.
1044
+ // Public bare resume / explicit message keep projection (operational
1045
+ // reasks use the message seam). Do not reselect stale env dialogue over
1046
+ // this-round summons (#1208 R1).
1047
+ if (effective.summons === undefined || effective.message !== undefined) {
1048
+ return buildInstructionSeatTurnRequest(admitted, { ...projection });
1049
+ }
1050
+ const sourceRunPath = admittedSourceRunPath(admitted);
1051
+ const body = await resolveReviewSeatDialogueBody({
1052
+ role: admitted.role,
1053
+ callerInstruction: summonsPrepared?.instruction ?? "",
1054
+ ...(sourceRunPath === undefined ? {} : { sourceRunPath }),
1055
+ projectRoot: admitted.projectRoot,
1056
+ home: env.home,
1057
+ });
1058
+ if (body === undefined) {
1059
+ return buildInstructionSeatTurnRequest(admitted, { ...projection });
1060
+ }
1061
+ const fileFlags = summonsPrepared?.attachments ?? [];
1062
+ const prompt = fileFlags.length === 0
1063
+ ? body
1064
+ : appendCallerFileFlagPaths(body, fileFlags);
979
1065
  return buildInstructionSeatTurnRequest(admitted, {
980
- ...resumeTurnRequestProjectionOptions(admitted, effective, env, summonsPrepared),
1066
+ ...projection,
1067
+ continuation: { kind: "resume", prompt },
981
1068
  });
982
1069
  },
983
1070
  adapters: {
@@ -1037,9 +1124,12 @@ async function queueConclusionFromChild(child, env, io) {
1037
1124
  return { stop: { exitCode: 0, ...(terminal === undefined ? {} : { terminal }) } };
1038
1125
  }
1039
1126
  let current = child;
1040
- // Notary, auditor, and inspector have no seat-local status reask. This loop
1041
- // is their one budget. The child resume keeps the caller's env so delivery,
1042
- // failure recovery, and other reask loops stay separate.
1127
+ // #1208: review-queue gate children also have seat-local
1128
+ // POST_SUBMISSION_ROUTING. This parent-side loop remains the
1129
+ // gate/correlation budget; nestedIncomplete from an exhausted seat-local
1130
+ // chain surfaces without a second spend. The child resume keeps the
1131
+ // caller's env so delivery, failure recovery, and other reask loops
1132
+ // stay separate.
1043
1133
  let budgetEnv = envForCourtContinue(env);
1044
1134
  for (;;) {
1045
1135
  const terminal = await trySettlePublicSeat(current, env.principalAuthority, { ...await readCurrentCourt(current.runDirectory), ...packageFaultScope(current, env, io) });
@@ -1177,7 +1267,8 @@ async function auditSubmittedRole(turn, env, io, resumedOfficer, passedReceipt)
1177
1267
  return runPublicInstructionSeatResume({
1178
1268
  runId: admitted.runId,
1179
1269
  runDirectory: admitted.runDirectory,
1180
- summons: { instruction: SECRETARIAT_STATUS_REASK },
1270
+ message: SECRETARIAT_STATUS_REASK,
1271
+ summons: { instructionEmpty: true },
1181
1272
  }, next, io);
1182
1273
  }
1183
1274
  if (admitted.role === "countersign" && (typeof status !== "string" || !REVIEW_QUEUE_STATUSES.has(status))) {
@@ -1192,7 +1283,8 @@ async function auditSubmittedRole(turn, env, io, resumedOfficer, passedReceipt)
1192
1283
  return runPublicInstructionSeatResume({
1193
1284
  runId: admitted.runId,
1194
1285
  runDirectory: admitted.runDirectory,
1195
- summons: { instruction: officerConclusionReask(status) },
1286
+ message: officerConclusionReask(status),
1287
+ summons: { instructionEmpty: true },
1196
1288
  }, next, io);
1197
1289
  }
1198
1290
  // Self-escalation is a valid open routing state for these seats only.