@akagilnc/pi-workflow-roles 0.1.5614 → 0.1.5650
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.
- package/dist/acp-host/production-host.js +960 -465
- package/dist/activation-ledger-topology.js +12 -0
- package/dist/external-host-turn-loop.js +281 -0
- package/dist/headless-host/production-host.js +900 -406
- package/dist/migrate-book-topology.js +8 -5
- package/dist/pi/role-turn-host.js +33 -3
- package/dist/public-cli/instruction-seat-run.js +177 -46
- package/dist/public-cli/invocation.js +5 -12
- package/dist/public-cli/main.js +541 -165
- package/dist/public-cli/post-admission.js +171 -18
- package/dist/public-cli/settlement.js +20 -10
- package/dist/report-ticket-tool.js +136 -0
- package/dist/role-run-placement.js +1 -2
- package/dist/role-run-relocation.js +24 -4
- package/package.json +1 -1
- package/resources/engine-dispatch.md +6 -3
- package/resources/engines/claude-code.md +4 -5
- package/resources/missing-ticket-reask.md +1 -0
- package/src/acp-host/role-turn-host.ts +41 -31
- package/src/activation-ledger-topology.ts +16 -0
- package/src/external-host-turn-loop.ts +103 -12
- package/src/headless-host/role-turn-host.ts +41 -25
- package/src/pi/role-turn-host.ts +37 -3
- package/src/public-cli/instruction-seat-run.ts +248 -50
- package/src/public-cli/invocation.ts +15 -27
- package/src/public-cli/post-admission.ts +250 -33
- package/src/public-cli/run-lifecycle.ts +5 -5
- package/src/public-cli/settlement.ts +38 -11
- package/src/public-cli/terminal.ts +6 -0
- package/src/report-ticket-tool.ts +176 -0
- package/src/role-envelope.ts +28 -3
- package/src/role-run-placement.ts +0 -2
- package/src/role-run-relocation.ts +45 -4
- package/src/role-runtime.ts +6 -0
- package/src/worker-submission-gates.ts +26 -3
|
@@ -4,7 +4,10 @@
|
|
|
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.ts";
|
|
7
8
|
import { isUnboundRunDirectory, sessionFileOf } from "../role-run-placement.ts";
|
|
9
|
+
import { rewriteRunDirectoryPathValue } from "../role-run-relocation.ts";
|
|
10
|
+
import { readPackageMaterial } from "../session-opening-materials.ts";
|
|
8
11
|
import { bookDirectOfficerRunPointer, readDirectOfficerRunPointer } from "../archivist-record-pointer.ts";
|
|
9
12
|
import { createPiDoctorAuditor } from "../doctor-auditor.ts";
|
|
10
13
|
|
|
@@ -26,7 +29,10 @@ import {
|
|
|
26
29
|
latestQueuePayload,
|
|
27
30
|
latestQueueStatus,
|
|
28
31
|
} from "../submission-gate.ts";
|
|
29
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
isSafePositiveTicketNumber,
|
|
34
|
+
readBoardTicketNumber,
|
|
35
|
+
} from "../run-ticket-number.ts";
|
|
30
36
|
import type { NotarySourceRunLocator } from "../notary-contracts.ts";
|
|
31
37
|
import { NotarySourceRunError, resolveNotarySourceRunLocator } from "../notary-source-run.ts";
|
|
32
38
|
import type { PackagedRole } from "../packaged-role-registry.ts";
|
|
@@ -173,10 +179,21 @@ function unreadablePostSubmissionStatus(
|
|
|
173
179
|
* status: the caller presents the volumes already in hand and does not advance
|
|
174
180
|
* review. ADR 0007 audit continues are not this shape.
|
|
175
181
|
*/
|
|
182
|
+
/**
|
|
183
|
+
* Drop turn-scoped soft-reask identity; keep cross-turn budgets and ownership cell.
|
|
184
|
+
* #1171 F2-R5 / F2-R9: ordinary status reask and court continue must not inherit
|
|
185
|
+
* softTicketReaskTurn (that flag only governs the soft-reask turn's own settle).
|
|
186
|
+
*/
|
|
187
|
+
function withoutSoftTicketReaskTurn(env: InstructionSeatRunEnv): InstructionSeatRunEnv {
|
|
188
|
+
const { softTicketReaskTurn: _softTicketReaskTurn, ...rest } = env;
|
|
189
|
+
return rest;
|
|
190
|
+
}
|
|
191
|
+
|
|
176
192
|
function withUnreadableReask(env: InstructionSeatRunEnv): InstructionSeatRunEnv | undefined {
|
|
177
193
|
const spent = env.unreadableReasksSpent ?? 0;
|
|
178
194
|
if (spent >= deliveryLimitFromConfig(env.autoResumeLimit)) return undefined;
|
|
179
|
-
|
|
195
|
+
// Ordinary status reask: strip soft-reask turn identity; keep spent budget (#1171 F2-R9).
|
|
196
|
+
return { ...withoutSoftTicketReaskTurn(env), unreadableReasksSpent: spent + 1 };
|
|
180
197
|
}
|
|
181
198
|
|
|
182
199
|
/** Budget spent on an unreadable status. Distinct from "status is readable". */
|
|
@@ -270,6 +287,203 @@ async function reaskUnreadablePostSubmissionStatus(
|
|
|
270
287
|
);
|
|
271
288
|
}
|
|
272
289
|
|
|
290
|
+
/**
|
|
291
|
+
* Soft-reask call outcome at the missing-ticket seam (#1171 F2-R6 / F2-R7).
|
|
292
|
+
* Ownership is decided here once from this nested chain's settlement evidence
|
|
293
|
+
* (soft-reask turn sealed → ownership cell; or returned terminal still carries volume);
|
|
294
|
+
* callers must not re-judge the same criterion.
|
|
295
|
+
*/
|
|
296
|
+
type SoftTicketReaskOutcome =
|
|
297
|
+
| { readonly kind: "none" }
|
|
298
|
+
| { readonly kind: "nested_owns"; readonly result: SeatRunResult }
|
|
299
|
+
| { readonly kind: "no_substitute"; readonly result: SeatRunResult };
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* #1171: sealed submission still leaves the leg unbound → soft reask once.
|
|
303
|
+
* Does not reject the sealed receipt; exhaustion leaves unbound as filed.
|
|
304
|
+
* Reask copy lives in package resources (ADR 0073).
|
|
305
|
+
* #1171 F2-R7: ownership comes from this nested resume's settlement seam
|
|
306
|
+
* (softTicketReaskOwnership.sealedSubstitute set when the soft-reask turn itself seals),
|
|
307
|
+
* not full-leg sealed count / time-window deltas that can absorb another court's seal.
|
|
308
|
+
*/
|
|
309
|
+
async function reaskMissingTicketOnce(
|
|
310
|
+
admitted: AdmittedRoleInvocation,
|
|
311
|
+
terminal: TerminalResult | undefined,
|
|
312
|
+
env: InstructionSeatRunEnv,
|
|
313
|
+
io: CliIo,
|
|
314
|
+
): Promise<SoftTicketReaskOutcome> {
|
|
315
|
+
if ((env.ticketReasksSpent ?? 0) >= 1) return { kind: "none" };
|
|
316
|
+
if (admitted.ticketNumber !== undefined) return { kind: "none" };
|
|
317
|
+
if (!isUnboundRunDirectory(admitted.runDirectory)) return { kind: "none" };
|
|
318
|
+
if (terminal?.roleOutcome.kind !== "accepted" && terminal?.roleOutcome.kind !== "audit_escalation") {
|
|
319
|
+
return { kind: "none" };
|
|
320
|
+
}
|
|
321
|
+
const payloads = terminal.roleOutcome.payloads ?? [];
|
|
322
|
+
if (payloads.length === 0) return { kind: "none" };
|
|
323
|
+
// Sealed-with-ticket skips via admitted.ticketNumber after bindSealedTicketNumber
|
|
324
|
+
// (post-admission). Do not re-parse payload.ticketNumber here — that would
|
|
325
|
+
// duplicate the existing ticket seam (#1171 notary: no rule copy).
|
|
326
|
+
const instruction = (await readPackageMaterial(MISSING_TICKET_REASK_MATERIAL)).trim();
|
|
327
|
+
// Spend budget and mark this turn as the soft reask (settlement identity).
|
|
328
|
+
// Ownership cell is set at that turn's settlement; env spreads keep the same
|
|
329
|
+
// ref through audit continue — see envForCourtContinue (#1171 F2-R5 / F2-R7).
|
|
330
|
+
const softTicketReaskOwnership = { sealedSubstitute: false };
|
|
331
|
+
const softReaskEnv: InstructionSeatRunEnv = {
|
|
332
|
+
...env,
|
|
333
|
+
ticketReasksSpent: 1,
|
|
334
|
+
softTicketReaskTurn: true,
|
|
335
|
+
softTicketReaskOwnership,
|
|
336
|
+
};
|
|
337
|
+
const result = await runPublicInstructionSeatResume(
|
|
338
|
+
{ runId: admitted.runId, summons: { instruction } },
|
|
339
|
+
softReaskEnv,
|
|
340
|
+
io,
|
|
341
|
+
);
|
|
342
|
+
if (
|
|
343
|
+
softTicketReaskOwnership.sealedSubstitute
|
|
344
|
+
|| terminalCarriesSealedSubmission(result.terminal)
|
|
345
|
+
) {
|
|
346
|
+
return { kind: "nested_owns", result };
|
|
347
|
+
}
|
|
348
|
+
return { kind: "no_substitute", result };
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/** Court continue: keep cross-turn budgets + ownership cell; drop turn-scoped soft-reask identity. */
|
|
352
|
+
function envForCourtContinue(env: InstructionSeatRunEnv): InstructionSeatRunEnv {
|
|
353
|
+
return { ...withoutSoftTicketReaskTurn(env), unreadableReasksSpent: 0 };
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/** True when a terminal still carries a sealed accepted / audit_escalation volume. */
|
|
357
|
+
function terminalCarriesSealedSubmission(terminal: TerminalResult | undefined): boolean {
|
|
358
|
+
const kind = terminal?.roleOutcome.kind;
|
|
359
|
+
if (kind !== "accepted" && kind !== "audit_escalation") return false;
|
|
360
|
+
return (terminal?.roleOutcome.payloads ?? []).length > 0;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* #1171 F2-R2: kept original terminal may still name pre-relocate artifact
|
|
365
|
+
* paths after a report-only reask moved the leg. Project onto live admitted
|
|
366
|
+
* via the same rewrite seam post-admission uses after relocate.
|
|
367
|
+
*/
|
|
368
|
+
function projectKeptOriginalArtifactsOntoLiveAdmitted(
|
|
369
|
+
original: SeatRunResult,
|
|
370
|
+
kept: SeatRunResult,
|
|
371
|
+
): void {
|
|
372
|
+
const oldRunDirectory = original.admitted?.runDirectory;
|
|
373
|
+
const newRunDirectory = kept.admitted?.runDirectory;
|
|
374
|
+
if (
|
|
375
|
+
oldRunDirectory === undefined
|
|
376
|
+
|| newRunDirectory === undefined
|
|
377
|
+
|| oldRunDirectory === newRunDirectory
|
|
378
|
+
|| kept.terminal === undefined
|
|
379
|
+
) {
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
for (const artifact of kept.terminal.artifacts) {
|
|
383
|
+
artifact.path = rewriteRunDirectoryPathValue(
|
|
384
|
+
artifact.path,
|
|
385
|
+
oldRunDirectory,
|
|
386
|
+
newRunDirectory,
|
|
387
|
+
) as string;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* #1171 F2: missing-ticket reask did not seal a substitute — keep original
|
|
393
|
+
* terminal; take live placement from the reask when it relocated.
|
|
394
|
+
* #1171 F2-R1: silence / report-only stays on the original chain; a real host
|
|
395
|
+
* failure (nonzero exit) must be delivered honestly — do not wash it into the
|
|
396
|
+
* original accepted result. Sealed original volume remains on disk either way.
|
|
397
|
+
* #1171 F2-R2: after keep, project report artifact paths onto the live
|
|
398
|
+
* admitted directory so public result, durable face, and artifact refs agree.
|
|
399
|
+
* Substitute ownership is decided once at the soft-reask call seam (F2-R6);
|
|
400
|
+
* this helper only finishes the keep path.
|
|
401
|
+
*/
|
|
402
|
+
function mergeMissingTicketReaskKeepOriginal(
|
|
403
|
+
original: SeatRunResult,
|
|
404
|
+
ticketReask: SeatRunResult,
|
|
405
|
+
): SeatRunResult {
|
|
406
|
+
if (ticketReask.exitCode !== 0) return ticketReask;
|
|
407
|
+
const admitted = ticketReask.admitted ?? original.admitted;
|
|
408
|
+
if (admitted === undefined) return { ...original };
|
|
409
|
+
const kept = { ...original, admitted };
|
|
410
|
+
projectKeptOriginalArtifactsOntoLiveAdmitted(original, kept);
|
|
411
|
+
return kept;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Post-submission status reask, then missing-ticket soft reask, then audit.
|
|
416
|
+
* #1171 F3: status exhaustion must not skip the one missing-ticket reask.
|
|
417
|
+
* #1171 F2: ticket reask without a substitute seal continues the original chain.
|
|
418
|
+
*/
|
|
419
|
+
async function continueAfterPostSubmissionGuards(
|
|
420
|
+
result: SeatRunResult,
|
|
421
|
+
admittedRole: PackagedRole,
|
|
422
|
+
env: InstructionSeatRunEnv,
|
|
423
|
+
io: CliIo,
|
|
424
|
+
held: readonly string[],
|
|
425
|
+
): Promise<SeatRunResult> {
|
|
426
|
+
const live = result.admitted;
|
|
427
|
+
if (live === undefined) {
|
|
428
|
+
await presentOriginalVolume(held, result.terminal, io);
|
|
429
|
+
return result;
|
|
430
|
+
}
|
|
431
|
+
const unreadableReask = await reaskUnreadablePostSubmissionStatus(
|
|
432
|
+
live,
|
|
433
|
+
result.terminal,
|
|
434
|
+
env,
|
|
435
|
+
io,
|
|
436
|
+
);
|
|
437
|
+
let afterStatus = result;
|
|
438
|
+
if (isUnreadableReaskExhausted(unreadableReask)) {
|
|
439
|
+
const terminal = result.terminal === undefined
|
|
440
|
+
? undefined
|
|
441
|
+
: withUnsettledDirection(result.terminal);
|
|
442
|
+
afterStatus = terminal === undefined
|
|
443
|
+
? { ...result, admitted: live }
|
|
444
|
+
: { ...result, admitted: live, terminal };
|
|
445
|
+
// Fall through: missing-ticket reask still owed on the original sealed volume.
|
|
446
|
+
} else if (unreadableReask !== undefined) {
|
|
447
|
+
return unreadableReask;
|
|
448
|
+
}
|
|
449
|
+
const forTicket = afterStatus.admitted ?? live;
|
|
450
|
+
// Ownership is decided once inside reaskMissingTicketOnce (#1171 F2-R6).
|
|
451
|
+
const softReask = await reaskMissingTicketOnce(
|
|
452
|
+
forTicket,
|
|
453
|
+
afterStatus.terminal ?? result.terminal,
|
|
454
|
+
env,
|
|
455
|
+
io,
|
|
456
|
+
);
|
|
457
|
+
if (softReask.kind === "nested_owns") {
|
|
458
|
+
// Nested resume already finished guards + audit/present for the substitute.
|
|
459
|
+
// Do not run a second outer settlement of the same public call (#1171 F2-R4b).
|
|
460
|
+
return softReask.result;
|
|
461
|
+
}
|
|
462
|
+
const continued = softReask.kind === "none"
|
|
463
|
+
? afterStatus
|
|
464
|
+
: mergeMissingTicketReaskKeepOriginal(afterStatus, softReask.result);
|
|
465
|
+
const continuedAdmitted = continued.admitted ?? live;
|
|
466
|
+
// Spent budget rides the outer audit/continue chain (#1171 F2-R4a). Soft-reask
|
|
467
|
+
// settlement identity does not — ordinary continue turns publish no_receipt (#1171 F2-R5).
|
|
468
|
+
const continuedEnv = softReask.kind !== "none"
|
|
469
|
+
? { ...env, ticketReasksSpent: 1 }
|
|
470
|
+
: env;
|
|
471
|
+
if (AUDITED_ROLES.has(admittedRole) && continued.terminal?.roleOutcome.kind === "accepted") {
|
|
472
|
+
return auditSubmittedRole(
|
|
473
|
+
{ ...continued, admitted: continuedAdmitted },
|
|
474
|
+
continuedEnv,
|
|
475
|
+
io,
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
await presentOriginalVolume(
|
|
479
|
+
held,
|
|
480
|
+
continued.terminal,
|
|
481
|
+
io,
|
|
482
|
+
continuedAdmitted.runDirectory,
|
|
483
|
+
);
|
|
484
|
+
return { ...continued, admitted: continuedAdmitted };
|
|
485
|
+
}
|
|
486
|
+
|
|
273
487
|
function roleRecord(role: PackagedRole) {
|
|
274
488
|
const record = packagedRoleMetadata(role);
|
|
275
489
|
if (record === undefined) {
|
|
@@ -523,25 +737,15 @@ async function dispatchAdmitted(
|
|
|
523
737
|
});
|
|
524
738
|
};
|
|
525
739
|
const result = await execute(env);
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
740
|
+
// Prefer the live admitted object returned from the turn (may have relocated).
|
|
741
|
+
const live = result.admitted ?? admitted;
|
|
742
|
+
return continueAfterPostSubmissionGuards(
|
|
743
|
+
{ ...result, admitted: live },
|
|
744
|
+
admitted.role,
|
|
529
745
|
env,
|
|
530
746
|
io,
|
|
747
|
+
held,
|
|
531
748
|
);
|
|
532
|
-
if (isUnreadableReaskExhausted(unreadableReask)) {
|
|
533
|
-
const terminal = result.terminal === undefined
|
|
534
|
-
? undefined
|
|
535
|
-
: withUnsettledDirection(result.terminal);
|
|
536
|
-
await presentOriginalVolume(held, terminal, io, admitted.runDirectory);
|
|
537
|
-
return terminal === undefined ? result : { ...result, terminal };
|
|
538
|
-
}
|
|
539
|
-
if (unreadableReask !== undefined) return unreadableReask;
|
|
540
|
-
if (AUDITED_ROLES.has(admitted.role) && result.terminal?.roleOutcome.kind === "accepted") {
|
|
541
|
-
return auditSubmittedRole(result, env, io);
|
|
542
|
-
}
|
|
543
|
-
await presentOriginalVolume(held, result.terminal, io, admitted.runDirectory);
|
|
544
|
-
return result;
|
|
545
749
|
}
|
|
546
750
|
|
|
547
751
|
/**
|
|
@@ -797,9 +1001,9 @@ export async function runPublicInstructionSeat(
|
|
|
797
1001
|
const result = await runCountersignBody(parsed, env, {
|
|
798
1002
|
stdout: (value) => held.push(value), stderr: io.stderr,
|
|
799
1003
|
});
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
return result;
|
|
1004
|
+
// Same post-submission guards as every other seat (#1171 F8): missing-ticket
|
|
1005
|
+
// soft reask once, then audit — no early return that skips the reask.
|
|
1006
|
+
return continueAfterPostSubmissionGuards(result, "countersign", env, io, held);
|
|
803
1007
|
}
|
|
804
1008
|
|
|
805
1009
|
if ("parallelLenses" in record && record.parallelLenses === true) {
|
|
@@ -1075,28 +1279,17 @@ export async function runPublicInstructionSeatResume(
|
|
|
1075
1279
|
},
|
|
1076
1280
|
...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
|
|
1077
1281
|
});
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
result.admitted,
|
|
1082
|
-
result.terminal,
|
|
1083
|
-
env,
|
|
1084
|
-
io,
|
|
1085
|
-
);
|
|
1086
|
-
if (isUnreadableReaskExhausted(unreadableReask)) {
|
|
1087
|
-
const terminal = result.terminal === undefined
|
|
1088
|
-
? undefined
|
|
1089
|
-
: withUnsettledDirection(result.terminal);
|
|
1090
|
-
await presentOriginalVolume(held, terminal, io, result.admitted?.runDirectory);
|
|
1091
|
-
return terminal === undefined ? result : { ...result, terminal };
|
|
1092
|
-
}
|
|
1093
|
-
if (unreadableReask !== undefined) return unreadableReask;
|
|
1094
|
-
if (result.admitted !== undefined && AUDITED_ROLES.has(result.admitted.role)
|
|
1095
|
-
&& result.terminal?.roleOutcome.kind === "accepted") {
|
|
1096
|
-
return auditSubmittedRole(result, env, io);
|
|
1282
|
+
if (result.admitted === undefined) {
|
|
1283
|
+
await presentOriginalVolume(held, result.terminal, io);
|
|
1284
|
+
return result;
|
|
1097
1285
|
}
|
|
1098
|
-
|
|
1099
|
-
|
|
1286
|
+
return continueAfterPostSubmissionGuards(
|
|
1287
|
+
result,
|
|
1288
|
+
result.admitted.role,
|
|
1289
|
+
env,
|
|
1290
|
+
io,
|
|
1291
|
+
held,
|
|
1292
|
+
);
|
|
1100
1293
|
};
|
|
1101
1294
|
let binding: Awaited<ReturnType<typeof readAuditorResumeBinding>>;
|
|
1102
1295
|
try {
|
|
@@ -1151,7 +1344,7 @@ async function queueConclusionFromChild(
|
|
|
1151
1344
|
// Notary, auditor, and inspector have no seat-local status reask. This loop
|
|
1152
1345
|
// is their one budget. The child resume keeps the caller's env so delivery,
|
|
1153
1346
|
// failure recovery, and other reask loops stay separate.
|
|
1154
|
-
let budgetEnv: InstructionSeatRunEnv =
|
|
1347
|
+
let budgetEnv: InstructionSeatRunEnv = envForCourtContinue(env);
|
|
1155
1348
|
for (;;) {
|
|
1156
1349
|
const terminal = await trySettlePublicSeat(
|
|
1157
1350
|
current,
|
|
@@ -1234,7 +1427,7 @@ export async function continueParentAfterChild(
|
|
|
1234
1427
|
return runPublicInstructionSeatResume({
|
|
1235
1428
|
runId: parentRunId,
|
|
1236
1429
|
summons: { instruction: readableGateItem(latestQueuePayload(resolved.terminal)) },
|
|
1237
|
-
},
|
|
1430
|
+
}, envForCourtContinue(env), io);
|
|
1238
1431
|
}
|
|
1239
1432
|
const result = await dispatchAdmitted(admitted, env, { ...io, omitFailureStderrDiagnostic: true });
|
|
1240
1433
|
showResumeErrorPointer(io, result.exitCode, result.terminal);
|
|
@@ -1285,6 +1478,8 @@ async function auditSubmittedRole(
|
|
|
1285
1478
|
resumedOfficer?: AdmittedRoleInvocation,
|
|
1286
1479
|
passedReceipt?: unknown,
|
|
1287
1480
|
): Promise<SeatRunResult> {
|
|
1481
|
+
// Ordinary audit chain: drop soft-reask turn identity; keep budgets/ownership (#1171 F2-R9).
|
|
1482
|
+
env = withoutSoftTicketReaskTurn(env);
|
|
1288
1483
|
const admitted = turn.admitted;
|
|
1289
1484
|
if (admitted === undefined || !AUDITED_ROLES.has(admitted.role)
|
|
1290
1485
|
|| turn.terminal?.roleOutcome.kind !== "accepted") return turn;
|
|
@@ -1329,10 +1524,13 @@ async function auditSubmittedRole(
|
|
|
1329
1524
|
await presentTerminal(turn.terminal, { ...io, omitFailureStderrDiagnostic: true }, admitted.runDirectory);
|
|
1330
1525
|
return turn;
|
|
1331
1526
|
}
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1527
|
+
// #1171 F2-R8: bind audit to the sealed identity carried on this turn's
|
|
1528
|
+
// settlement terminal — never the whole-leg latest accepted row (another
|
|
1529
|
+
// lawful public call may have sealed and passed台院 meanwhile).
|
|
1530
|
+
const toolCallId = turn.terminal.submissionToolCallId;
|
|
1531
|
+
if (toolCallId === undefined || toolCallId.length === 0) {
|
|
1532
|
+
throw new Error("accepted submission has no tool call identity");
|
|
1533
|
+
}
|
|
1336
1534
|
const sessionFile = env.principalAuthority.decode(admitted.principal).sessionFile;
|
|
1337
1535
|
const entries = admitted.role === "doctor"
|
|
1338
1536
|
? await readBoundSessionEntries(sessionFile)
|
|
@@ -1393,7 +1591,7 @@ async function auditSubmittedRole(
|
|
|
1393
1591
|
if (decision.status === "continue") {
|
|
1394
1592
|
return runPublicInstructionSeatResume({
|
|
1395
1593
|
runId: admitted.runId, summons: { instruction: readableGateItem(decision.receipt ?? decision.violations) },
|
|
1396
|
-
},
|
|
1594
|
+
}, envForCourtContinue(env), io);
|
|
1397
1595
|
}
|
|
1398
1596
|
if (decision.status === "received") {
|
|
1399
1597
|
if (lastSummon?.terminal === undefined) {
|
|
@@ -1495,7 +1693,7 @@ async function auditSubmittedRole(
|
|
|
1495
1693
|
return runPublicInstructionSeatResume({
|
|
1496
1694
|
runId: admitted.runId,
|
|
1497
1695
|
summons: { instruction: readableGateItem(chain.passes.at(-1)?.receipt) },
|
|
1498
|
-
},
|
|
1696
|
+
}, envForCourtContinue(env), io);
|
|
1499
1697
|
}
|
|
1500
1698
|
if (admitted.role === "secretariat") {
|
|
1501
1699
|
const pass = chain.passes.at(-1);
|
|
@@ -52,7 +52,7 @@ import {
|
|
|
52
52
|
requireSafePositiveTicketNumber,
|
|
53
53
|
} from "../run-ticket-number.ts";
|
|
54
54
|
import {
|
|
55
|
-
|
|
55
|
+
rewriteAdmittedRoleRunPage,
|
|
56
56
|
} from "../role-run-relocation.ts";
|
|
57
57
|
import { readPageSync, renderCurrentSync, updateSectionSync, writeSectionSync } from "../run-dossier.ts";
|
|
58
58
|
import {
|
|
@@ -99,12 +99,12 @@ import type { PublicThinkingLevel } from "./registry.ts";
|
|
|
99
99
|
import { errorText, isRecord } from "../unknown-value.ts";
|
|
100
100
|
|
|
101
101
|
/** Caller-supplied attachment path recorded as-is (#1165). */
|
|
102
|
-
export type
|
|
102
|
+
export type AdmittedAttachment = {
|
|
103
103
|
readonly path: string;
|
|
104
104
|
};
|
|
105
105
|
|
|
106
106
|
/** Normalize admitted attachment records (caller path only; #1165). */
|
|
107
|
-
export function normalizeAdmittedAttachment(raw: unknown):
|
|
107
|
+
export function normalizeAdmittedAttachment(raw: unknown): AdmittedAttachment | undefined {
|
|
108
108
|
if (typeof raw !== "object" || raw === null) return undefined;
|
|
109
109
|
const record = raw as Record<string, unknown>;
|
|
110
110
|
const path = record.path;
|
|
@@ -115,7 +115,7 @@ export function normalizeAdmittedAttachment(raw: unknown): FrozenAttachment | un
|
|
|
115
115
|
|
|
116
116
|
function admitCallerAttachments(
|
|
117
117
|
attachmentPaths: readonly string[],
|
|
118
|
-
): readonly
|
|
118
|
+
): readonly AdmittedAttachment[] {
|
|
119
119
|
return attachmentPaths.map((path) => ({ path }));
|
|
120
120
|
}
|
|
121
121
|
|
|
@@ -128,7 +128,7 @@ export type AdmittedRoleInvocationBase = {
|
|
|
128
128
|
readonly instruction: string;
|
|
129
129
|
/** True when the caller supplied a literally empty instruction (length 0). */
|
|
130
130
|
readonly instructionEmpty: boolean;
|
|
131
|
-
readonly attachments: readonly
|
|
131
|
+
readonly attachments: readonly AdmittedAttachment[];
|
|
132
132
|
readonly runDirectory: string;
|
|
133
133
|
/** Host-issued opaque durable principal (coordinates only via authority.decode). */
|
|
134
134
|
readonly principal: DurablePrincipal;
|
|
@@ -307,7 +307,6 @@ export type AdmissionPlacement = {
|
|
|
307
307
|
readonly sessionDirectory: string;
|
|
308
308
|
readonly sessionFile: string;
|
|
309
309
|
readonly runDirectory: string;
|
|
310
|
-
readonly attachmentsDirectory: string;
|
|
311
310
|
readonly ledgerHome: string;
|
|
312
311
|
readonly bookKey: string;
|
|
313
312
|
};
|
|
@@ -608,18 +607,10 @@ export async function relocateAdmittedRunToTicket(
|
|
|
608
607
|
// render that may refuse (#1161 L1 / BASE order).
|
|
609
608
|
heldLease?.relocate(target.runDirectory);
|
|
610
609
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
[
|
|
615
|
-
"runDirectory",
|
|
616
|
-
"mergerInputPath",
|
|
617
|
-
],
|
|
618
|
-
oldRunDirectory,
|
|
619
|
-
target.runDirectory,
|
|
610
|
+
rewriteAdmittedRoleRunPage(
|
|
611
|
+
admitted as unknown as Record<string, unknown>,
|
|
612
|
+
[{ oldRunDirectory, newRunDirectory: target.runDirectory }],
|
|
620
613
|
);
|
|
621
|
-
// #1165/#1168: caller file-flag paths stay as given. Obsolete copy fields
|
|
622
|
-
// (taskPath/packetPath) are not rewritten — stock volumes keep their bytes.
|
|
623
614
|
(admitted as { principal: DurablePrincipal }).principal = principal;
|
|
624
615
|
|
|
625
616
|
// current.json projects host.original from this run directory. Ownership is
|
|
@@ -1610,8 +1601,7 @@ type PlacedRoleAdmission = {
|
|
|
1610
1601
|
readonly principal: DurablePrincipal;
|
|
1611
1602
|
readonly sessionDirectory: string;
|
|
1612
1603
|
readonly sessionFile: string;
|
|
1613
|
-
readonly attachments: readonly
|
|
1614
|
-
readonly attachmentsDirectory: string;
|
|
1604
|
+
readonly attachments: readonly AdmittedAttachment[];
|
|
1615
1605
|
readonly ledgerHome: string;
|
|
1616
1606
|
readonly ticketFields: ReturnType<typeof ticketAdmissionFields>;
|
|
1617
1607
|
};
|
|
@@ -1644,7 +1634,6 @@ async function placeRoleAdmission(options: {
|
|
|
1644
1634
|
sessionDirectory,
|
|
1645
1635
|
sessionFile,
|
|
1646
1636
|
runDirectory,
|
|
1647
|
-
attachmentsDirectory,
|
|
1648
1637
|
ledgerHome,
|
|
1649
1638
|
bookKey,
|
|
1650
1639
|
} = issueAdmissionPlacement(options.principalAuthority, {
|
|
@@ -1667,14 +1656,13 @@ async function placeRoleAdmission(options: {
|
|
|
1667
1656
|
sessionDirectory,
|
|
1668
1657
|
sessionFile,
|
|
1669
1658
|
attachments,
|
|
1670
|
-
attachmentsDirectory,
|
|
1671
1659
|
ledgerHome,
|
|
1672
1660
|
ticketFields,
|
|
1673
1661
|
};
|
|
1674
1662
|
}
|
|
1675
1663
|
|
|
1676
1664
|
function persistedAttachmentRefs(
|
|
1677
|
-
attachments: readonly
|
|
1665
|
+
attachments: readonly AdmittedAttachment[],
|
|
1678
1666
|
): ReadonlyArray<{ path: string }> {
|
|
1679
1667
|
return attachments.map((attachment) => ({ path: attachment.path }));
|
|
1680
1668
|
}
|
|
@@ -1712,8 +1700,8 @@ async function persistPlacedAdmission(
|
|
|
1712
1700
|
}
|
|
1713
1701
|
|
|
1714
1702
|
/**
|
|
1715
|
-
* Shared admission: project check, placement,
|
|
1716
|
-
* admitted
|
|
1703
|
+
* Shared admission: project check, placement, admitted and invocation section write.
|
|
1704
|
+
* Caller file-flag paths are recorded opaque on the admitted page (ADR 0087).
|
|
1717
1705
|
* Countersign passes deferPersistence so same-ticket lookup can reserve coordinates
|
|
1718
1706
|
* before materializeCountersignInvocation writes the page.
|
|
1719
1707
|
* Seats whose extra facts are known before placement pass them as admittedFields.
|
|
@@ -1937,7 +1925,7 @@ export async function materializeCountersignInvocation(
|
|
|
1937
1925
|
if (ticketFields.ticketNumber !== undefined) {
|
|
1938
1926
|
(admitted as { ticketNumber?: number }).ticketNumber = ticketFields.ticketNumber;
|
|
1939
1927
|
}
|
|
1940
|
-
(admitted as { attachments: readonly
|
|
1928
|
+
(admitted as { attachments: readonly AdmittedAttachment[] }).attachments = placement.attachments;
|
|
1941
1929
|
writeAdmittedRequestPersistence(admitted.runDirectory, admitted, {
|
|
1942
1930
|
sessionDirectory: placement.sessionDirectory,
|
|
1943
1931
|
sessionFile: placement.sessionFile,
|
|
@@ -1955,7 +1943,7 @@ export async function loadAdmittedJudgeRequest(
|
|
|
1955
1943
|
): Promise<{
|
|
1956
1944
|
instruction: string;
|
|
1957
1945
|
instructionEmpty: boolean;
|
|
1958
|
-
attachments: readonly
|
|
1946
|
+
attachments: readonly AdmittedAttachment[];
|
|
1959
1947
|
} | undefined> {
|
|
1960
1948
|
try {
|
|
1961
1949
|
const record = readPageSync(runDirectory, "admitted");
|
|
@@ -1966,7 +1954,7 @@ export async function loadAdmittedJudgeRequest(
|
|
|
1966
1954
|
if (!Array.isArray(record.attachments)) return undefined;
|
|
1967
1955
|
const attachments = record.attachments
|
|
1968
1956
|
.map((item) => normalizeAdmittedAttachment(item))
|
|
1969
|
-
.filter((item): item is
|
|
1957
|
+
.filter((item): item is AdmittedAttachment => item !== undefined);
|
|
1970
1958
|
return {
|
|
1971
1959
|
instruction: record.instruction,
|
|
1972
1960
|
instructionEmpty: record.instructionEmpty,
|