@akagilnc/pi-workflow-roles 0.1.5483 → 0.1.5571
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/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/acp-host/production-host.js +6312 -6376
- package/dist/analyst-gate-cycles-read.js +28 -78
- package/dist/analyst-ledger.js +26 -42
- package/dist/analyst-metric-families/acceptance-success-rework.js +25 -1
- package/dist/analyst-metric-families/round-timeline.js +2 -5
- package/dist/archivist-record-entry.js +4 -3
- package/dist/archivist-record-pointer.js +71 -36
- package/dist/atomic-write.js +2 -2
- package/dist/auditor-dossier-tool.js +11 -6
- package/dist/auditor-soul.js +5 -12
- package/dist/collector-config.js +6 -5
- package/dist/doctor-evidence.js +8 -4
- package/dist/engine-detour-usage.js +8 -22
- package/dist/headless-host/production-host.js +5969 -6011
- package/dist/host-descriptions.js +0 -4
- package/dist/host-session-record.js +266 -267
- package/dist/ledger-session-read.js +5 -0
- package/dist/migrate-book-topology.js +1369 -722
- package/dist/navigator-public-session.js +13 -17
- package/dist/notary-source-run.js +8 -16
- package/dist/packaged-role-registry.js +5 -56
- package/dist/pi/durable-principal.js +2 -3
- package/dist/pi/role-turn-host.js +79 -15
- package/dist/public-cli/auto-resume.js +8 -47
- package/dist/public-cli/instruction-seat-run.js +9 -7
- package/dist/public-cli/invocation.js +67 -100
- package/dist/public-cli/main.js +2689 -2532
- package/dist/public-cli/post-admission.js +96 -55
- package/dist/public-cli/run-lifecycle.js +44 -100
- package/dist/public-cli/settlement.js +184 -429
- package/dist/public-cli/terminal.js +0 -12
- package/dist/role-run-placement.js +53 -16
- package/dist/role-run-relocation.js +111 -52
- package/dist/run-dossier-files.js +30 -0
- package/dist/run-dossier.js +322 -0
- package/dist/run-terminal-artifacts.js +69 -202
- package/dist/run-ticket-number.js +49 -7
- package/dist/session-assistant-usage.js +9 -18
- package/dist/session-identity.js +49 -19
- package/dist/sitian-appender.js +29 -18
- package/dist/sitian-facade.js +11 -0
- package/dist/sitian-reader.js +44 -29
- package/dist/submission-gate.js +1 -1
- package/dist/submission-ledger.js +4 -4
- package/dist/ticket-provenance-contracts.js +2 -8
- package/dist/ticket-provenance.js +25 -9
- package/package.json +1 -1
- package/scripts/build-package.mjs +2 -0
- package/src/acp-host/production-host.ts +1 -1
- package/src/acp-host/role-turn-host.ts +11 -4
- package/src/analyst-gate-cycles-read.ts +31 -88
- package/src/analyst-ledger.ts +36 -51
- package/src/analyst-metric-families/acceptance-success-rework.ts +24 -1
- package/src/analyst-metric-families/gate-cycles.ts +1 -1
- package/src/analyst-metric-families/round-timeline.ts +2 -5
- package/src/analyst-page.ts +3 -3
- package/src/archivist-record-entry.ts +6 -5
- package/src/archivist-record-pointer.ts +92 -42
- package/src/atomic-write.ts +3 -1
- package/src/auditor-dossier-tool.ts +14 -8
- package/src/auditor-soul.ts +5 -10
- package/src/book-topology-migration-placement.ts +16 -3
- package/src/book-topology-record-class-migrators.ts +4 -2
- package/src/book-topology-runs-migrator.ts +6 -3
- package/src/collector-config.ts +5 -3
- package/src/doctor-contracts.ts +1 -1
- package/src/doctor-evidence.ts +7 -3
- package/src/engine-detour-tool.ts +1 -1
- package/src/engine-detour-usage.ts +12 -25
- package/src/external-host-turn-loop.ts +25 -0
- package/src/factory-board.ts +16 -81
- package/src/headless-host/production-host.ts +1 -4
- package/src/headless-host/role-turn-host.ts +56 -21
- package/src/host-descriptions.ts +0 -6
- package/src/host-session-record.ts +224 -140
- package/src/ledger-session-read.ts +6 -0
- package/src/navigator-public-session.ts +13 -18
- package/src/navigator-work-context.ts +3 -1
- package/src/notary-role.ts +2 -1
- package/src/notary-source-run.ts +8 -18
- package/src/packaged-role-registry.ts +1 -65
- package/src/pi/adapter.ts +50 -7
- package/src/pi/durable-principal.ts +2 -4
- package/src/pi/role-turn-host.ts +85 -14
- package/src/public-cli/auto-resume.ts +7 -43
- package/src/public-cli/instruction-seat-run.ts +13 -6
- package/src/public-cli/invocation.ts +90 -144
- package/src/public-cli/post-admission.ts +111 -72
- package/src/public-cli/run-lifecycle.ts +47 -115
- package/src/public-cli/settlement.ts +221 -589
- package/src/public-cli/terminal.ts +1 -61
- package/src/role-envelope.ts +4 -2
- package/src/role-run-placement.ts +65 -17
- package/src/role-run-relocation.ts +110 -70
- package/src/role-runtime.ts +46 -69
- package/src/run-dossier-files.ts +33 -0
- package/src/run-dossier.ts +357 -0
- package/src/run-terminal-artifacts.ts +84 -240
- package/src/run-ticket-number.ts +82 -14
- package/src/session-assistant-usage.ts +8 -20
- package/src/session-identity.ts +52 -21
- package/src/sitian-appender.ts +33 -26
- package/src/sitian-facade.ts +22 -0
- package/src/sitian-reader.ts +62 -32
- package/src/submission-gate.ts +1 -1
- package/src/submission-ledger.ts +4 -4
- package/src/ticket-provenance-contracts.ts +3 -6
- package/src/ticket-provenance.ts +23 -11
- package/src/ticket-snapshot.ts +3 -2
- package/src/ticket-trajectory.ts +95 -109
|
@@ -27,9 +27,9 @@ import { resolveBookKeyFromGit } from "../activation-ledger-git.ts";
|
|
|
27
27
|
import {
|
|
28
28
|
ensureRoleRunDirectory,
|
|
29
29
|
ensureRoleRunPlacement,
|
|
30
|
+
formatRunLeaf,
|
|
30
31
|
isUnboundRunDirectory,
|
|
31
32
|
listBookRunDirectories,
|
|
32
|
-
roleRunArtifactsDirectory,
|
|
33
33
|
roleRunPlacement,
|
|
34
34
|
type RoleRunSubject,
|
|
35
35
|
} from "../role-run-placement.ts";
|
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
} from "../packaged-role-registry.ts";
|
|
52
52
|
import {
|
|
53
53
|
isSafePositiveTicketNumber,
|
|
54
|
+
parseTicketNumber,
|
|
54
55
|
readBoardTicketNumber,
|
|
55
56
|
requireSafePositiveTicketNumber,
|
|
56
57
|
} from "../run-ticket-number.ts";
|
|
@@ -58,6 +59,7 @@ import {
|
|
|
58
59
|
rewriteRunDirectoryPathFields,
|
|
59
60
|
rewriteRunDirectoryPathValue,
|
|
60
61
|
} from "../role-run-relocation.ts";
|
|
62
|
+
import { readPageSync, renderCurrentSync, updateSectionSync, writeSectionSync } from "../run-dossier.ts";
|
|
61
63
|
import {
|
|
62
64
|
loadDoctorCase,
|
|
63
65
|
} from "../doctor-evidence.ts";
|
|
@@ -135,7 +137,6 @@ export type AdmittedRoleInvocationBase = {
|
|
|
135
137
|
readonly runDirectory: string;
|
|
136
138
|
/** Host-issued opaque durable principal (coordinates only via authority.decode). */
|
|
137
139
|
readonly principal: DurablePrincipal;
|
|
138
|
-
readonly admittedRequestPath: string;
|
|
139
140
|
/**
|
|
140
141
|
* Optional opaque invocation correlation restored from a prior admitted page
|
|
141
142
|
* (ADR 0049 host channel). Admission does not mint ticket-binding ids.
|
|
@@ -316,7 +317,6 @@ export type AdmissionPlacement = {
|
|
|
316
317
|
readonly sessionDirectory: string;
|
|
317
318
|
readonly sessionFile: string;
|
|
318
319
|
readonly runDirectory: string;
|
|
319
|
-
readonly artifactsDirectory: string;
|
|
320
320
|
readonly attachmentsDirectory: string;
|
|
321
321
|
readonly ledgerHome: string;
|
|
322
322
|
readonly bookKey: string;
|
|
@@ -354,25 +354,20 @@ export function issueAdmissionPlacement(
|
|
|
354
354
|
}
|
|
355
355
|
|
|
356
356
|
/**
|
|
357
|
-
* Unique admitted
|
|
357
|
+
* Unique `admitted` section projection of current.json: top-level sessionDirectory/sessionFile
|
|
358
358
|
* (base wire shape). Memory Admitted keeps only the opaque principal — never dual-carry.
|
|
359
359
|
*/
|
|
360
|
-
|
|
361
|
-
|
|
360
|
+
function writeAdmittedRequestPersistence(
|
|
361
|
+
runDirectory: string,
|
|
362
362
|
body: Record<string, unknown>,
|
|
363
363
|
coordinates: { readonly sessionDirectory: string; readonly sessionFile: string },
|
|
364
|
-
):
|
|
364
|
+
): void {
|
|
365
365
|
const { principal: _omitPrincipal, ...rest } = body;
|
|
366
|
-
|
|
366
|
+
writeSectionSync(runDirectory, "admitted", {
|
|
367
367
|
...rest,
|
|
368
368
|
sessionDirectory: coordinates.sessionDirectory,
|
|
369
369
|
sessionFile: coordinates.sessionFile,
|
|
370
|
-
};
|
|
371
|
-
await writeFile(
|
|
372
|
-
admittedRequestPath,
|
|
373
|
-
`${JSON.stringify(projection, null, 2)}\n`,
|
|
374
|
-
"utf8",
|
|
375
|
-
);
|
|
370
|
+
});
|
|
376
371
|
}
|
|
377
372
|
|
|
378
373
|
/**
|
|
@@ -401,7 +396,7 @@ function effectiveModelLedgerFields(
|
|
|
401
396
|
export { homeFromRunDirectory };
|
|
402
397
|
|
|
403
398
|
/**
|
|
404
|
-
* Persist
|
|
399
|
+
* Persist the `invocation` identity section of current.json for the public run.
|
|
405
400
|
* Admission is the sole source for every field; this is the only identity
|
|
406
401
|
* projection and callers never provide an independent ledger shape.
|
|
407
402
|
* When an effective model is known at admission, provider/model (and thinking
|
|
@@ -424,11 +419,7 @@ async function writeRoleInvocationLedger(
|
|
|
424
419
|
...(source.ticketNumber === undefined ? {} : { ticketNumber: source.ticketNumber }),
|
|
425
420
|
...effectiveModelLedgerFields(effectiveModel),
|
|
426
421
|
};
|
|
427
|
-
|
|
428
|
-
join(source.runDirectory, "invocation.json"),
|
|
429
|
-
`${JSON.stringify(identity, null, 2)}\n`,
|
|
430
|
-
"utf8",
|
|
431
|
-
);
|
|
422
|
+
writeSectionSync(source.runDirectory, "invocation", identity);
|
|
432
423
|
}
|
|
433
424
|
|
|
434
425
|
/**
|
|
@@ -448,7 +439,7 @@ export async function recordEffectiveInvocationModel(
|
|
|
448
439
|
host?: string,
|
|
449
440
|
engineModel?: string | null,
|
|
450
441
|
): Promise<void> {
|
|
451
|
-
|
|
442
|
+
updateSectionSync(runDirectory, "invocation", (current) => {
|
|
452
443
|
const next: Record<string, unknown> = { ...current };
|
|
453
444
|
if (model !== undefined) {
|
|
454
445
|
next.provider = model.provider;
|
|
@@ -476,33 +467,19 @@ export async function recordEffectiveInvocationModel(
|
|
|
476
467
|
});
|
|
477
468
|
}
|
|
478
469
|
|
|
479
|
-
/**
|
|
480
|
-
* Read one durable JSON object, let the caller keep its field rules, and write
|
|
481
|
-
* the page back. `undefined` means the caller decided not to change the page.
|
|
482
|
-
*/
|
|
483
|
-
async function updateDurableJsonPage(
|
|
484
|
-
path: string,
|
|
485
|
-
update: (current: Record<string, unknown>) => Record<string, unknown> | undefined,
|
|
486
|
-
): Promise<void> {
|
|
487
|
-
const current = JSON.parse(await readFile(path, "utf8")) as Record<string, unknown>;
|
|
488
|
-
const next = update(current);
|
|
489
|
-
if (next === undefined) return;
|
|
490
|
-
await writeFile(path, `${JSON.stringify(next, null, 2)}\n`, "utf8");
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
/** Merge observed launch-time fields into the single existing invocation.json identity page. */
|
|
470
|
+
/** Merge observed launch-time fields into the single existing invocation section of current.json. */
|
|
494
471
|
async function mergeInvocationIdentityPage(
|
|
495
472
|
runDirectory: string,
|
|
496
473
|
fields: Record<string, unknown>,
|
|
497
474
|
): Promise<void> {
|
|
498
|
-
|
|
475
|
+
updateSectionSync(runDirectory, "invocation", (current) => ({
|
|
499
476
|
...current,
|
|
500
477
|
...fields,
|
|
501
478
|
}));
|
|
502
479
|
}
|
|
503
480
|
|
|
504
481
|
/**
|
|
505
|
-
* Persist parent --source-run path onto admitted
|
|
482
|
+
* Persist parent --source-run path onto the admitted section for officer resume lookup (#747).
|
|
506
483
|
* Reuses the existing notary sourceRunPath key; does not invent a new field name.
|
|
507
484
|
*/
|
|
508
485
|
export async function persistAdmittedSourceRunPath(
|
|
@@ -513,7 +490,7 @@ export async function persistAdmittedSourceRunPath(
|
|
|
513
490
|
if (sourceRunPath.trim() === "") {
|
|
514
491
|
throw new Error("persistAdmittedSourceRunPath requires a non-empty sourceRunPath");
|
|
515
492
|
}
|
|
516
|
-
|
|
493
|
+
updateSectionSync(admitted.runDirectory, "admitted", (current) => {
|
|
517
494
|
if (typeof current.sourceRunPath === "string" && current.sourceRunPath !== sourceRunPath) {
|
|
518
495
|
throw new Error(
|
|
519
496
|
`persistAdmittedSourceRunPath refuses to replace ${current.sourceRunPath} with ${sourceRunPath}`,
|
|
@@ -531,7 +508,7 @@ export async function persistAdmittedSourceRunPath(
|
|
|
531
508
|
|
|
532
509
|
/**
|
|
533
510
|
* Bind a post-admission resolved ticketNumber onto the in-memory admitted
|
|
534
|
-
* object and both durable
|
|
511
|
+
* object and both durable sections (invocation + admitted).
|
|
535
512
|
* A later typed role receipt replaces an earlier assertion (#1025).
|
|
536
513
|
*/
|
|
537
514
|
export async function bindAdmittedTicketNumber(
|
|
@@ -549,7 +526,7 @@ export async function recordAdmittedCorrelation(
|
|
|
549
526
|
correlationId: string,
|
|
550
527
|
): Promise<void> {
|
|
551
528
|
let correlationIds: string[] = [];
|
|
552
|
-
|
|
529
|
+
updateSectionSync(admitted.runDirectory, "admitted", (current) => {
|
|
553
530
|
const prior = [
|
|
554
531
|
...(Array.isArray(current.correlationIds)
|
|
555
532
|
? current.correlationIds.filter((value): value is string =>
|
|
@@ -596,11 +573,10 @@ export async function relocateAdmittedRunToTicket(
|
|
|
596
573
|
const principal = authority.seal(target);
|
|
597
574
|
|
|
598
575
|
if (admitted.role !== "diarist") {
|
|
599
|
-
const
|
|
600
|
-
const childRunIds = parentPage.childDiaristRunIds;
|
|
576
|
+
const childRunIds = readPageSync(oldRunDirectory, "admitted")?.childDiaristRunIds;
|
|
601
577
|
for (const childRunId of Array.isArray(childRunIds) ? childRunIds : []) {
|
|
602
578
|
if (typeof childRunId !== "string") continue;
|
|
603
|
-
const childDirectory = join(dirname(oldRunDirectory),
|
|
579
|
+
const childDirectory = join(dirname(oldRunDirectory), formatRunLeaf(childRunId, "diarist"));
|
|
604
580
|
// A child that already filed under a ticket keeps its own assertion.
|
|
605
581
|
if (!existsSync(childDirectory)) continue;
|
|
606
582
|
const childTarget = roleRunPlacement(ledgerHome, {
|
|
@@ -614,6 +590,16 @@ export async function relocateAdmittedRunToTicket(
|
|
|
614
590
|
await rehomeUnboundTicketProvenance(childDirectory, admitted.ticketNumber, admitted.projectRoot, homeFromRunDirectory(oldRunDirectory));
|
|
615
591
|
ensureRoleRunDirectory(ledgerHome, dirname(childTarget.runDirectory));
|
|
616
592
|
await rename(childDirectory, childTarget.runDirectory);
|
|
593
|
+
// Finished child legs will not settle again; refresh derived host.original
|
|
594
|
+
// after the rename commit. A derived render fault must not undo or block
|
|
595
|
+
// the committed placement (#1161 L1) — note and keep the true cause.
|
|
596
|
+
try {
|
|
597
|
+
renderCurrentSync(childTarget.runDirectory);
|
|
598
|
+
} catch (error) {
|
|
599
|
+
process.stderr.write(
|
|
600
|
+
`[invocation] current.json render refused after child relocate; placement kept: ${childTarget.runDirectory}: ${errorText(error)}\n`,
|
|
601
|
+
);
|
|
602
|
+
}
|
|
617
603
|
}
|
|
618
604
|
}
|
|
619
605
|
|
|
@@ -627,8 +613,9 @@ export async function relocateAdmittedRunToTicket(
|
|
|
627
613
|
// Persisted paths are resolved from typed run identity on read.
|
|
628
614
|
await rename(oldRunDirectory, target.runDirectory);
|
|
629
615
|
|
|
630
|
-
// rename moved the open lock inode with the directory. Transfer cleanup
|
|
631
|
-
// ownership immediately after the commit
|
|
616
|
+
// rename moved the open lock inode with the directory. Transfer cleanup and
|
|
617
|
+
// admitted path ownership immediately after the commit — before any derived
|
|
618
|
+
// render that may refuse (#1161 L1 / BASE order).
|
|
632
619
|
heldLease?.relocate(target.runDirectory);
|
|
633
620
|
|
|
634
621
|
const admittedRecord = admitted as unknown as Record<string, unknown>;
|
|
@@ -636,7 +623,6 @@ export async function relocateAdmittedRunToTicket(
|
|
|
636
623
|
admittedRecord,
|
|
637
624
|
[
|
|
638
625
|
"runDirectory",
|
|
639
|
-
"admittedRequestPath",
|
|
640
626
|
"taskPath",
|
|
641
627
|
"packetPath",
|
|
642
628
|
"prerequisitesPath",
|
|
@@ -655,6 +641,11 @@ export async function relocateAdmittedRunToTicket(
|
|
|
655
641
|
}
|
|
656
642
|
(admitted as { principal: DurablePrincipal }).principal = principal;
|
|
657
643
|
|
|
644
|
+
// current.json projects host.original from this run directory. Ownership is
|
|
645
|
+
// already transferred; a refuse still carries the true cause to the caller
|
|
646
|
+
// (#1161 L1) without leaving the leg under the unbound path.
|
|
647
|
+
renderCurrentSync(target.runDirectory);
|
|
648
|
+
|
|
658
649
|
return { oldRunDirectory, newRunDirectory: target.runDirectory };
|
|
659
650
|
}
|
|
660
651
|
|
|
@@ -663,7 +654,7 @@ export async function recordChildDiaristRun(
|
|
|
663
654
|
parent: AdmittedRoleInvocation,
|
|
664
655
|
childRunId: string,
|
|
665
656
|
): Promise<void> {
|
|
666
|
-
|
|
657
|
+
updateSectionSync(parent.runDirectory, "admitted", (page) => {
|
|
667
658
|
const existing = Array.isArray(page.childDiaristRunIds)
|
|
668
659
|
? page.childDiaristRunIds.filter((runId): runId is string => typeof runId === "string")
|
|
669
660
|
: [];
|
|
@@ -685,9 +676,8 @@ export async function bindTicketNumberOnRunDirectory(
|
|
|
685
676
|
ticketNumber,
|
|
686
677
|
"bindTicketNumberOnRunDirectory",
|
|
687
678
|
);
|
|
688
|
-
const admittedPath = join(runDirectory, "admitted-request.json");
|
|
689
679
|
let unchanged = false;
|
|
690
|
-
|
|
680
|
+
updateSectionSync(runDirectory, "admitted", (admitted) => {
|
|
691
681
|
if (admitted.ticketNumber === ticketNumber) {
|
|
692
682
|
unchanged = true;
|
|
693
683
|
return undefined;
|
|
@@ -710,7 +700,7 @@ export async function recordLaunchedPiIdentity(
|
|
|
710
700
|
}
|
|
711
701
|
|
|
712
702
|
/**
|
|
713
|
-
* Observed role-package launch provenance written onto the same
|
|
703
|
+
* Observed role-package launch provenance written onto the same state.jsonl invocation row.
|
|
714
704
|
* Values are field observations from the public CLI activation seam — never fixed schema markers.
|
|
715
705
|
*/
|
|
716
706
|
export type LaunchedRolePackageIdentity = {
|
|
@@ -759,20 +749,22 @@ export async function recordLaunchedRolePackageIdentity(
|
|
|
759
749
|
});
|
|
760
750
|
}
|
|
761
751
|
|
|
752
|
+
/** Trim, then the shared ticket spelling. Callers keep their own diagnostic text. */
|
|
753
|
+
function rejectUnlessTicketNumber(raw: string, message: string): number {
|
|
754
|
+
const parsed = parseTicketNumber(raw.trim());
|
|
755
|
+
if (parsed === undefined) throw new CliUsageError(message);
|
|
756
|
+
return parsed;
|
|
757
|
+
}
|
|
758
|
+
|
|
762
759
|
/** Positive ticket number for analyst query-scope face (and shared integer parse). */
|
|
763
760
|
export function parsePositiveTicketNumber(
|
|
764
761
|
raw: string,
|
|
765
762
|
flag: string,
|
|
766
763
|
): number {
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
const value = Number(trimmed);
|
|
772
|
-
if (!Number.isSafeInteger(value) || value < 1) {
|
|
773
|
-
throw new CliUsageError(`${flag} must be a positive integer, got ${raw}`);
|
|
774
|
-
}
|
|
775
|
-
return value;
|
|
764
|
+
return rejectUnlessTicketNumber(
|
|
765
|
+
raw,
|
|
766
|
+
`${flag} must be a positive integer, got ${raw}`,
|
|
767
|
+
);
|
|
776
768
|
}
|
|
777
769
|
|
|
778
770
|
/**
|
|
@@ -1299,10 +1291,16 @@ async function freezeRegularFileAttachment(
|
|
|
1299
1291
|
|
|
1300
1292
|
/** Freeze attachments only — ticket binding is the shared LLM seat path (#635). */
|
|
1301
1293
|
async function freezeAttachments(
|
|
1294
|
+
ledgerHome: string,
|
|
1302
1295
|
attachmentPaths: readonly string[],
|
|
1303
1296
|
attachmentsDirectory: string,
|
|
1304
1297
|
): Promise<readonly FrozenAttachment[]> {
|
|
1305
1298
|
const attachments: FrozenAttachment[] = [];
|
|
1299
|
+
// attachments/ appears only when something is frozen; created through the
|
|
1300
|
+
// ledger's symlink-rejecting tree walk, never a plain mkdir.
|
|
1301
|
+
if (attachmentPaths.length > 0) {
|
|
1302
|
+
ensureRealDirectoryTree(ledgerHome, attachmentsDirectory);
|
|
1303
|
+
}
|
|
1306
1304
|
for (let i = 0; i < attachmentPaths.length; i += 1) {
|
|
1307
1305
|
const frozen = await freezeRegularFileAttachment(
|
|
1308
1306
|
attachmentPaths[i]!,
|
|
@@ -1328,7 +1326,7 @@ export async function freezeAttachmentsIntoRun(
|
|
|
1328
1326
|
const ledgerHome = resolveActivationLedgerHome(homeFromRunDirectory(runDirectory));
|
|
1329
1327
|
const attachmentsDirectory = join(runDirectory, "attachments", summonsKey);
|
|
1330
1328
|
ensureRealDirectoryTree(ledgerHome, attachmentsDirectory);
|
|
1331
|
-
return freezeAttachments(attachmentPaths, attachmentsDirectory);
|
|
1329
|
+
return freezeAttachments(ledgerHome, attachmentPaths, attachmentsDirectory);
|
|
1332
1330
|
}
|
|
1333
1331
|
|
|
1334
1332
|
/** Freeze prepared bytes and metadata through one path for birth or retained runs. */
|
|
@@ -1650,7 +1648,10 @@ export async function admitPublicRole(
|
|
|
1650
1648
|
}
|
|
1651
1649
|
let caseIdentity: DoctorCaseIdentity;
|
|
1652
1650
|
try {
|
|
1653
|
-
const patient = await loadDoctorCase(
|
|
1651
|
+
const patient = await loadDoctorCase(
|
|
1652
|
+
caseRunsPath,
|
|
1653
|
+
parsed.runs === undefined ? undefined : placed.projectRoot,
|
|
1654
|
+
);
|
|
1654
1655
|
if (patient.identity.issueNumber !== issueNumber) {
|
|
1655
1656
|
throw new CliUsageError(
|
|
1656
1657
|
`doctor case issue ${patient.identity.issueNumber} does not match --issue ${issueNumber}`,
|
|
@@ -1666,7 +1667,7 @@ export async function admitPublicRole(
|
|
|
1666
1667
|
{ cause: error },
|
|
1667
1668
|
);
|
|
1668
1669
|
}
|
|
1669
|
-
frozenAttachments = await freezeAttachments(attachmentPaths, placed.attachmentsDirectory);
|
|
1670
|
+
frozenAttachments = await freezeAttachments(placed.ledgerHome, attachmentPaths, placed.attachmentsDirectory);
|
|
1670
1671
|
return {
|
|
1671
1672
|
issueNumber,
|
|
1672
1673
|
caseRunsPath,
|
|
@@ -1825,7 +1826,7 @@ export type AdmitJudgeInvocationOptions = {
|
|
|
1825
1826
|
/** Injectable clock/id for tests. */
|
|
1826
1827
|
createRunId?: () => string;
|
|
1827
1828
|
principalAuthority: DurablePrincipalAuthority;
|
|
1828
|
-
/** Effective model for this invocation — written onto invocation.
|
|
1829
|
+
/** Effective model for this invocation — written onto the state.jsonl invocation row. */
|
|
1829
1830
|
model?: InvocationEffectiveModel;
|
|
1830
1831
|
/** Typed ticket already on this summons (起居录 / parent board). Never parsed from prose. */
|
|
1831
1832
|
assertedTicketNumber?: number;
|
|
@@ -1897,7 +1898,7 @@ async function placeRoleAdmission(options: {
|
|
|
1897
1898
|
});
|
|
1898
1899
|
const attachments = options.freezeAttachments === false
|
|
1899
1900
|
? []
|
|
1900
|
-
: await freezeAttachments(options.attachmentPaths, attachmentsDirectory);
|
|
1901
|
+
: await freezeAttachments(ledgerHome, options.attachmentPaths, attachmentsDirectory);
|
|
1901
1902
|
return {
|
|
1902
1903
|
runId,
|
|
1903
1904
|
bookKey,
|
|
@@ -1947,9 +1948,8 @@ async function persistPlacedAdmission(
|
|
|
1947
1948
|
},
|
|
1948
1949
|
placed: PlacedRoleAdmission,
|
|
1949
1950
|
model: InvocationEffectiveModel | undefined,
|
|
1950
|
-
): Promise<
|
|
1951
|
-
|
|
1952
|
-
await writeAdmittedRequestPersistence(admittedRequestPath, admitted, {
|
|
1951
|
+
): Promise<void> {
|
|
1952
|
+
writeAdmittedRequestPersistence(placed.runDirectory, admitted, {
|
|
1953
1953
|
sessionDirectory: placed.sessionDirectory,
|
|
1954
1954
|
sessionFile: placed.sessionFile,
|
|
1955
1955
|
});
|
|
@@ -1962,12 +1962,11 @@ async function persistPlacedAdmission(
|
|
|
1962
1962
|
admitted.role,
|
|
1963
1963
|
model,
|
|
1964
1964
|
);
|
|
1965
|
-
return admittedRequestPath;
|
|
1966
1965
|
}
|
|
1967
1966
|
|
|
1968
1967
|
/**
|
|
1969
1968
|
* Shared admission: project check, placement, attachment freeze,
|
|
1970
|
-
* admitted
|
|
1969
|
+
* admitted and invocation section write.
|
|
1971
1970
|
* Countersign passes deferPersistence so same-ticket lookup can reserve coordinates
|
|
1972
1971
|
* before materializeCountersignInvocation writes the page.
|
|
1973
1972
|
* Seats whose extra facts are known before placement pass them as admittedFields.
|
|
@@ -1981,7 +1980,7 @@ async function admitStandardMaterialInvocation<
|
|
|
1981
1980
|
>(
|
|
1982
1981
|
role: R,
|
|
1983
1982
|
options: AdmitInspectorInvocationOptions & {
|
|
1984
|
-
/** Reserve coordinates; skip freeze, placement disk, and admitted
|
|
1983
|
+
/** Reserve coordinates; skip freeze, placement disk, and admitted section write. */
|
|
1985
1984
|
readonly deferPersistence?: boolean;
|
|
1986
1985
|
/** Skip attachment freeze. Gleaner-left admits no caller attachments. */
|
|
1987
1986
|
readonly freezeAttachments?: boolean;
|
|
@@ -2032,9 +2031,7 @@ async function admitStandardMaterialInvocation<
|
|
|
2032
2031
|
attachments: persistedAttachmentRefs(placed.attachments),
|
|
2033
2032
|
...placed.ticketFields,
|
|
2034
2033
|
};
|
|
2035
|
-
|
|
2036
|
-
? join(placed.runDirectory, "admitted-request.json")
|
|
2037
|
-
: await persistPlacedAdmission(admitted, placed, options.model);
|
|
2034
|
+
if (!defer) await persistPlacedAdmission(admitted, placed, options.model);
|
|
2038
2035
|
return {
|
|
2039
2036
|
role,
|
|
2040
2037
|
runId: placed.runId,
|
|
@@ -2045,7 +2042,6 @@ async function admitStandardMaterialInvocation<
|
|
|
2045
2042
|
attachments: placed.attachments,
|
|
2046
2043
|
runDirectory: placed.runDirectory,
|
|
2047
2044
|
principal: placed.principal,
|
|
2048
|
-
admittedRequestPath,
|
|
2049
2045
|
...correlationFields,
|
|
2050
2046
|
...placed.ticketFields,
|
|
2051
2047
|
...admittedFields,
|
|
@@ -2138,7 +2134,7 @@ export type AdmitCountersignInvocationOptions = {
|
|
|
2138
2134
|
/** Injectable clock/id for tests. */
|
|
2139
2135
|
createRunId?: () => string;
|
|
2140
2136
|
principalAuthority: DurablePrincipalAuthority;
|
|
2141
|
-
/** Effective model for this invocation — written onto invocation.
|
|
2137
|
+
/** Effective model for this invocation — written onto the state.jsonl invocation row. */
|
|
2142
2138
|
model?: InvocationEffectiveModel;
|
|
2143
2139
|
correlationId?: string;
|
|
2144
2140
|
/** Same-ticket lookup may select an existing run before a new run is persisted. */
|
|
@@ -2168,10 +2164,6 @@ export async function materializeCountersignInvocation(
|
|
|
2168
2164
|
});
|
|
2169
2165
|
const placement = placed;
|
|
2170
2166
|
(admitted as { runDirectory: string }).runDirectory = placement.runDirectory;
|
|
2171
|
-
(admitted as { admittedRequestPath: string }).admittedRequestPath = join(
|
|
2172
|
-
placement.runDirectory,
|
|
2173
|
-
"admitted-request.json",
|
|
2174
|
-
);
|
|
2175
2167
|
(admitted as { principal: DurablePrincipal }).principal = placement.principal;
|
|
2176
2168
|
const ticketFields = ticketAdmissionFields(options.ticketNumber);
|
|
2177
2169
|
if (ticketFields.ticketNumber !== undefined) {
|
|
@@ -2182,7 +2174,7 @@ export async function materializeCountersignInvocation(
|
|
|
2182
2174
|
admitted.runDirectory,
|
|
2183
2175
|
);
|
|
2184
2176
|
(admitted as { attachments: readonly FrozenAttachment[] }).attachments = attachments;
|
|
2185
|
-
|
|
2177
|
+
writeAdmittedRequestPersistence(admitted.runDirectory, admitted, {
|
|
2186
2178
|
sessionDirectory: placement.sessionDirectory,
|
|
2187
2179
|
sessionFile: placement.sessionFile,
|
|
2188
2180
|
});
|
|
@@ -2193,7 +2185,7 @@ export async function materializeCountersignInvocation(
|
|
|
2193
2185
|
);
|
|
2194
2186
|
}
|
|
2195
2187
|
|
|
2196
|
-
/** Load admitted
|
|
2188
|
+
/** Load the admitted section written at admission (Navigator work-context seam). */
|
|
2197
2189
|
export async function loadAdmittedJudgeRequest(
|
|
2198
2190
|
runDirectory: string,
|
|
2199
2191
|
): Promise<{
|
|
@@ -2202,11 +2194,8 @@ export async function loadAdmittedJudgeRequest(
|
|
|
2202
2194
|
attachments: readonly FrozenAttachment[];
|
|
2203
2195
|
} | undefined> {
|
|
2204
2196
|
try {
|
|
2205
|
-
const
|
|
2206
|
-
|
|
2207
|
-
) as unknown;
|
|
2208
|
-
if (!isRecord(raw)) return undefined;
|
|
2209
|
-
const record = raw;
|
|
2197
|
+
const record = readPageSync(runDirectory, "admitted");
|
|
2198
|
+
if (record === undefined) return undefined;
|
|
2210
2199
|
if (!packagedPublicInstructionSubject(record.role)) return undefined;
|
|
2211
2200
|
if (typeof record.instruction !== "string") return undefined;
|
|
2212
2201
|
if (typeof record.instructionEmpty !== "boolean") return undefined;
|
|
@@ -2221,14 +2210,6 @@ export async function loadAdmittedJudgeRequest(
|
|
|
2221
2210
|
}
|
|
2222
2211
|
}
|
|
2223
2212
|
|
|
2224
|
-
export async function ensureRunArtifactsDir(runDirectory: string): Promise<string> {
|
|
2225
|
-
const directory = roleRunArtifactsDirectory(runDirectory);
|
|
2226
|
-
return ensureRoleRunDirectory(
|
|
2227
|
-
resolveActivationLedgerHome(homeFromRunDirectory(runDirectory)),
|
|
2228
|
-
directory,
|
|
2229
|
-
);
|
|
2230
|
-
}
|
|
2231
|
-
|
|
2232
2213
|
export type AdmitFixerInvocationOptions = {
|
|
2233
2214
|
home: string;
|
|
2234
2215
|
principalAuthority: DurablePrincipalAuthority;
|
|
@@ -2240,7 +2221,7 @@ export type AdmitFixerInvocationOptions = {
|
|
|
2240
2221
|
prerequisitesPath?: string;
|
|
2241
2222
|
project?: string;
|
|
2242
2223
|
createRunId?: () => string;
|
|
2243
|
-
/** Effective model for this invocation — written onto invocation.
|
|
2224
|
+
/** Effective model for this invocation — written onto the state.jsonl invocation row. */
|
|
2244
2225
|
model?: InvocationEffectiveModel;
|
|
2245
2226
|
/** Typed ticket already on this summons. Placement uses it; code does not infer one. */
|
|
2246
2227
|
assertedTicketNumber?: number;
|
|
@@ -2311,32 +2292,21 @@ function isGitRemoteMissing(error: unknown): boolean {
|
|
|
2311
2292
|
return status === 2;
|
|
2312
2293
|
}
|
|
2313
2294
|
|
|
2314
|
-
/** Positive Issue number grammar shared with Doctor case path identity. */
|
|
2315
|
-
const DOCTOR_ISSUE_NUMBER_PATTERN = /^[1-9]\d*$/;
|
|
2316
|
-
|
|
2317
|
-
/** Match retained Doctor case runs roots (ADR 0017 / loadDoctorCase). */
|
|
2318
|
-
/** Canonical Doctor case: `<book>/<ticket>/runs`. Legacy `issues/<n>/runs` is read-only compat. */
|
|
2319
|
-
const DOCTOR_CASE_RUNS_PATH_PATTERN =
|
|
2320
|
-
/\/\.ak-roles\/books\/[^/]+\/(?:issues\/)?([1-9]\d*)\/runs$/;
|
|
2321
|
-
|
|
2322
2295
|
/**
|
|
2323
2296
|
* Parse a positive Issue number for public Doctor admission.
|
|
2324
2297
|
* Leading zeros and non-integers are structural rejects.
|
|
2325
2298
|
*/
|
|
2326
2299
|
export function parseDoctorIssueNumber(raw: string): number {
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
);
|
|
2332
|
-
}
|
|
2333
|
-
return Number(trimmed);
|
|
2300
|
+
return rejectUnlessTicketNumber(
|
|
2301
|
+
raw,
|
|
2302
|
+
`doctor --issue must be a positive integer, got ${raw}`,
|
|
2303
|
+
);
|
|
2334
2304
|
}
|
|
2335
2305
|
|
|
2336
2306
|
/**
|
|
2337
2307
|
* Resolve the retained Doctor case runs root from Issue identity.
|
|
2338
|
-
* Default is the #78 book locator; optional --runs must stay project-confined
|
|
2339
|
-
*
|
|
2308
|
+
* Default is the #78 book locator; optional --runs must stay project-confined.
|
|
2309
|
+
* loadDoctorCase owns retained-root grammar at IO; admission matches its case identity.
|
|
2340
2310
|
*/
|
|
2341
2311
|
export async function resolveDoctorCaseRunsPath(options: {
|
|
2342
2312
|
home: string;
|
|
@@ -2388,18 +2358,6 @@ export async function resolveDoctorCaseRunsPath(options: {
|
|
|
2388
2358
|
);
|
|
2389
2359
|
}
|
|
2390
2360
|
|
|
2391
|
-
const normalized = real.split(sep).join("/");
|
|
2392
|
-
const match = normalized.match(DOCTOR_CASE_RUNS_PATH_PATTERN);
|
|
2393
|
-
if (!match) {
|
|
2394
|
-
throw new CliUsageError(
|
|
2395
|
-
"doctor --runs must be an .ak-roles/books/<book>/<n>/runs directory",
|
|
2396
|
-
);
|
|
2397
|
-
}
|
|
2398
|
-
if (Number(match[1]) !== options.issueNumber) {
|
|
2399
|
-
throw new CliUsageError(
|
|
2400
|
-
`doctor --runs issue ${match[1]} does not match --issue ${options.issueNumber}`,
|
|
2401
|
-
);
|
|
2402
|
-
}
|
|
2403
2361
|
return real;
|
|
2404
2362
|
}
|
|
2405
2363
|
|
|
@@ -2418,7 +2376,7 @@ export type AdmitReviewerInvocationOptions = {
|
|
|
2418
2376
|
project?: string;
|
|
2419
2377
|
createRunId?: () => string;
|
|
2420
2378
|
correlationId?: string;
|
|
2421
|
-
/** Effective model for this invocation — written onto invocation.
|
|
2379
|
+
/** Effective model for this invocation — written onto the state.jsonl invocation row. */
|
|
2422
2380
|
model?: InvocationEffectiveModel;
|
|
2423
2381
|
/** Typed ticket already on this summons. Placement uses it; code does not infer one. */
|
|
2424
2382
|
assertedTicketNumber?: number;
|
|
@@ -2471,14 +2429,12 @@ export type AdmitMergerInvocationOptions = {
|
|
|
2471
2429
|
attachmentPaths: readonly string[];
|
|
2472
2430
|
project?: string;
|
|
2473
2431
|
createRunId?: () => string;
|
|
2474
|
-
/** Effective model for this invocation — written onto invocation.
|
|
2432
|
+
/** Effective model for this invocation — written onto the state.jsonl invocation row. */
|
|
2475
2433
|
model?: InvocationEffectiveModel;
|
|
2476
2434
|
/** Typed ticket already on this summons. Placement uses it; code does not infer one. */
|
|
2477
2435
|
assertedTicketNumber?: number;
|
|
2478
2436
|
};
|
|
2479
2437
|
|
|
2480
|
-
const ANALYST_TICKET_NUMBER_PATTERN = /^[1-9]\d*$/;
|
|
2481
|
-
|
|
2482
2438
|
/**
|
|
2483
2439
|
* Parse a positive ticket / issue number for public analyst admission.
|
|
2484
2440
|
* Leading zeros and non-integers are structural rejects (same face as #176).
|
|
@@ -2488,20 +2444,10 @@ export function parseAnalystTicketNumber(
|
|
|
2488
2444
|
raw: string,
|
|
2489
2445
|
flag: string = "--ticket",
|
|
2490
2446
|
): number {
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
);
|
|
2496
|
-
}
|
|
2497
|
-
const value = Number(trimmed);
|
|
2498
|
-
// Digit-only strings beyond MAX_SAFE_INTEGER round or become Infinity — reject.
|
|
2499
|
-
if (!Number.isSafeInteger(value) || value < 1) {
|
|
2500
|
-
throw new CliUsageError(
|
|
2501
|
-
`analyst ${flag} must be a positive integer, got ${raw}`,
|
|
2502
|
-
);
|
|
2503
|
-
}
|
|
2504
|
-
return value;
|
|
2447
|
+
return rejectUnlessTicketNumber(
|
|
2448
|
+
raw,
|
|
2449
|
+
`analyst ${flag} must be a positive integer, got ${raw}`,
|
|
2450
|
+
);
|
|
2505
2451
|
}
|
|
2506
2452
|
|
|
2507
2453
|
/**
|
|
@@ -2535,7 +2481,7 @@ export function parseAnalystCohortIssueToken(
|
|
|
2535
2481
|
const sep = trimmed.lastIndexOf(":");
|
|
2536
2482
|
if (sep > 0) {
|
|
2537
2483
|
const rhs = trimmed.slice(sep + 1);
|
|
2538
|
-
if (
|
|
2484
|
+
if (parseTicketNumber(rhs) !== undefined) {
|
|
2539
2485
|
const bookKey = trimmed.slice(0, sep);
|
|
2540
2486
|
if (bookKey.trim() === "") {
|
|
2541
2487
|
throw new CliUsageError(
|