@akagilnc/pi-workflow-roles 0.1.6014 → 0.1.6021

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.
@@ -4,10 +4,7 @@
4
4
  * starts two ordinary single-axis runs here.
5
5
  */
6
6
  import { dirname, resolve } from "node:path";
7
- import { MISSING_TICKET_REASK_MATERIAL } from "../report-ticket-tool.js";
8
7
  import { isUnboundRunDirectory, sessionFileOf } from "../role-run-placement.js";
9
- import { rewriteRunDirectoryPathValue } from "../role-run-relocation.js";
10
- import { readPackageMaterial } from "../session-opening-materials.js";
11
8
  import { createPiDoctorAuditor } from "../doctor-auditor.js";
12
9
  import { officerConclusionReask, gateOfficerForSubject } from "../gatekeeper-role.js";
13
10
  import { receivedDiscriminator } from "../submission-errors.js";
@@ -100,21 +97,11 @@ function unreadablePostSubmissionStatus(admitted, terminal) {
100
97
  * status: the caller presents the volumes already in hand and does not advance
101
98
  * review. ADR 0007 audit continues are not this shape.
102
99
  */
103
- /**
104
- * Drop turn-scoped soft-reask identity; keep cross-turn budgets and ownership cell.
105
- * #1171 F2-R5 / F2-R9: ordinary status reask and court continue must not inherit
106
- * softTicketReaskTurn (that flag only governs the soft-reask turn's own settle).
107
- */
108
- function withoutSoftTicketReaskTurn(env) {
109
- const { softTicketReaskTurn: _softTicketReaskTurn, ...rest } = env;
110
- return rest;
111
- }
112
100
  function withUnreadableReask(env) {
113
101
  const spent = env.unreadableReasksSpent ?? 0;
114
102
  if (spent >= deliveryLimitFromConfig(env.autoResumeLimit))
115
103
  return undefined;
116
- // Ordinary status reask: strip soft-reask turn identity; keep spent budget (#1171 F2-R9).
117
- return { ...withoutSoftTicketReaskTurn(env), unreadableReasksSpent: spent + 1 };
104
+ return { ...env, unreadableReasksSpent: spent + 1 };
118
105
  }
119
106
  /** Budget spent on an unreadable status. Distinct from "status is readable". */
120
107
  const UNREADABLE_REASK_EXHAUSTED = { unreadableReaskExhausted: true };
@@ -256,113 +243,11 @@ async function reaskUnreadablePostSubmissionStatus(admitted, terminal, env, io,
256
243
  summons: { instructionEmpty: true },
257
244
  }, next, io);
258
245
  }
259
- /**
260
- * #1171: sealed submission still leaves the leg unbound → soft reask once.
261
- * Does not reject the sealed receipt; exhaustion leaves unbound as filed.
262
- * Reask copy lives in package resources (ADR 0073).
263
- * #1171 F2-R7: ownership comes from this nested resume's settlement seam
264
- * (softTicketReaskOwnership.sealedSubstitute set when the soft-reask turn itself seals),
265
- * not full-leg sealed count / time-window deltas that can absorb another court's seal.
266
- */
267
- async function reaskMissingTicketOnce(admitted, terminal, env, io) {
268
- if ((env.ticketReasksSpent ?? 0) >= 1)
269
- return { kind: "none" };
270
- // #1192: a table lookup has no one to ask, and a second turn would re-seal.
271
- if (isNavigatorSeat(admitted.role))
272
- return { kind: "none" };
273
- if (admitted.ticketNumber !== undefined)
274
- return { kind: "none" };
275
- if (!isUnboundRunDirectory(admitted.runDirectory))
276
- return { kind: "none" };
277
- if (terminal?.roleOutcome.kind !== "accepted" && terminal?.roleOutcome.kind !== "audit_escalation") {
278
- return { kind: "none" };
279
- }
280
- const payloads = terminal.roleOutcome.payloads ?? [];
281
- if (payloads.length === 0)
282
- return { kind: "none" };
283
- // Sealed-with-ticket skips via admitted.ticketNumber after bindSealedTicketNumber
284
- // (post-admission). Do not re-parse payload.ticketNumber here — that would
285
- // duplicate the existing ticket seam (#1171 notary: no rule copy).
286
- const instruction = (await readPackageMaterial(MISSING_TICKET_REASK_MATERIAL)).trim();
287
- // Spend budget and mark this turn as the soft reask (settlement identity).
288
- // Ownership cell is set at that turn's settlement; env spreads keep the same
289
- // ref through audit continue — see envForCourtContinue (#1171 F2-R5 / F2-R7).
290
- const softTicketReaskOwnership = { sealedSubstitute: false };
291
- const softReaskEnv = {
292
- ...env,
293
- ticketReasksSpent: 1,
294
- softTicketReaskTurn: true,
295
- softTicketReaskOwnership,
296
- };
297
- // Named #1171 missing-ticket: summons opens a new court; message carries the
298
- // package reask copy (not arbitrary summons.instruction passthrough).
299
- const result = await runPublicInstructionSeatResume({
300
- runId: admitted.runId,
301
- runDirectory: admitted.runDirectory,
302
- message: instruction,
303
- summons: { instructionEmpty: true },
304
- }, softReaskEnv, io);
305
- if (softTicketReaskOwnership.sealedSubstitute
306
- || terminalCarriesSealedSubmission(result.terminal)) {
307
- return { kind: "nested_owns", result };
308
- }
309
- return { kind: "no_substitute", result };
310
- }
311
- /** Court continue: keep cross-turn budgets + ownership cell; drop turn-scoped soft-reask identity. */
246
+ /** Court continue resets the unreadable-status budget. */
312
247
  function envForCourtContinue(env) {
313
- return { ...withoutSoftTicketReaskTurn(env), unreadableReasksSpent: 0 };
248
+ return { ...env, unreadableReasksSpent: 0 };
314
249
  }
