@akagilnc/pi-workflow-roles 0.1.3412 → 0.1.3439
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/archivist-record-entry.js +21 -12
- package/dist/archivist-record-topology.js +20 -0
- package/dist/auditor-dossier-tool.js +67 -76
- package/dist/canonical-json.js +66 -69
- package/dist/compliance-transport.js +99 -86
- package/dist/countersign-contracts.js +23 -25
- package/dist/doctor-contracts.js +169 -145
- package/dist/engine-detour-tool.js +83 -87
- package/dist/engine-detour.js +148 -183
- package/dist/evidence-child-executor.js +10 -1
- package/dist/exact-utf8.js +11 -10
- package/dist/git-object-id.js +10 -5
- package/dist/gleaner-left-contracts.js +33 -35
- package/dist/grok/production-host.js +229 -118
- package/dist/inspector-contracts.js +28 -31
- package/dist/institutional-resolution.js +101 -104
- package/dist/ledger-session-read.js +241 -0
- package/dist/merger-contracts.js +66 -56
- package/dist/notary-contracts.js +91 -100
- package/dist/open-tool-schema.js +32 -31
- package/dist/package-contracts/collector-output.js +56 -43
- package/dist/package-contracts/fixer-output.js +52 -50
- package/dist/package-contracts/fixer-packet.js +67 -64
- package/dist/package-contracts/gatekeeper-output.js +48 -56
- package/dist/package-contracts/judge-output.js +23 -20
- package/dist/package-contracts/navigator-output.js +35 -40
- package/dist/package-contracts/reviewer-output.js +94 -110
- package/dist/package-contracts/terminating-infrastructure.js +62 -90
- package/dist/package-contracts/worker-output.js +30 -11
- package/dist/package-resources/engine-material.js +58 -94
- package/dist/public-cli/config.js +420 -484
- package/dist/public-cli/main.js +1219 -524
- package/dist/public-cli/registry.js +107 -114
- package/dist/receipt-delivery-policy.js +74 -76
- package/dist/session-opening-materials.js +44 -47
- package/dist/sha256.js +5 -3
- package/dist/sitian-appender.js +204 -210
- package/dist/sitian-contracts.js +15 -19
- package/dist/sitian-facade.js +5 -10
- package/dist/sitian-reader.js +37 -46
- package/dist/stream-idle-guard.js +63 -71
- package/dist/ticket-seat-memory.js +244 -0
- package/package.json +1 -1
- package/scripts/build-package.mjs +41 -0
- package/souls/audit-law.md +4 -0
- package/souls/countersign.md +1 -1
- package/souls/gleaner-left.md +1 -1
- package/souls/inspector.md +1 -1
- package/souls/notary.md +1 -1
- package/souls/reviewer.md +1 -1
- package/src/analyst-gate-cycles-read.ts +152 -83
- package/src/analyst-ledger.ts +170 -43
- package/src/archivist-record-entry.ts +32 -11
- package/src/archivist-record-topology.ts +39 -0
- package/src/evidence-child-executor.ts +14 -1
- package/src/grok/production-host.ts +44 -9
- package/src/host-contracts.ts +18 -0
- package/src/host-transition-prior-native.ts +8 -1
- package/src/ledger-session-read.ts +56 -0
- package/src/pi/durable-principal.ts +6 -0
- package/src/public-cli/inspector-run.ts +21 -10
- package/src/public-cli/invocation.ts +4 -20
- package/src/public-cli/notary-run.ts +21 -4
- package/src/public-cli/post-admission.ts +333 -42
- package/src/public-cli/settlement.ts +266 -81
- package/src/session-opening-materials.ts +4 -2
- package/src/ticket-seat-memory.ts +454 -0
|
@@ -12,6 +12,9 @@ import {
|
|
|
12
12
|
type AnalystGateCycleRound,
|
|
13
13
|
} from "../analyst-gate-cycles-read.ts";
|
|
14
14
|
import { readSitianRecords, resolveSitianRecordPath, sitianReport } from "../sitian-facade.ts";
|
|
15
|
+
import {
|
|
16
|
+
resolveTicketSeatMemoryNestDirectories,
|
|
17
|
+
} from "../ticket-seat-memory.ts";
|
|
15
18
|
import {
|
|
16
19
|
readAuditEscalationSubmission,
|
|
17
20
|
readLatestSubmissionOutcome,
|
|
@@ -432,6 +435,111 @@ function isTypedActivationError(
|
|
|
432
435
|
);
|
|
433
436
|
}
|
|
434
437
|
|
|
438
|
+
/** Flatten nested AggregateError leaves; non-aggregate values stay as one fact. */
|
|
439
|
+
function flattenThrownFailureLeaves(error: unknown): unknown[] {
|
|
440
|
+
if (!(error instanceof AggregateError)) {
|
|
441
|
+
return [error];
|
|
442
|
+
}
|
|
443
|
+
const leaves: unknown[] = [];
|
|
444
|
+
for (const item of error.errors) {
|
|
445
|
+
leaves.push(...flattenThrownFailureLeaves(item));
|
|
446
|
+
}
|
|
447
|
+
return leaves;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Project one thrown value into a ControlledFailure leaf.
|
|
452
|
+
* Sole owner for thrown-leaf identity/diagnostic mapping (last-host write concurrent
|
|
453
|
+
* facts reuse this — do not fork a second leaf mapper).
|
|
454
|
+
* AggregateError nesting is handled by classifyThrownFailure.
|
|
455
|
+
*/
|
|
456
|
+
export function projectThrownFailureLeaf(error: unknown): ControlledFailure {
|
|
457
|
+
if (isTypedActivationError(error)) {
|
|
458
|
+
const identity = thrownIdentity(error);
|
|
459
|
+
if (error.failureCode !== undefined && identity.code === undefined) {
|
|
460
|
+
identity.code = error.failureCode;
|
|
461
|
+
}
|
|
462
|
+
return {
|
|
463
|
+
cause: error.knownCause,
|
|
464
|
+
diagnostic: error.message || error.name || "unrecognized exception",
|
|
465
|
+
identity,
|
|
466
|
+
...(error.details === undefined ? {} : { details: error.details }),
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
if (error instanceof Error) {
|
|
470
|
+
const identity = thrownIdentity(error);
|
|
471
|
+
return {
|
|
472
|
+
cause: "unrecognized",
|
|
473
|
+
diagnostic: error.message || error.name || "unrecognized exception",
|
|
474
|
+
identity,
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
return {
|
|
478
|
+
cause: "unrecognized",
|
|
479
|
+
diagnostic: String(error),
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Concurrent thrown failures (host + cleanup / last-host write, etc.):
|
|
485
|
+
* primary leaf owns cause/diagnostic/identity; remaining leaves stay as
|
|
486
|
+
* details.concurrentFailures so neither fact covers the other.
|
|
487
|
+
*/
|
|
488
|
+
function classifyThrownFailure(error: unknown): ControlledFailure {
|
|
489
|
+
if (!(error instanceof AggregateError)) {
|
|
490
|
+
return projectThrownFailureLeaf(error);
|
|
491
|
+
}
|
|
492
|
+
const leaves = flattenThrownFailureLeaves(error);
|
|
493
|
+
if (leaves.length === 0) {
|
|
494
|
+
// Empty aggregate — retain the aggregate shell rather than invent a cause.
|
|
495
|
+
return projectThrownFailureLeaf(error);
|
|
496
|
+
}
|
|
497
|
+
const primary = projectThrownFailureLeaf(leaves[0]);
|
|
498
|
+
if (leaves.length === 1) {
|
|
499
|
+
return primary;
|
|
500
|
+
}
|
|
501
|
+
const priorConcurrent = Array.isArray(primary.details?.concurrentFailures)
|
|
502
|
+
? primary.details.concurrentFailures
|
|
503
|
+
: [];
|
|
504
|
+
const concurrentFailures = [
|
|
505
|
+
...priorConcurrent,
|
|
506
|
+
...leaves.slice(1).map((leaf) => {
|
|
507
|
+
const secondary = projectThrownFailureLeaf(leaf);
|
|
508
|
+
return {
|
|
509
|
+
cause: secondary.cause,
|
|
510
|
+
diagnostic: secondary.diagnostic,
|
|
511
|
+
...(secondary.identity === undefined ? {} : { identity: secondary.identity }),
|
|
512
|
+
...(secondary.details === undefined ? {} : { details: secondary.details }),
|
|
513
|
+
};
|
|
514
|
+
}),
|
|
515
|
+
];
|
|
516
|
+
return {
|
|
517
|
+
cause: primary.cause,
|
|
518
|
+
diagnostic: primary.diagnostic,
|
|
519
|
+
...(primary.identity === undefined ? {} : { identity: primary.identity }),
|
|
520
|
+
details: {
|
|
521
|
+
...(primary.details ?? {}),
|
|
522
|
+
concurrentFailures,
|
|
523
|
+
},
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/** Merge caller-owned secondary evidence into a classified failure without washing path facts. */
|
|
528
|
+
function withKnownDetails(
|
|
529
|
+
failure: ControlledFailure,
|
|
530
|
+
knownDetails: Readonly<Record<string, unknown>> | undefined,
|
|
531
|
+
): ControlledFailure {
|
|
532
|
+
if (knownDetails === undefined) return failure;
|
|
533
|
+
const { timedOut: _knownTimedOut, ...rest } = knownDetails;
|
|
534
|
+
return {
|
|
535
|
+
...failure,
|
|
536
|
+
details: {
|
|
537
|
+
...rest,
|
|
538
|
+
...(failure.details ?? {}),
|
|
539
|
+
},
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
|
|
435
543
|
/**
|
|
436
544
|
* Classify a controlled post-admission failure without washing unrecognized identities.
|
|
437
545
|
* Cause classes are closed; diagnostic text retains the original identity when known.
|
|
@@ -439,6 +547,7 @@ function isTypedActivationError(
|
|
|
439
547
|
* Order: thrown → knownCause → timeout → activation (nonzero) → session → output.
|
|
440
548
|
* knownCause precedes timeout so a co-present typed provider/session identity is not
|
|
441
549
|
* washed when the child also timed out. Cause is never inferred from stderr wording.
|
|
550
|
+
* AggregateError concurrent leaves keep primary identity and secondary facts in details.
|
|
442
551
|
*/
|
|
443
552
|
export function classifyPostAdmissionFailure(input: {
|
|
444
553
|
timedOut: boolean;
|
|
@@ -468,30 +577,7 @@ export function classifyPostAdmissionFailure(input: {
|
|
|
468
577
|
}): ControlledFailure {
|
|
469
578
|
// Own-key presence, not value: `throw undefined` is a real caught exception.
|
|
470
579
|
if (Object.hasOwn(input, "thrown")) {
|
|
471
|
-
|
|
472
|
-
if (isTypedActivationError(error)) {
|
|
473
|
-
const identity = thrownIdentity(error);
|
|
474
|
-
if (error.failureCode !== undefined && identity.code === undefined) {
|
|
475
|
-
identity.code = error.failureCode;
|
|
476
|
-
}
|
|
477
|
-
return {
|
|
478
|
-
cause: error.knownCause,
|
|
479
|
-
diagnostic: error.message || error.name || "unrecognized exception",
|
|
480
|
-
identity,
|
|
481
|
-
};
|
|
482
|
-
}
|
|
483
|
-
if (error instanceof Error) {
|
|
484
|
-
const identity = thrownIdentity(error);
|
|
485
|
-
return {
|
|
486
|
-
cause: "unrecognized",
|
|
487
|
-
diagnostic: error.message || error.name || "unrecognized exception",
|
|
488
|
-
identity,
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
return {
|
|
492
|
-
cause: "unrecognized",
|
|
493
|
-
diagnostic: String(error),
|
|
494
|
-
};
|
|
580
|
+
return classifyThrownFailure(input.thrown);
|
|
495
581
|
}
|
|
496
582
|
if (input.knownCause !== undefined) {
|
|
497
583
|
const fallback =
|
|
@@ -524,39 +610,54 @@ export function classifyPostAdmissionFailure(input: {
|
|
|
524
610
|
};
|
|
525
611
|
}
|
|
526
612
|
if (input.timedOut) {
|
|
527
|
-
return
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
613
|
+
return withKnownDetails(
|
|
614
|
+
{
|
|
615
|
+
cause: "timeout",
|
|
616
|
+
diagnostic: "role run timed out",
|
|
617
|
+
details: { timedOut: true, exitCode: input.code },
|
|
618
|
+
},
|
|
619
|
+
input.knownDetails,
|
|
620
|
+
);
|
|
532
621
|
}
|
|
533
622
|
if (input.code !== 0) {
|
|
534
623
|
const fallback = `role run failed with exit ${input.code ?? "null"}`;
|
|
535
|
-
return
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
624
|
+
return withKnownDetails(
|
|
625
|
+
{
|
|
626
|
+
cause: "activation",
|
|
627
|
+
diagnostic: conciseChildDiagnostic(input.stderr, fallback),
|
|
628
|
+
details: { exitCode: input.code },
|
|
629
|
+
},
|
|
630
|
+
input.knownDetails,
|
|
631
|
+
);
|
|
540
632
|
}
|
|
541
633
|
if (input.session?.state === "missing") {
|
|
542
|
-
return
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
634
|
+
return withKnownDetails(
|
|
635
|
+
{
|
|
636
|
+
cause: "session",
|
|
637
|
+
diagnostic: "role run left no readable session transcript",
|
|
638
|
+
details: { exitCode: input.code, session: "missing" },
|
|
639
|
+
},
|
|
640
|
+
input.knownDetails,
|
|
641
|
+
);
|
|
547
642
|
}
|
|
548
643
|
if (input.session?.state === "unreadable") {
|
|
549
|
-
return
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
644
|
+
return withKnownDetails(
|
|
645
|
+
{
|
|
646
|
+
cause: "session",
|
|
647
|
+
diagnostic: input.session.diagnostic,
|
|
648
|
+
details: { exitCode: input.code, session: "unreadable" },
|
|
649
|
+
},
|
|
650
|
+
input.knownDetails,
|
|
651
|
+
);
|
|
554
652
|
}
|
|
555
|
-
return
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
653
|
+
return withKnownDetails(
|
|
654
|
+
{
|
|
655
|
+
cause: "output",
|
|
656
|
+
diagnostic: "role run completed without a lawful typed terminal result",
|
|
657
|
+
details: { exitCode: input.code },
|
|
658
|
+
},
|
|
659
|
+
input.knownDetails,
|
|
660
|
+
);
|
|
560
661
|
}
|
|
561
662
|
|
|
562
663
|
/** One projection owner for the four audited public runners. */
|
|
@@ -907,41 +1008,100 @@ async function loadBoundAuditorVolumes(
|
|
|
907
1008
|
latestParentUserIndex = i;
|
|
908
1009
|
break;
|
|
909
1010
|
}
|
|
910
|
-
const
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
1011
|
+
const runDirectory = join(dirname(sessionFile), "..");
|
|
1012
|
+
// #636: ticket-seat auditor memory nest via sole discovery helper.
|
|
1013
|
+
const memoryNests = await resolveTicketSeatMemoryNestDirectories({
|
|
1014
|
+
runDirectory,
|
|
1015
|
+
seats: ["auditor"],
|
|
1016
|
+
});
|
|
1017
|
+
const childDirectories = [
|
|
1018
|
+
join(dirname(sessionFile), "auditor-roles"),
|
|
1019
|
+
...memoryNests,
|
|
1020
|
+
];
|
|
918
1021
|
// Auto-resume seam (owner A): stale check must ignore resume envelope and
|
|
919
1022
|
// prioritize retention. Previous `attemptEntryIndex < latest` discarded the
|
|
920
1023
|
// first attempt's child after resume advanced latest, losing retentionFailure
|
|
921
1024
|
// when retry had no compliance entry. Fix: ignore envelope for staleness and
|
|
922
1025
|
// prefer any valid compliance failure before falling back to primary.
|
|
923
1026
|
const valid: BoundAuditorVolume[] = [];
|
|
924
|
-
|
|
925
|
-
|
|
1027
|
+
let sawAnyDirectory = false;
|
|
1028
|
+
for (const childDirectory of childDirectories) {
|
|
1029
|
+
let names: string[];
|
|
926
1030
|
try {
|
|
927
|
-
|
|
1031
|
+
names = await readdir(childDirectory);
|
|
1032
|
+
sawAnyDirectory = true;
|
|
928
1033
|
} catch (error) {
|
|
929
|
-
|
|
1034
|
+
if (isMissingPathError(error)) continue;
|
|
1035
|
+
throw sessionReadFailure(error, "failed to read bound auditor session directory");
|
|
1036
|
+
}
|
|
1037
|
+
for (const file of names.filter((name) => name.endsWith(".jsonl")).sort().reverse()) {
|
|
1038
|
+
let entries: SessionEntry[];
|
|
1039
|
+
try {
|
|
1040
|
+
entries = await readBoundSessionEntries(join(childDirectory, file));
|
|
1041
|
+
} catch (error) {
|
|
1042
|
+
throw sessionReadFailure(error, "failed to read discovered auditor session");
|
|
1043
|
+
}
|
|
1044
|
+
const header = entries.find((entry) => entry.type === "session");
|
|
1045
|
+
if (!isRecord(header)) continue;
|
|
1046
|
+
// Ticket-seat continuous memory keeps the first parent's header.parentSession;
|
|
1047
|
+
// binding.parent.sessionFile is the per-summons authority (#636).
|
|
1048
|
+
// Each binding owns only its interval — never whole-volume provider/compliance.
|
|
1049
|
+
const bindingIndexes: number[] = [];
|
|
1050
|
+
for (let i = 0; i < entries.length; i += 1) {
|
|
1051
|
+
const entry = entries[i];
|
|
1052
|
+
if (entry?.type === "custom" && entry.customType === AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE) {
|
|
1053
|
+
bindingIndexes.push(i);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
const bindingPasses: Array<{ entry: SessionEntry | undefined; start: number; end: number }> =
|
|
1057
|
+
bindingIndexes.length > 0
|
|
1058
|
+
? bindingIndexes.map((start, idx) => ({
|
|
1059
|
+
entry: entries[start],
|
|
1060
|
+
start,
|
|
1061
|
+
end: idx + 1 < bindingIndexes.length ? bindingIndexes[idx + 1]! : entries.length,
|
|
1062
|
+
}))
|
|
1063
|
+
: [{ entry: undefined, start: 0, end: entries.length }];
|
|
1064
|
+
for (const { entry: bindingEntry, start, end } of bindingPasses) {
|
|
1065
|
+
const bindingParent =
|
|
1066
|
+
bindingEntry !== undefined &&
|
|
1067
|
+
isRecord(bindingEntry.data) &&
|
|
1068
|
+
isRecord(bindingEntry.data.parent)
|
|
1069
|
+
? bindingEntry.data.parent
|
|
1070
|
+
: undefined;
|
|
1071
|
+
const attemptEntryId =
|
|
1072
|
+
typeof bindingParent?.attemptEntryId === "string"
|
|
1073
|
+
? bindingParent.attemptEntryId
|
|
1074
|
+
: undefined;
|
|
1075
|
+
const attemptEntryIndex =
|
|
1076
|
+
attemptEntryId === undefined
|
|
1077
|
+
? -1
|
|
1078
|
+
: parentEntries.findIndex((entry) => entry.id === attemptEntryId);
|
|
1079
|
+
const boundSessionFile =
|
|
1080
|
+
typeof bindingParent?.sessionFile === "string"
|
|
1081
|
+
? bindingParent.sessionFile
|
|
1082
|
+
: typeof header.parentSession === "string"
|
|
1083
|
+
? header.parentSession
|
|
1084
|
+
: undefined;
|
|
1085
|
+
if (boundSessionFile !== sessionFile) continue;
|
|
1086
|
+
if (
|
|
1087
|
+
bindingParent !== undefined &&
|
|
1088
|
+
(bindingParent.sessionId !== parentId || attemptEntryIndex < latestParentUserIndex)
|
|
1089
|
+
) {
|
|
1090
|
+
continue;
|
|
1091
|
+
}
|
|
1092
|
+
if (bindingParent === undefined && header.parentSession !== sessionFile) continue;
|
|
1093
|
+
valid.push({
|
|
1094
|
+
entries: entries.slice(start, end),
|
|
1095
|
+
parentId,
|
|
1096
|
+
sessionFile,
|
|
1097
|
+
...(attemptEntryId === undefined ? {} : { attemptEntryId }),
|
|
1098
|
+
});
|
|
1099
|
+
// Keep every qualifying interval in the current parent-user range.
|
|
1100
|
+
// A single first-match break drops later same-user summons failures (#636).
|
|
1101
|
+
}
|
|
930
1102
|
}
|
|
931
|
-
const header = entries.find((entry) => entry.type === "session");
|
|
932
|
-
if (!isRecord(header) || header.parentSession !== sessionFile) continue;
|
|
933
|
-
const bindingEntry = entries.find((entry) => entry.type === "custom" && entry.customType === AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE);
|
|
934
|
-
const bindingParent = isRecord(bindingEntry?.data) && isRecord(bindingEntry.data.parent) ? bindingEntry.data.parent : undefined;
|
|
935
|
-
const attemptEntryId = typeof bindingParent?.attemptEntryId === "string" ? bindingParent.attemptEntryId : undefined;
|
|
936
|
-
const attemptEntryIndex = attemptEntryId === undefined ? -1 : parentEntries.findIndex((entry) => entry.id === attemptEntryId);
|
|
937
|
-
if (bindingParent?.sessionId !== parentId || bindingParent.sessionFile !== sessionFile || attemptEntryIndex < latestParentUserIndex) continue;
|
|
938
|
-
valid.push({
|
|
939
|
-
entries,
|
|
940
|
-
parentId,
|
|
941
|
-
sessionFile,
|
|
942
|
-
...(attemptEntryId === undefined ? {} : { attemptEntryId }),
|
|
943
|
-
});
|
|
944
1103
|
}
|
|
1104
|
+
if (!sawAnyDirectory && valid.length === 0) return undefined;
|
|
945
1105
|
return valid;
|
|
946
1106
|
}
|
|
947
1107
|
|
|
@@ -2206,16 +2366,32 @@ export function projectTerminalGateFact(
|
|
|
2206
2366
|
}
|
|
2207
2367
|
|
|
2208
2368
|
/**
|
|
2209
|
-
* Read gate facts from the run's session/auditor-roles nest
|
|
2210
|
-
*
|
|
2211
|
-
*
|
|
2369
|
+
* Read gate facts from the run's session/auditor-roles nest and, when the run
|
|
2370
|
+
* carries a ticket, from #636 ticket-seat memory nests via the sole nested-volume
|
|
2371
|
+
* reader (#446/#478). Missing directories → undefined (no-gate zero change).
|
|
2372
|
+
* Damaged discovered volumes propagate — never wash to "no gate".
|
|
2212
2373
|
*/
|
|
2213
2374
|
export async function extractGateFactFromSessionDirectory(
|
|
2214
2375
|
sessionDirectory: string,
|
|
2376
|
+
options: {
|
|
2377
|
+
readonly runDirectory?: string;
|
|
2378
|
+
readonly parentSessionFile?: string;
|
|
2379
|
+
} = {},
|
|
2215
2380
|
): Promise<TerminalGateFact | undefined> {
|
|
2216
|
-
const
|
|
2381
|
+
const runDirectory = options.runDirectory ?? join(sessionDirectory, "..");
|
|
2382
|
+
const memoryNests = await resolveTicketSeatMemoryNestDirectories({
|
|
2383
|
+
runDirectory,
|
|
2384
|
+
seats: ["inspector", "notary"],
|
|
2385
|
+
});
|
|
2386
|
+
const directories = [
|
|
2217
2387
|
join(sessionDirectory, "auditor-roles"),
|
|
2218
|
-
|
|
2388
|
+
...memoryNests,
|
|
2389
|
+
];
|
|
2390
|
+
const parentSessionFile =
|
|
2391
|
+
options.parentSessionFile ?? join(sessionDirectory, "session.jsonl");
|
|
2392
|
+
const rounds = await readAnalystGateCyclesFromAuditorRoles(directories, {
|
|
2393
|
+
parentSessionFile,
|
|
2394
|
+
});
|
|
2219
2395
|
return projectTerminalGateFact(rounds);
|
|
2220
2396
|
}
|
|
2221
2397
|
|
|
@@ -2233,7 +2409,14 @@ async function withOptionalGateProjection<
|
|
|
2233
2409
|
navigator: TerminalNavigatorFact;
|
|
2234
2410
|
artifacts: readonly TerminalArtifactRef[];
|
|
2235
2411
|
},
|
|
2236
|
-
>(
|
|
2412
|
+
>(
|
|
2413
|
+
base: T,
|
|
2414
|
+
sessionDirectory: string,
|
|
2415
|
+
gateContext: {
|
|
2416
|
+
readonly runDirectory?: string;
|
|
2417
|
+
readonly parentSessionFile?: string;
|
|
2418
|
+
} = {},
|
|
2419
|
+
): Promise<T & { gate?: TerminalGateFact }> {
|
|
2237
2420
|
// A gate transport failure is already represented by typed evidence and has no
|
|
2238
2421
|
// accepted gate cycle to project. Re-reading that rejected receipt as an
|
|
2239
2422
|
// accepted cycle would replace the original failure with a projection error.
|
|
@@ -2249,7 +2432,9 @@ async function withOptionalGateProjection<
|
|
|
2249
2432
|
|| secondaryEvidence.stage === "notary"
|
|
2250
2433
|
)
|
|
2251
2434
|
) return base;
|
|
2252
|
-
|
|
2435
|
+
|
|
2436
|
+
// Defaults live solely in extractGateFactFromSessionDirectory — do not re-derive.
|
|
2437
|
+
const gate = await extractGateFactFromSessionDirectory(sessionDirectory, gateContext);
|
|
2253
2438
|
return gate === undefined ? base : { ...base, gate };
|
|
2254
2439
|
}
|
|
2255
2440
|
|
|
@@ -18,11 +18,13 @@ import {
|
|
|
18
18
|
} from "./packaged-role-registry.ts";
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
* Resolve the install package root from
|
|
21
|
+
* Resolve the install package root from a module URL.
|
|
22
22
|
* Works for src/ layout and for bundled artifacts under dist/ (walk up until
|
|
23
23
|
* package.json + souls/ — the shipped material tree).
|
|
24
|
+
* Sole package-root walk for deferred loaders that lack an injected packageRoot
|
|
25
|
+
* (#443 materials; #580/#636 runtime-constructed imports).
|
|
24
26
|
*/
|
|
25
|
-
function resolvePackageRootDir(moduleUrl: string = import.meta.url): string {
|
|
27
|
+
export function resolvePackageRootDir(moduleUrl: string = import.meta.url): string {
|
|
26
28
|
let dir = dirname(fileURLToPath(moduleUrl));
|
|
27
29
|
for (let i = 0; i < 8; i += 1) {
|
|
28
30
|
if (existsSync(join(dir, "package.json")) && existsSync(join(dir, "souls"))) {
|