@akagilnc/pi-workflow-roles 0.1.5483 → 0.1.5504
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 +4945 -4980
- package/dist/analyst-gate-cycles-read.js +11 -29
- package/dist/analyst-ledger.js +10 -12
- package/dist/archivist-record-entry.js +4 -3
- package/dist/archivist-record-pointer.js +59 -32
- package/dist/atomic-write.js +2 -2
- package/dist/auditor-dossier-tool.js +8 -4
- package/dist/auditor-soul.js +2 -1
- package/dist/collector-config.js +6 -5
- package/dist/doctor-evidence.js +5 -1
- package/dist/engine-detour-usage.js +4 -10
- package/dist/headless-host/production-host.js +2450 -2484
- package/dist/ledger-session-read.js +5 -0
- package/dist/migrate-book-topology.js +129 -100
- package/dist/navigator-public-session.js +13 -17
- package/dist/notary-source-run.js +7 -15
- package/dist/packaged-role-registry.js +0 -12
- package/dist/pi/durable-principal.js +2 -3
- package/dist/pi/role-turn-host.js +27 -13
- package/dist/public-cli/instruction-seat-run.js +4 -4
- package/dist/public-cli/invocation.js +17 -45
- package/dist/public-cli/main.js +1074 -1085
- package/dist/public-cli/run-lifecycle.js +17 -47
- package/dist/public-cli/settlement.js +28 -128
- package/dist/public-cli/terminal.js +0 -12
- package/dist/role-run-placement.js +52 -5
- package/dist/role-run-relocation.js +39 -2
- package/dist/run-terminal-artifacts.js +2 -6
- package/dist/run-ticket-number.js +33 -4
- package/dist/session-assistant-usage.js +9 -18
- package/dist/session-identity.js +3 -4
- package/dist/sitian-appender.js +20 -6
- package/dist/submission-ledger.js +4 -4
- package/dist/ticket-provenance-contracts.js +2 -8
- package/dist/ticket-provenance.js +5 -4
- package/package.json +1 -1
- package/src/acp-host/role-turn-host.ts +2 -2
- package/src/analyst-gate-cycles-read.ts +11 -34
- package/src/analyst-ledger.ts +14 -14
- package/src/archivist-record-entry.ts +4 -3
- package/src/archivist-record-pointer.ts +70 -34
- package/src/atomic-write.ts +3 -1
- package/src/auditor-dossier-tool.ts +8 -4
- package/src/auditor-soul.ts +2 -1
- package/src/collector-config.ts +5 -3
- package/src/doctor-evidence.ts +5 -1
- package/src/engine-detour-usage.ts +10 -13
- package/src/factory-board.ts +16 -81
- package/src/headless-host/role-turn-host.ts +2 -1
- 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 +7 -17
- package/src/packaged-role-registry.ts +0 -15
- package/src/pi/durable-principal.ts +2 -4
- package/src/pi/role-turn-host.ts +35 -12
- package/src/public-cli/instruction-seat-run.ts +4 -4
- package/src/public-cli/invocation.ts +29 -57
- package/src/public-cli/run-lifecycle.ts +20 -43
- package/src/public-cli/settlement.ts +30 -173
- package/src/public-cli/terminal.ts +0 -60
- package/src/role-envelope.ts +4 -2
- package/src/role-run-placement.ts +63 -4
- package/src/role-run-relocation.ts +39 -2
- package/src/role-runtime.ts +40 -59
- package/src/run-terminal-artifacts.ts +3 -5
- package/src/run-ticket-number.ts +41 -3
- package/src/session-assistant-usage.ts +8 -20
- package/src/session-identity.ts +3 -4
- package/src/sitian-appender.ts +20 -12
- package/src/submission-ledger.ts +4 -4
- package/src/ticket-provenance-contracts.ts +3 -6
- package/src/ticket-provenance.ts +5 -6
- package/src/ticket-snapshot.ts +3 -2
- package/src/ticket-trajectory.ts +85 -99
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
import { chmod, lstat, open, readdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
9
9
|
import { basename, isAbsolute, join } from "node:path";
|
|
10
10
|
import { activationBookDirectory, resolveActivationLedgerHome, } from "../activation-ledger-topology.js";
|
|
11
|
-
import { listBookRunDirectories } from "../role-run-placement.js";
|
|
11
|
+
import { findRoleRunDirectory, listBookRunDirectories, parseRunLeaf, sessionFileIn } from "../role-run-placement.js";
|
|
12
12
|
import { isSafePositiveTicketNumber } from "../run-ticket-number.js";
|
|
13
13
|
import { CliUsageError } from "./cli-errors.js";
|
|
14
14
|
import { parseCollectorRepository } from "../collector-config.js";
|
|
15
|
-
import { rewriteAdmittedRoleRunPage, rewriteRunDirectoryPathValue, } from "../role-run-relocation.js";
|
|
15
|
+
import { retainedRunPathsMatch, rewriteAdmittedRoleRunPage, rewriteRunDirectoryPathValue, } from "../role-run-relocation.js";
|
|
16
16
|
import { appendEngineSessionMaterial, engineSessionMaterialFromOptions, pickEngineAxis, } from "../package-resources/engine-material.js";
|
|
17
17
|
import { packagedResumeSourcePath, packagedRoleMetadata, } from "../packaged-role-registry.js";
|
|
18
18
|
import { admittedReviewerBaseFault, recordEffectiveInvocationModel, requireAuthorityRef, isReviewerLens, } from "./invocation.js";
|
|
@@ -645,33 +645,9 @@ export async function findRunDirectoryById(home, runId, onlyBookKey, onlyRole) {
|
|
|
645
645
|
return undefined;
|
|
646
646
|
throw error;
|
|
647
647
|
}
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
continue;
|
|
652
|
-
const bookDir = activationBookDirectory(ledgerHome, bookKey);
|
|
653
|
-
let runDirectories;
|
|
654
|
-
try {
|
|
655
|
-
runDirectories = await listBookRunDirectories(bookDir);
|
|
656
|
-
}
|
|
657
|
-
catch (error) {
|
|
658
|
-
if (errorCodeOf(error) === "ENOENT")
|
|
659
|
-
continue;
|
|
660
|
-
throw error;
|
|
661
|
-
}
|
|
662
|
-
for (const runDirectory of runDirectories) {
|
|
663
|
-
const entry = basename(runDirectory);
|
|
664
|
-
if ((onlyRole === undefined && (entry === `${runId}@judge` || entry.startsWith(`${runId}@`))) ||
|
|
665
|
-
entry === `${runId}@${onlyRole}`) {
|
|
666
|
-
matches.push(runDirectory);
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
if (matches.length === 0)
|
|
671
|
-
return undefined;
|
|
672
|
-
if (matches.length === 1)
|
|
673
|
-
return matches[0];
|
|
674
|
-
throw new Error(`ambiguous role run id ${runId}: ${matches.join(", ")}`);
|
|
648
|
+
return findRoleRunDirectory(bookKeys
|
|
649
|
+
.filter((bookKey) => onlyBookKey === undefined || bookKey === onlyBookKey)
|
|
650
|
+
.map((bookKey) => activationBookDirectory(ledgerHome, bookKey)), runId, onlyRole);
|
|
675
651
|
}
|
|
676
652
|
/** Code-owned gate inspector summons prefix (public-role-summons / #747). */
|
|
677
653
|
export const GATE_DOSSIER_POINTER_PREFIX = "卷宗指针:";
|
|
@@ -723,7 +699,7 @@ async function runHasFormedSessionPrincipal(runDirectory) {
|
|
|
723
699
|
const sessionFile = typeof disk.principalWire.sessionFile === "string" &&
|
|
724
700
|
disk.principalWire.sessionFile.trim() !== ""
|
|
725
701
|
? disk.principalWire.sessionFile
|
|
726
|
-
:
|
|
702
|
+
: sessionFileIn(disk.principalWire.sessionDirectory);
|
|
727
703
|
try {
|
|
728
704
|
const stat = await lstat(sessionFile);
|
|
729
705
|
return stat.isFile() && !stat.isSymbolicLink();
|
|
@@ -759,19 +735,15 @@ export async function findLatestRunIdForSeatTicket(input) {
|
|
|
759
735
|
return undefined;
|
|
760
736
|
throw error;
|
|
761
737
|
}
|
|
762
|
-
const suffix = `@${input.role}`;
|
|
763
738
|
let best;
|
|
764
739
|
for (const runDirectory of runDirectories) {
|
|
765
|
-
const
|
|
766
|
-
if (
|
|
767
|
-
continue;
|
|
768
|
-
const runId = entry.slice(0, entry.length - suffix.length);
|
|
769
|
-
if (runId.length === 0)
|
|
740
|
+
const parsed = parseRunLeaf(basename(runDirectory));
|
|
741
|
+
if (parsed === undefined || parsed.role !== input.role)
|
|
770
742
|
continue;
|
|
743
|
+
const runId = parsed.runId;
|
|
771
744
|
const parentPath = await readRunParentPath(runDirectory);
|
|
772
|
-
//
|
|
773
|
-
|
|
774
|
-
if (parentPath !== input.parentRunPath && basename(parentPath ?? "") !== basename(input.parentRunPath))
|
|
745
|
+
// Follow the parent's retained placement without collapsing distinct book/subject bindings.
|
|
746
|
+
if (!await retainedRunPathsMatch(parentPath, input.parentRunPath, input.parentRunPath))
|
|
775
747
|
continue;
|
|
776
748
|
// Durable fact: never resume-select a provisional that never formed principal.
|
|
777
749
|
if (!(await runHasFormedSessionPrincipal(runDirectory)))
|
|
@@ -888,7 +860,7 @@ async function loadResumableRunRecord(home, runId, authority) {
|
|
|
888
860
|
lens = record.lens;
|
|
889
861
|
}
|
|
890
862
|
// Collector — admitted repository/PR identity (#633 resume).
|
|
891
|
-
if (
|
|
863
|
+
if (isSafePositiveTicketNumber(record.prNumber)) {
|
|
892
864
|
prNumber = record.prNumber;
|
|
893
865
|
}
|
|
894
866
|
if (typeof record.repository === "string" && record.repository.trim() !== "") {
|
|
@@ -907,7 +879,7 @@ async function loadResumableRunRecord(home, runId, authority) {
|
|
|
907
879
|
waitWindowMs = record.waitWindowMs;
|
|
908
880
|
}
|
|
909
881
|
// Doctor — admitted single-case identity (#633 resume).
|
|
910
|
-
if (
|
|
882
|
+
if (isSafePositiveTicketNumber(record.issueNumber)) {
|
|
911
883
|
issueNumber = record.issueNumber;
|
|
912
884
|
}
|
|
913
885
|
if (typeof record.caseRunsPath === "string" && record.caseRunsPath.trim() !== "") {
|
|
@@ -915,9 +887,7 @@ async function loadResumableRunRecord(home, runId, authority) {
|
|
|
915
887
|
}
|
|
916
888
|
if (isRecord(record.caseIdentity)) {
|
|
917
889
|
const ci = record.caseIdentity;
|
|
918
|
-
if (
|
|
919
|
-
Number.isSafeInteger(ci.issueNumber) &&
|
|
920
|
-
ci.issueNumber >= 1 &&
|
|
890
|
+
if (isSafePositiveTicketNumber(ci.issueNumber) &&
|
|
921
891
|
typeof ci.runsPath === "string" &&
|
|
922
892
|
ci.runsPath.trim() !== "") {
|
|
923
893
|
caseIdentity = { issueNumber: ci.issueNumber, runsPath: ci.runsPath };
|
|
@@ -1013,9 +983,9 @@ async function loadResumableRunRecord(home, runId, authority) {
|
|
|
1013
983
|
throw new CliUsageError(`role run invocation identity is unreadable: ${runId}`, { cause: error });
|
|
1014
984
|
}
|
|
1015
985
|
}
|
|
1016
|
-
const sourceRunLeaf = basename(sourceRunPath ?? "")
|
|
1017
|
-
const referencedRunId = sourceRun?.runId ?? sourceRunLeaf
|
|
1018
|
-
const referencedRole = sourceRun?.role ?? sourceRunLeaf
|
|
986
|
+
const sourceRunLeaf = parseRunLeaf(basename(sourceRunPath ?? ""));
|
|
987
|
+
const referencedRunId = sourceRun?.runId ?? sourceRunLeaf?.runId;
|
|
988
|
+
const referencedRole = sourceRun?.role ?? sourceRunLeaf?.role;
|
|
1019
989
|
if (referencedRunId !== undefined && referencedRunId !== "") {
|
|
1020
990
|
const currentSourceRunDirectory = await findRunDirectoryById(home, referencedRunId, run.bookKey, referencedRole);
|
|
1021
991
|
if (currentSourceRunDirectory !== undefined) {
|
|
@@ -6,12 +6,13 @@
|
|
|
6
6
|
import { randomUUID } from "node:crypto";
|
|
7
7
|
import { dirname, join } from "node:path";
|
|
8
8
|
import { lstat, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
9
|
+
import { sessionFileOf } from "../role-run-placement.js";
|
|
9
10
|
import { writeHardenedArtifactFile } from "./auto-resume.js";
|
|
10
|
-
import { readAnalystGateCyclesFromAuditorRoles, } from "../analyst-gate-cycles-read.js";
|
|
11
11
|
import { sitianReport } from "../sitian-facade.js";
|
|
12
12
|
import { readAttemptScopedSubmissionRows, readRecordedSubmissionRows, readRecordedSubmissions, } from "../submission-ledger.js";
|
|
13
13
|
import { CONTROLLED_FAILURE_CAUSES } from "../host-contracts.js";
|
|
14
14
|
import { childSignalDeathDiagnostic, processCancelDiagnostic, } from "./process-cancel.js";
|
|
15
|
+
import { readStrictPiSessionJsonl } from "../ledger-session-read.js";
|
|
15
16
|
import { serializeThrownValue } from "../serialize-thrown-value.js";
|
|
16
17
|
import { describeErrorIdentity } from "./run-lifecycle.js";
|
|
17
18
|
import { readEngineDetourToolUsage, readInvocationEngineMounted, runDirectoryFromSessionDirectory, sessionFileFromSessionDirectory, withEngineDetourToolUsageFact, } from "../engine-detour-usage.js";
|
|
@@ -19,7 +20,7 @@ import { DOCTOR_CANDIDATE_ENTRY_TYPE } from "../dossier-resolution.js";
|
|
|
19
20
|
import { ensureRunArtifactsDir, homeFromRunDirectory, } from "./invocation.js";
|
|
20
21
|
import { SECRETARIAT_COUNTERSIGN_TERMINAL_FACT_KEY, SECRETARIAT_GATE_OFFICER_ENTRY_TYPE, } from "../secretariat-contracts.js";
|
|
21
22
|
import { findLatestDurablePackagedRoleTerminal, NAVIGATOR_ROUTE_PLAYBOOK_FAILURE_ENTRY, } from "../navigator-invocation-identity.js";
|
|
22
|
-
import { packagedDurableOfficerEntry, packagedRoleAcceptedOutputTool, packagedRoleMetadata,
|
|
23
|
+
import { packagedDurableOfficerEntry, packagedRoleAcceptedOutputTool, packagedRoleMetadata, } from "../packaged-role-registry.js";
|
|
23
24
|
import { NO_RECEIPT_LIFECYCLE_ENTRY_TYPE, noReceiptLifecycleFacts, parseNoReceiptLifecycleFacts, priorReceiptContinuation, receiptAttemptPointer, } from "../receipt-delivery-policy.js";
|
|
24
25
|
import { roleRunArtifactsDirectory } from "../role-run-placement.js";
|
|
25
26
|
import { listSeamOwnedUniqueErrorFacePaths, uniqueErrorFallbackName, UNIQUE_ERROR_FALLBACK_STEM, RUN_TERMINAL_ARTIFACT_FILES, RUN_TERMINAL_ERROR_FALLBACK_RELATIVE_PATHS, RUN_TERMINAL_REPORT_FILE, RUN_TERMINAL_ERROR_FILE, RUN_TERMINAL_EVIDENCE_FILE, RUN_TERMINAL_ERROR_SETTLEMENT_FILE, } from "../run-terminal-artifacts.js";
|
|
@@ -123,7 +124,7 @@ export async function noteSettlementFault(runDirectory, scope, diagnostic) {
|
|
|
123
124
|
export function ledgerReadScope(admitted, scope) {
|
|
124
125
|
return {
|
|
125
126
|
home: sealedLedgerHome(admitted),
|
|
126
|
-
sessionParent:
|
|
127
|
+
sessionParent: sessionFileOf(admitted.runDirectory),
|
|
127
128
|
...(scope?.courtAttemptId === undefined || scope.courtAttemptId.length === 0
|
|
128
129
|
? {}
|
|
129
130
|
: { attemptId: scope.courtAttemptId }),
|
|
@@ -297,12 +298,12 @@ async function settleNoReceiptTerminal(admitted, authority, scope, facts) {
|
|
|
297
298
|
}
|
|
298
299
|
if (scope?.previewOnly !== true)
|
|
299
300
|
await clearOppositeTerminalArtifactFace(admitted.runDirectory);
|
|
300
|
-
return
|
|
301
|
+
return attachEngineDetourToolUsage({
|
|
301
302
|
roleOutcome,
|
|
302
303
|
navigator: await extractNavigatorFactFromAdmittedSession(coordinates.sessionFile, admitted.runDirectory, scope),
|
|
303
304
|
artifacts: [],
|
|
304
305
|
runId: admitted.runId,
|
|
305
|
-
}, coordinates.sessionDirectory,
|
|
306
|
+
}, coordinates.sessionDirectory, detourUsageContext(admitted, scope));
|
|
306
307
|
}
|
|
307
308
|
/** Transitional host-session reads remain only for non-sealed failure and audit evidence. */
|
|
308
309
|
function coordinatesFromAdmitted(authority, admitted) {
|
|
@@ -638,12 +639,7 @@ export const NAVIGATOR_POST_ROLE_GRACE_MS = 10_000;
|
|
|
638
639
|
* terminal note it; they do not relabel it as a host session cause.
|
|
639
640
|
*/
|
|
640
641
|
export async function readBoundSessionEntries(sessionFile) {
|
|
641
|
-
|
|
642
|
-
const entries = [];
|
|
643
|
-
for (const line of text.trim().split("\n").filter(Boolean)) {
|
|
644
|
-
entries.push(JSON.parse(line));
|
|
645
|
-
}
|
|
646
|
-
return entries;
|
|
642
|
+
return await readStrictPiSessionJsonl(sessionFile);
|
|
647
643
|
}
|
|
648
644
|
/**
|
|
649
645
|
* #419 per-attempt process history. 史必追加,指针可覆盖;指针可以覆盖的前提是史已落。
|
|
@@ -737,61 +733,6 @@ function parseNavigatorAttendanceDetails(details) {
|
|
|
737
733
|
reason: "Navigator attendance disposition is unparseable",
|
|
738
734
|
};
|
|
739
735
|
}
|
|
740
|
-
/**
|
|
741
|
-
* Project direct and historical paired gate rounds onto the public Terminal.
|
|
742
|
-
* actualSeats derive only from accepted receipts, never expected/missing seats.
|
|
743
|
-
*/
|
|
744
|
-
export function projectTerminalGateFact(rounds) {
|
|
745
|
-
if (rounds.length === 0)
|
|
746
|
-
return undefined;
|
|
747
|
-
const seen = new Set();
|
|
748
|
-
for (const round of rounds) {
|
|
749
|
-
if (round.origin.kind === "historical_dispatch")
|
|
750
|
-
seen.add("gatekeeper");
|
|
751
|
-
seen.add(round.officer);
|
|
752
|
-
}
|
|
753
|
-
const actualSeats = ["gatekeeper", "inspector", "notary"].filter((seat) => seen.has(seat));
|
|
754
|
-
return {
|
|
755
|
-
actualSeats,
|
|
756
|
-
rounds: rounds.map((round) => ({
|
|
757
|
-
roundIndex: round.roundIndex,
|
|
758
|
-
dispatch: round.origin.kind === "direct"
|
|
759
|
-
? { kind: "direct", officer: round.officer }
|
|
760
|
-
: {
|
|
761
|
-
kind: "historical_dispatch",
|
|
762
|
-
officer: round.officer,
|
|
763
|
-
...(round.origin.reason === undefined
|
|
764
|
-
? {}
|
|
765
|
-
: { reason: round.origin.reason }),
|
|
766
|
-
},
|
|
767
|
-
officer: {
|
|
768
|
-
seat: round.officer,
|
|
769
|
-
status: round.status,
|
|
770
|
-
findings: round.findings,
|
|
771
|
-
},
|
|
772
|
-
})),
|
|
773
|
-
};
|
|
774
|
-
}
|
|
775
|
-
/**
|
|
776
|
-
* Read gate facts from the run's session/auditor-roles nest (#446/#478).
|
|
777
|
-
* Missing directories → undefined (no-gate zero change).
|
|
778
|
-
* Damaged discovered volumes propagate — never wash to "no gate".
|
|
779
|
-
*/
|
|
780
|
-
export async function extractGateFactFromSessionDirectory(sessionDirectory, options = {}) {
|
|
781
|
-
const directories = [join(sessionDirectory, "auditor-roles")];
|
|
782
|
-
const parentSessionFile = options.parentSessionFile ?? join(sessionDirectory, "session.jsonl");
|
|
783
|
-
const rounds = await readAnalystGateCyclesFromAuditorRoles(directories, {
|
|
784
|
-
parentSessionFile,
|
|
785
|
-
});
|
|
786
|
-
return projectTerminalGateFact(rounds);
|
|
787
|
-
}
|
|
788
|
-
/**
|
|
789
|
-
* Attach optional gate projection onto a settled Terminal base.
|
|
790
|
-
* Shared by every settle path so auditor-roles is scanned once here only.
|
|
791
|
-
* `runId` is optional only for a batch projection that has no parent run.
|
|
792
|
-
* Gate read damage is a package note beside the host terminal. It does not
|
|
793
|
-
* replace that terminal and it does not omit the gate without a trace.
|
|
794
|
-
*/
|
|
795
736
|
/**
|
|
796
737
|
* #537: project this-invocation ak_engine_detour usage onto decisiveFacts.
|
|
797
738
|
* Absent when engine is not mounted; callCount 0 when mounted with zero calls.
|
|
@@ -800,27 +741,27 @@ export async function extractGateFactFromSessionDirectory(sessionDirectory, opti
|
|
|
800
741
|
* courtAttemptId and never Pi session.jsonl toolResult join. Session damage
|
|
801
742
|
* therefore cannot replace an already-formed roleOutcome (no_receipt / failure).
|
|
802
743
|
*/
|
|
803
|
-
async function attachEngineDetourToolUsage(base, sessionDirectory,
|
|
804
|
-
const runDirectory = typeof
|
|
805
|
-
?
|
|
744
|
+
async function attachEngineDetourToolUsage(base, sessionDirectory, detourContext = {}) {
|
|
745
|
+
const runDirectory = typeof detourContext.runDirectory === "string" && detourContext.runDirectory.length > 0
|
|
746
|
+
? detourContext.runDirectory
|
|
806
747
|
: runDirectoryFromSessionDirectory(sessionDirectory);
|
|
807
748
|
let engineMounted = false;
|
|
808
749
|
try {
|
|
809
750
|
engineMounted = await readInvocationEngineMounted(runDirectory);
|
|
810
751
|
}
|
|
811
752
|
catch (error) {
|
|
812
|
-
await noteSettlementFault(runDirectory,
|
|
753
|
+
await noteSettlementFault(runDirectory, detourContext.notePackageFault === undefined
|
|
813
754
|
? undefined
|
|
814
|
-
: { notePackageFault:
|
|
755
|
+
: { notePackageFault: detourContext.notePackageFault }, `engine mount read failed beside host terminal: ${describeErrorIdentity(error)}`);
|
|
815
756
|
return base;
|
|
816
757
|
}
|
|
817
758
|
if (!engineMounted)
|
|
818
759
|
return base;
|
|
819
760
|
// Public-invocation scope from the shared Host envelope (settlement scope), never
|
|
820
761
|
// courtAttemptId and never a detour sidecar file.
|
|
821
|
-
const invocationScopeId = typeof
|
|
822
|
-
|
|
823
|
-
?
|
|
762
|
+
const invocationScopeId = typeof detourContext.invocationScopeId === "string" &&
|
|
763
|
+
detourContext.invocationScopeId.length > 0
|
|
764
|
+
? detourContext.invocationScopeId
|
|
824
765
|
: undefined;
|
|
825
766
|
const sessionFile = sessionFileFromSessionDirectory(sessionDirectory);
|
|
826
767
|
let usage;
|
|
@@ -833,9 +774,9 @@ async function attachEngineDetourToolUsage(base, sessionDirectory, gateContext =
|
|
|
833
774
|
});
|
|
834
775
|
}
|
|
835
776
|
catch (error) {
|
|
836
|
-
await noteSettlementFault(runDirectory,
|
|
777
|
+
await noteSettlementFault(runDirectory, detourContext.notePackageFault === undefined
|
|
837
778
|
? undefined
|
|
838
|
-
: { notePackageFault:
|
|
779
|
+
: { notePackageFault: detourContext.notePackageFault }, `engine detour read failed beside host terminal: ${describeErrorIdentity(error)}`);
|
|
839
780
|
return base;
|
|
840
781
|
}
|
|
841
782
|
return {
|
|
@@ -843,45 +784,16 @@ async function attachEngineDetourToolUsage(base, sessionDirectory, gateContext =
|
|
|
843
784
|
roleOutcome: withEngineDetourToolUsageFact(base.roleOutcome, usage),
|
|
844
785
|
};
|
|
845
786
|
}
|
|
846
|
-
/**
|
|
847
|
-
function
|
|
787
|
+
/** Engine-detour usage context from admitted run + settlement scope. */
|
|
788
|
+
function detourUsageContext(admitted, scope) {
|
|
848
789
|
return {
|
|
849
790
|
runDirectory: admitted.runDirectory,
|
|
850
|
-
...(scope?.courtAttemptId === undefined || scope.courtAttemptId.length === 0
|
|
851
|
-
? {}
|
|
852
|
-
: { courtAttemptId: scope.courtAttemptId }),
|
|
853
791
|
...(scope?.invocationScopeId === undefined || scope.invocationScopeId.length === 0
|
|
854
792
|
? {}
|
|
855
793
|
: { invocationScopeId: scope.invocationScopeId }),
|
|
856
794
|
...(scope?.notePackageFault === undefined ? {} : { notePackageFault: scope.notePackageFault }),
|
|
857
795
|
};
|
|
858
796
|
}
|
|
859
|
-
async function withOptionalGateProjection(base, sessionDirectory, gateContext = {}) {
|
|
860
|
-
// A gate transport failure is already represented by typed evidence and has no
|
|
861
|
-
// accepted gate cycle to project. Re-reading that rejected receipt as an
|
|
862
|
-
// accepted cycle would replace the original failure with a projection error.
|
|
863
|
-
const secondaryEvidence = base.roleOutcome.kind === "failure"
|
|
864
|
-
? base.roleOutcome.decisiveFacts.secondaryEvidence
|
|
865
|
-
: undefined;
|
|
866
|
-
const skipGate = isRecord(secondaryEvidence)
|
|
867
|
-
&& secondaryEvidence.kind === "role_infrastructure_failure"
|
|
868
|
-
&& packagedSkipsGateOnInfrastructureStage(secondaryEvidence.stage);
|
|
869
|
-
let next = base;
|
|
870
|
-
if (!skipGate) {
|
|
871
|
-
try {
|
|
872
|
-
const gate = await extractGateFactFromSessionDirectory(sessionDirectory, gateContext);
|
|
873
|
-
if (gate !== undefined)
|
|
874
|
-
next = { ...base, gate };
|
|
875
|
-
}
|
|
876
|
-
catch (error) {
|
|
877
|
-
const runDirectory = gateContext.runDirectory ?? runDirectoryFromSessionDirectory(sessionDirectory);
|
|
878
|
-
await noteSettlementFault(runDirectory, gateContext.notePackageFault === undefined
|
|
879
|
-
? undefined
|
|
880
|
-
: { notePackageFault: gateContext.notePackageFault }, `gate read failed beside host terminal: ${describeErrorIdentity(error)}`);
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
return attachEngineDetourToolUsage(next, sessionDirectory, gateContext);
|
|
884
|
-
}
|
|
885
797
|
function routePlaybookFailureMessage(entries) {
|
|
886
798
|
for (let i = entries.length - 1; i >= 0; i -= 1) {
|
|
887
799
|
const entry = entries[i];
|
|
@@ -1071,12 +983,12 @@ async function finishLawfulSeat(admitted, coordinates, entries, roleOutcome, sco
|
|
|
1071
983
|
const artifacts = scope?.previewOnly === true
|
|
1072
984
|
? []
|
|
1073
985
|
: await publishDeclaredSeatArtifacts(admitted, roleOutcome, coordinates, entries, scope?.recordAttemptHistory === true);
|
|
1074
|
-
const terminal = await
|
|
986
|
+
const terminal = await attachEngineDetourToolUsage({
|
|
1075
987
|
roleOutcome,
|
|
1076
988
|
navigator: extractNavigatorFact(entries),
|
|
1077
989
|
artifacts,
|
|
1078
990
|
runId: admitted.runId,
|
|
1079
|
-
}, coordinates.sessionDirectory,
|
|
991
|
+
}, coordinates.sessionDirectory, detourUsageContext(admitted, scope));
|
|
1080
992
|
return attachRecordedSubmissions(admitted, terminal, scope);
|
|
1081
993
|
}
|
|
1082
994
|
/**
|
|
@@ -1317,26 +1229,14 @@ async function applySecretariatCountersignTerminal(admitted, authority, settled,
|
|
|
1317
1229
|
return settled;
|
|
1318
1230
|
}
|
|
1319
1231
|
/**
|
|
1320
|
-
* After the audit gate returns, attach
|
|
1321
|
-
*
|
|
1322
|
-
*
|
|
1232
|
+
* After the audit gate returns, attach the secretariat officer fact onto the
|
|
1233
|
+
* terminal this turn already settled. Does not publish again — a second publish
|
|
1234
|
+
* would append another attempt-history row for the same attempt.
|
|
1323
1235
|
*/
|
|
1324
|
-
export async function
|
|
1325
|
-
const { sessionDirectory, sessionFile } = coordinatesFromAdmitted(authority, admitted);
|
|
1326
|
-
let gate;
|
|
1327
|
-
try {
|
|
1328
|
-
gate = await extractGateFactFromSessionDirectory(sessionDirectory, {
|
|
1329
|
-
runDirectory: admitted.runDirectory,
|
|
1330
|
-
parentSessionFile: sessionFile,
|
|
1331
|
-
});
|
|
1332
|
-
}
|
|
1333
|
-
catch (error) {
|
|
1334
|
-
await noteSettlementFault(admitted.runDirectory, scope, `gate read failed beside host terminal: ${describeErrorIdentity(error)}`);
|
|
1335
|
-
}
|
|
1336
|
-
const withGate = gate === undefined ? terminal : { ...terminal, gate };
|
|
1236
|
+
export async function attachPostAuditCountersignFact(admitted, authority, terminal, scope) {
|
|
1337
1237
|
if (admitted.role !== "secretariat")
|
|
1338
|
-
return
|
|
1339
|
-
return applySecretariatCountersignTerminal(admitted, authority,
|
|
1238
|
+
return terminal;
|
|
1239
|
+
return applySecretariatCountersignTerminal(admitted, authority, terminal, scope);
|
|
1340
1240
|
}
|
|
1341
1241
|
function publicationAttemptFromError(path, error) {
|
|
1342
1242
|
if (error instanceof Error) {
|
|
@@ -1538,7 +1438,7 @@ export async function settleFailureTerminalResult(admitted, failure, authority,
|
|
|
1538
1438
|
diagnostic: failure.diagnostic,
|
|
1539
1439
|
decisiveFacts,
|
|
1540
1440
|
};
|
|
1541
|
-
return
|
|
1441
|
+
return attachEngineDetourToolUsage({ roleOutcome, navigator, artifacts, runId: admitted.runId }, sessionDirectory, detourUsageContext(admitted, options));
|
|
1542
1442
|
}
|
|
1543
1443
|
/**
|
|
1544
1444
|
* Emit one complete failure Terminal on stdout and one concise stderr diagnostic.
|
|
@@ -85,18 +85,6 @@ export function formatTerminalResult(result) {
|
|
|
85
85
|
for (const artifact of result.artifacts) {
|
|
86
86
|
lines.push(`artifact\t${artifact.kind}\t${encodeTerminalField(artifact.path)}`);
|
|
87
87
|
}
|
|
88
|
-
// Gate region is unfrozen presentation of typed facts (ADR 0052) — machines
|
|
89
|
-
// read result.gate, never these row labels.
|
|
90
|
-
if (result.gate !== undefined) {
|
|
91
|
-
lines.push(`gate\t${encodeTerminalField(result.gate.actualSeats.join(","))}\t${result.gate.rounds.length}`);
|
|
92
|
-
for (const round of result.gate.rounds) {
|
|
93
|
-
const reason = round.dispatch.kind === "historical_dispatch"
|
|
94
|
-
&& round.dispatch.reason !== undefined
|
|
95
|
-
? encodeTerminalField(round.dispatch.reason)
|
|
96
|
-
: "";
|
|
97
|
-
lines.push(`gate-round\t${round.roundIndex}\t${round.dispatch.kind}\t${round.dispatch.officer}\t${reason}\t${round.officer.seat}\t${encodeTerminalField(round.officer.status)}\t${encodeTerminalField(JSON.stringify(round.officer.findings))}`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
88
|
if (result.reviewerChildOutcomes !== undefined) {
|
|
101
89
|
for (const axis of ["completeness", "correctness"]) {
|
|
102
90
|
const child = result.reviewerChildOutcomes[axis];
|
|
@@ -1,11 +1,35 @@
|
|
|
1
1
|
import { readdir } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
2
|
+
import { basename, dirname, join } from "node:path";
|
|
3
3
|
import {
|
|
4
4
|
activationBookDirectory,
|
|
5
5
|
ensureRealDirectoryTree
|
|
6
6
|
} from "./activation-ledger-topology.js";
|
|
7
7
|
import { requireSafePositiveTicketNumber } from "./run-ticket-number.js";
|
|
8
8
|
import { isEnoent } from "./unknown-value.js";
|
|
9
|
+
function formatRunLeaf(runId, role) {
|
|
10
|
+
return `${runId}@${role}`;
|
|
11
|
+
}
|
|
12
|
+
function parseRunLeaf(name) {
|
|
13
|
+
const at = name.lastIndexOf("@");
|
|
14
|
+
if (at <= 0 || at === name.length - 1) return void 0;
|
|
15
|
+
return { runId: name.slice(0, at), role: name.slice(at + 1) };
|
|
16
|
+
}
|
|
17
|
+
function sessionDirectoryOf(runDirectory) {
|
|
18
|
+
return join(runDirectory, "session");
|
|
19
|
+
}
|
|
20
|
+
const ROLE_RUN_SESSION_FILENAME = "session.jsonl";
|
|
21
|
+
function sessionFileIn(sessionDirectory) {
|
|
22
|
+
return join(sessionDirectory, ROLE_RUN_SESSION_FILENAME);
|
|
23
|
+
}
|
|
24
|
+
function sessionFileOf(runDirectory) {
|
|
25
|
+
return sessionFileIn(sessionDirectoryOf(runDirectory));
|
|
26
|
+
}
|
|
27
|
+
function runDirectoryOfSessionFile(sessionFile) {
|
|
28
|
+
return dirname(dirname(sessionFile));
|
|
29
|
+
}
|
|
30
|
+
function runDirectoryFromSessionDirectory(sessionDirectory) {
|
|
31
|
+
return dirname(sessionDirectory);
|
|
32
|
+
}
|
|
9
33
|
async function listBookRunDirectories(bookDir) {
|
|
10
34
|
const out = [];
|
|
11
35
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -39,6 +63,20 @@ async function listBookRunDirectories(bookDir) {
|
|
|
39
63
|
}
|
|
40
64
|
return out.sort();
|
|
41
65
|
}
|
|
66
|
+
async function findRoleRunDirectory(bookDirectories, runId, onlyRole) {
|
|
67
|
+
const matches = [];
|
|
68
|
+
for (const bookDirectory of bookDirectories) {
|
|
69
|
+
for (const runDirectory of await listBookRunDirectories(bookDirectory)) {
|
|
70
|
+
const parsed = parseRunLeaf(basename(runDirectory));
|
|
71
|
+
if (parsed === void 0 || parsed.runId !== runId) continue;
|
|
72
|
+
if (onlyRole !== void 0 && parsed.role !== onlyRole) continue;
|
|
73
|
+
matches.push(runDirectory);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (matches.length === 0) return void 0;
|
|
77
|
+
if (matches.length === 1) return matches[0];
|
|
78
|
+
throw new Error(`ambiguous role run id ${runId}: ${matches.join(", ")}`);
|
|
79
|
+
}
|
|
42
80
|
function roleRunPlacement(ledgerHome, input) {
|
|
43
81
|
const subjectDirectory = "ticketNumber" in input.subject ? String(requireSafePositiveTicketNumber(
|
|
44
82
|
input.subject.ticketNumber,
|
|
@@ -48,13 +86,13 @@ function roleRunPlacement(ledgerHome, input) {
|
|
|
48
86
|
activationBookDirectory(ledgerHome, input.bookKey),
|
|
49
87
|
subjectDirectory,
|
|
50
88
|
"runs",
|
|
51
|
-
|
|
89
|
+
formatRunLeaf(input.runId, input.role)
|
|
52
90
|
);
|
|
53
|
-
const sessionDirectory =
|
|
91
|
+
const sessionDirectory = sessionDirectoryOf(runDirectory);
|
|
54
92
|
return {
|
|
55
93
|
runDirectory,
|
|
56
94
|
sessionDirectory,
|
|
57
|
-
sessionFile:
|
|
95
|
+
sessionFile: sessionFileIn(sessionDirectory),
|
|
58
96
|
artifactsDirectory: roleRunArtifactsDirectory(runDirectory),
|
|
59
97
|
attachmentsDirectory: join(runDirectory, "attachments")
|
|
60
98
|
};
|
|
@@ -77,10 +115,19 @@ function ensureRoleRunPlacement(ledgerHome, placement) {
|
|
|
77
115
|
}
|
|
78
116
|
}
|
|
79
117
|
export {
|
|
118
|
+
ROLE_RUN_SESSION_FILENAME,
|
|
80
119
|
ensureRoleRunDirectory,
|
|
81
120
|
ensureRoleRunPlacement,
|
|
121
|
+
findRoleRunDirectory,
|
|
122
|
+
formatRunLeaf,
|
|
82
123
|
isUnboundRunDirectory,
|
|
83
124
|
listBookRunDirectories,
|
|
125
|
+
parseRunLeaf,
|
|
84
126
|
roleRunArtifactsDirectory,
|
|
85
|
-
roleRunPlacement
|
|
127
|
+
roleRunPlacement,
|
|
128
|
+
runDirectoryFromSessionDirectory,
|
|
129
|
+
runDirectoryOfSessionFile,
|
|
130
|
+
sessionDirectoryOf,
|
|
131
|
+
sessionFileIn,
|
|
132
|
+
sessionFileOf
|
|
86
133
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Single authority for rewriting durable run pages after a run directory moves.
|
|
3
|
-
* Live
|
|
3
|
+
* Live callers project typed fields; only historical migrators walk durable pages.
|
|
4
4
|
*
|
|
5
5
|
* Scope is typed machine-consumed path fields only — never free text, never
|
|
6
6
|
* frozen attachment/artifact bytes. Nested walk is confined to package-owned
|
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { existsSync } from "node:fs";
|
|
10
10
|
import { readdir, readFile, writeFile } from "node:fs/promises";
|
|
11
|
-
import { join, sep } from "node:path";
|
|
11
|
+
import { dirname, join, sep } from "node:path";
|
|
12
|
+
import { activationBookDirectory, physicalPathIdentity } from "./activation-ledger-topology.js";
|
|
13
|
+
import { readRoleRunIdentity } from "./public-cli/run-lifecycle.js";
|
|
14
|
+
import { findRoleRunDirectory, roleRunPlacement } from "./role-run-placement.js";
|
|
12
15
|
import { AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE } from "./compliance-transport.js";
|
|
13
16
|
import { isRecord, isEnoent } from "./unknown-value.js";
|
|
14
17
|
const ADMITTED_PAGE_FIELDS = [
|
|
@@ -52,6 +55,40 @@ export function rewriteRunDirectoryPathValue(value, oldRunDirectory, newRunDirec
|
|
|
52
55
|
}
|
|
53
56
|
return value;
|
|
54
57
|
}
|
|
58
|
+
/** Match physical aliases or a normal unbound→ticket bind, never historical migration layouts. */
|
|
59
|
+
export async function retainedRunPathsMatch(recordedPath, currentPath, currentRunDirectory) {
|
|
60
|
+
if (recordedPath === currentPath)
|
|
61
|
+
return true;
|
|
62
|
+
if (typeof recordedPath !== "string")
|
|
63
|
+
return false;
|
|
64
|
+
const recordedPathIdentity = physicalPathIdentity(recordedPath);
|
|
65
|
+
const currentPathIdentity = physicalPathIdentity(currentPath);
|
|
66
|
+
if (recordedPathIdentity === currentPathIdentity)
|
|
67
|
+
return true;
|
|
68
|
+
if (currentRunDirectory === undefined)
|
|
69
|
+
return false;
|
|
70
|
+
const runDirectory = physicalPathIdentity(currentRunDirectory);
|
|
71
|
+
const identity = await readRoleRunIdentity(runDirectory);
|
|
72
|
+
if (identity === undefined)
|
|
73
|
+
return false;
|
|
74
|
+
for (let directory = runDirectory; dirname(directory) !== directory; directory = dirname(directory)) {
|
|
75
|
+
const ledgerHome = dirname(dirname(directory));
|
|
76
|
+
if (directory !== activationBookDirectory(ledgerHome, identity.bookKey))
|
|
77
|
+
continue;
|
|
78
|
+
const unbound = roleRunPlacement(ledgerHome, {
|
|
79
|
+
bookKey: identity.bookKey,
|
|
80
|
+
subject: { unbound: true },
|
|
81
|
+
runId: identity.runId,
|
|
82
|
+
role: identity.role,
|
|
83
|
+
});
|
|
84
|
+
if (rewriteRunDirectoryPathValue(recordedPathIdentity, unbound.runDirectory, runDirectory) !== currentPathIdentity) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
const placed = await findRoleRunDirectory([directory], identity.runId, identity.role);
|
|
88
|
+
return placed !== undefined && physicalPathIdentity(placed) === runDirectory;
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
55
92
|
/**
|
|
56
93
|
* Apply the longest matching oldRunDirectory rewrite. Longer keys win so a
|
|
57
94
|
* nested run path is not partially claimed by a shorter sibling prefix.
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { randomUUID } from "node:crypto";
|
|
10
10
|
import { readdir, readFile } from "node:fs/promises";
|
|
11
11
|
import { basename, dirname, join } from "node:path";
|
|
12
|
-
import { roleRunArtifactsDirectory } from "./role-run-placement.js";
|
|
12
|
+
import { parseRunLeaf, roleRunArtifactsDirectory } from "./role-run-placement.js";
|
|
13
13
|
import { isRecord, errorText, isMissingPathError } from "./unknown-value.js";
|
|
14
14
|
export const RUN_TERMINAL_REPORT_FILE = "report.json";
|
|
15
15
|
export const RUN_TERMINAL_ERROR_FILE = "error.json";
|
|
@@ -126,11 +126,7 @@ async function listUniqueErrorFallbackPaths(directories) {
|
|
|
126
126
|
* Sole authority for runDirectory → runId (last `@` split).
|
|
127
127
|
*/
|
|
128
128
|
export function runIdFromRunDirectory(runDirectory) {
|
|
129
|
-
|
|
130
|
-
const at = name.lastIndexOf("@");
|
|
131
|
-
if (at <= 0 || at === name.length - 1)
|
|
132
|
-
return undefined;
|
|
133
|
-
return name.slice(0, at);
|
|
129
|
+
return parseRunLeaf(basename(runDirectory))?.runId;
|
|
134
130
|
}
|
|
135
131
|
/**
|
|
136
132
|
* Shared parent-directory unique fallback may be adopted only when the
|
|
@@ -2,8 +2,33 @@ import { readFile } from "node:fs/promises";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { isEnoent, isRecord } from "./unknown-value.js";
|
|
4
4
|
const MIGRATION_TICKET_DERIVATION_PAGE = "migration-ticket-derivation.json";
|
|
5
|
+
function isPositiveTicketNumber(value) {
|
|
6
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 1;
|
|
7
|
+
}
|
|
5
8
|
function isSafePositiveTicketNumber(value) {
|
|
6
|
-
return
|
|
9
|
+
return isPositiveTicketNumber(value) && Number.isSafeInteger(value);
|
|
10
|
+
}
|
|
11
|
+
function isTicketNumberString(value) {
|
|
12
|
+
return typeof value === "string" && /^[1-9][0-9]*$/.test(value);
|
|
13
|
+
}
|
|
14
|
+
function parseTicketNumber(value) {
|
|
15
|
+
if (isSafePositiveTicketNumber(value)) return value;
|
|
16
|
+
if (!isTicketNumberString(value)) return void 0;
|
|
17
|
+
const parsed = Number(value);
|
|
18
|
+
return isSafePositiveTicketNumber(parsed) ? parsed : void 0;
|
|
19
|
+
}
|
|
20
|
+
function sitianSubjectTicketNumber(subject) {
|
|
21
|
+
if (typeof subject === "string" || typeof subject === "number") {
|
|
22
|
+
return parseTicketNumber(subject);
|
|
23
|
+
}
|
|
24
|
+
if (subject !== null && typeof subject === "object" && !Array.isArray(subject)) {
|
|
25
|
+
return parseTicketNumber(subject.ticketNumber);
|
|
26
|
+
}
|
|
27
|
+
return void 0;
|
|
28
|
+
}
|
|
29
|
+
function ticketNumberFromSitianSubject(subject) {
|
|
30
|
+
const parsed = sitianSubjectTicketNumber(subject);
|
|
31
|
+
return parsed === void 0 ? void 0 : String(parsed);
|
|
7
32
|
}
|
|
8
33
|
function readDeclaredTicketNumber(value) {
|
|
9
34
|
if (isSafePositiveTicketNumber(value)) return value;
|
|
@@ -11,8 +36,7 @@ function readDeclaredTicketNumber(value) {
|
|
|
11
36
|
const trimmed = value.trim();
|
|
12
37
|
const match = /^#?([1-9]\d*)(?!\.\d)(?:\b|$)/.exec(trimmed);
|
|
13
38
|
if (match === null) return void 0;
|
|
14
|
-
|
|
15
|
-
return isSafePositiveTicketNumber(ticketNumber) ? ticketNumber : void 0;
|
|
39
|
+
return parseTicketNumber(match[1]);
|
|
16
40
|
}
|
|
17
41
|
function requireSafePositiveTicketNumber(ticketNumber, context = "ticketNumber") {
|
|
18
42
|
if (!isSafePositiveTicketNumber(ticketNumber)) {
|
|
@@ -58,10 +82,15 @@ async function readRunTicketNumber(runDirectory) {
|
|
|
58
82
|
}
|
|
59
83
|
export {
|
|
60
84
|
MIGRATION_TICKET_DERIVATION_PAGE,
|
|
85
|
+
isPositiveTicketNumber,
|
|
61
86
|
isSafePositiveTicketNumber,
|
|
87
|
+
isTicketNumberString,
|
|
88
|
+
parseTicketNumber,
|
|
62
89
|
readBoardTicketNumber,
|
|
63
90
|
readDeclaredTicketNumber,
|
|
64
91
|
readMigrationDerivedTicketNumber,
|
|
65
92
|
readRunTicketNumber,
|
|
66
|
-
requireSafePositiveTicketNumber
|
|
93
|
+
requireSafePositiveTicketNumber,
|
|
94
|
+
sitianSubjectTicketNumber,
|
|
95
|
+
ticketNumberFromSitianSubject
|
|
67
96
|
};
|