315
- /** True when a terminal still carries a sealed accepted / audit_escalation volume. */
316
- function terminalCarriesSealedSubmission(terminal) {
317
- const kind = terminal?.roleOutcome.kind;
318
- if (kind !== "accepted" && kind !== "audit_escalation")
319
- return false;
320
- return (terminal?.roleOutcome.payloads ?? []).length > 0;
321
- }
322
- /**
323
- * #1171 F2-R2: kept original terminal may still name pre-relocate artifact
324
- * paths after a report-only reask moved the leg. Project onto live admitted
325
- * via the same rewrite seam post-admission uses after relocate.
326
- */
327
- function projectKeptOriginalArtifactsOntoLiveAdmitted(original, kept) {
328
- const oldRunDirectory = original.admitted?.runDirectory;
329
- const newRunDirectory = kept.admitted?.runDirectory;
330
- if (oldRunDirectory === undefined
331
- || newRunDirectory === undefined
332
- || oldRunDirectory === newRunDirectory
333
- || kept.terminal === undefined) {
334
- return;
335
- }
336
- for (const artifact of kept.terminal.artifacts) {
337
- artifact.path = rewriteRunDirectoryPathValue(artifact.path, oldRunDirectory, newRunDirectory);
338
- }
339
- }
340
- /**
341
- * #1171 F2: missing-ticket reask did not seal a substitute — keep original
342
- * terminal; take live placement from the reask when it relocated.
343
- * #1171 F2-R1: silence / report-only stays on the original chain; a real host
344
- * failure (nonzero exit) must be delivered honestly — do not wash it into the
345
- * original accepted result. Sealed original volume remains on disk either way.
346
- * #1171 F2-R2: after keep, project report artifact paths onto the live
347
- * admitted directory so public result, durable face, and artifact refs agree.
348
- * Substitute ownership is decided once at the soft-reask call seam (F2-R6);
349
- * this helper only finishes the keep path.
350
- */
351
- function mergeMissingTicketReaskKeepOriginal(original, ticketReask) {
352
- if (ticketReask.exitCode !== 0)
353
- return ticketReask;
354
- const admitted = ticketReask.admitted ?? original.admitted;
355
- if (admitted === undefined)
356
- return { ...original };
357
- const kept = { ...original, admitted };
358
- projectKeptOriginalArtifactsOntoLiveAdmitted(original, kept);
359
- return kept;
360
- }
361
- /**
362
- * Post-submission status reask, then missing-ticket soft reask, then audit.
363
- * #1171 F3: status exhaustion must not skip the one missing-ticket reask.
364
- * #1171 F2: ticket reask without a substitute seal continues the original chain.
365
- */
250
+ /** Post-submission status reask, then audit. */
366
251
  async function continueAfterPostSubmissionGuards(result, admittedRole, env, io, held) {
367
252
  const live = result.admitted;
368
253
  if (live === undefined) {
@@ -370,38 +255,21 @@ async function continueAfterPostSubmissionGuards(result, admittedRole, env, io,
370
255
  return result;
371
256
  }
372
257
  const unreadableReask = await reaskUnreadablePostSubmissionStatus(live, result.terminal, env, io);
373
- let afterStatus = result;
258
+ let continued = result;
374
259
  if (isUnreadableReaskExhausted(unreadableReask)) {
375
260
  const terminal = result.terminal === undefined
376
261
  ? undefined
377
262
  : withUnsettledDirection(result.terminal);
378
- afterStatus = terminal === undefined
263
+ continued = terminal === undefined
379
264
  ? { ...result, admitted: live }
380
265
  : { ...result, admitted: live, terminal };
381
- // Fall through: missing-ticket reask still owed on the original sealed volume.
382
266
  }
383
267
  else if (unreadableReask !== undefined) {
384
268
  return unreadableReask;
385
269
  }
386
- const forTicket = afterStatus.admitted ?? live;
387
- // Ownership is decided once inside reaskMissingTicketOnce (#1171 F2-R6).
388
- const softReask = await reaskMissingTicketOnce(forTicket, afterStatus.terminal ?? result.terminal, env, io);
389
- if (softReask.kind === "nested_owns") {
390
- // Nested resume already finished guards + audit/present for the substitute.
391
- // Do not run a second outer settlement of the same public call (#1171 F2-R4b).
392
- return softReask.result;
393
- }
394
- const continued = softReask.kind === "none"
395
- ? afterStatus
396
- : mergeMissingTicketReaskKeepOriginal(afterStatus, softReask.result);
397
270
  const continuedAdmitted = continued.admitted ?? live;
398
- // Spent budget rides the outer audit/continue chain (#1171 F2-R4a). Soft-reask
399
- // settlement identity does not — ordinary continue turns publish no_receipt (#1171 F2-R5).
400
- const continuedEnv = softReask.kind !== "none"
401
- ? { ...env, ticketReasksSpent: 1 }
402
- : env;
403
271
  if (AUDITED_ROLES.has(admittedRole) && continued.terminal?.roleOutcome.kind === "accepted") {
404
- return auditSubmittedRole({ ...continued, admitted: continuedAdmitted }, continuedEnv, io);
272
+ return auditSubmittedRole({ ...continued, admitted: continuedAdmitted }, env, io);
405
273
  }
406
274
  await presentOriginalVolume(held, continued.terminal, io, continuedAdmitted.runDirectory);
407
275
  return { ...continued, admitted: continuedAdmitted };
@@ -863,8 +731,7 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
863
731
  const result = await runCountersignBody(parsed, env, {
864
732
  stdout: (value) => held.push(value), stderr: io.stderr,
865
733
  });
866
- // Same post-submission guards as every other seat (#1171 F8): missing-ticket
867
- // soft reask once, then audit — no early return that skips the reask.
734
+ // Apply the same post-submission guards and audit as every other seat.
868
735
  return continueAfterPostSubmissionGuards(result, "countersign", env, io, held);
869
736
  }
870
737
  if ("parallelLenses" in record && record.parallelLenses === true) {
@@ -1020,9 +887,7 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
1020
887
  await persistAdmittedSourceRunPath(admitted, auditorSource, auditorSubject);
1021
888
  admitted = { ...admitted, sourceRunPath: auditorSource };
1022
889
  }
1023
- // #1208 C2: same helper as resume — known board ticket on the audited
1024
- // source binds and relocates before the missing-ticket soft reask can
1025
- // fire. True no-ticket sources stay unbound (#1171 asks once).
890
+ // #1208 C2: same helper as resume — inherit the audited source's known ticket.
1026
891
  admitted = await inheritKnownTicketFromSource(admitted, env.principalAuthority);
1027
892
  const runAdmitted = async () => {
1028
893
  await markRunAdmitted(admitted, env.principalAuthority);
@@ -1322,8 +1187,6 @@ chainOrder) {
1322
1187
  }
1323
1188
  /** Finished submissions enter the existing audit gate after their tool call has returned. */
1324
1189
  async function auditSubmittedRole(turn, env, io, resumedOfficer, passedReceipt) {
1325
- // Ordinary audit chain: drop soft-reask turn identity; keep budgets/ownership (#1171 F2-R9).
1326
- env = withoutSoftTicketReaskTurn(env);
1327
1190
  const admitted = turn.admitted;
1328
1191
  if (admitted === undefined || !AUDITED_ROLES.has(admitted.role)
1329
1192
  || turn.terminal?.roleOutcome.kind !== "accepted")