@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
package/src/ticket-trajectory.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Station resolution (four-layer fallback):
|
|
8
8
|
* 1) terminating tool name inside the session (ak_<role>_output)
|
|
9
|
-
* 2)
|
|
9
|
+
* 2) current.json invocation role
|
|
10
10
|
* 3) run-directory name heuristic
|
|
11
11
|
* 4) unknown (still listed; never dropped)
|
|
12
12
|
*
|
|
@@ -18,19 +18,27 @@
|
|
|
18
18
|
* write does not advertise refresh. Regeneration faults surface the original
|
|
19
19
|
* cause via handle.closed / stop(). Caller stops the handle.
|
|
20
20
|
*/
|
|
21
|
-
import {
|
|
22
|
-
import { lstat, mkdir, readdir, readFile, realpath
|
|
21
|
+
import { readSectionSync } from "./run-dossier.ts";
|
|
22
|
+
import { lstat, mkdir, readdir, readFile, realpath } from "node:fs/promises";
|
|
23
23
|
import { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
24
24
|
import { pathToFileURL } from "node:url";
|
|
25
25
|
|
|
26
|
+
import { writeFileAtomically } from "./atomic-write.ts";
|
|
27
|
+
import {
|
|
28
|
+
errnoCode,
|
|
29
|
+
pathContainedIn,
|
|
30
|
+
physicalPathIdentity,
|
|
31
|
+
} from "./activation-ledger-topology.ts";
|
|
32
|
+
|
|
26
33
|
import {
|
|
27
34
|
formatDurationZh,
|
|
28
35
|
formatLocalDateTime,
|
|
29
36
|
formatTokensCompact,
|
|
30
37
|
formatUsdPrecise,
|
|
31
38
|
} from "./human-format.ts";
|
|
32
|
-
import { listBookRunDirectories } from "./role-run-placement.ts";
|
|
33
|
-
import {
|
|
39
|
+
import { listBookRunDirectories, sessionDirectoryOf } from "./role-run-placement.ts";
|
|
40
|
+
import { sitianVolumeDirectory } from "./sitian-appender.ts";
|
|
41
|
+
import { isPositiveTicketNumber, readRunTicketNumber } from "./run-ticket-number.ts";
|
|
34
42
|
import {
|
|
35
43
|
extractSessionTimestampSpan,
|
|
36
44
|
readLedgerSessionJsonl,
|
|
@@ -279,9 +287,8 @@ type InvocationInfo = {
|
|
|
279
287
|
|
|
280
288
|
async function readInvocation(runDir: string): Promise<InvocationInfo | undefined> {
|
|
281
289
|
try {
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
if (!isRecord(parsed)) return undefined;
|
|
290
|
+
const parsed = readSectionSync(runDir, "invocation");
|
|
291
|
+
if (parsed === undefined) return undefined;
|
|
285
292
|
const info: InvocationInfo = {};
|
|
286
293
|
if (typeof parsed.role === "string" && parsed.role.trim()) info.role = parsed.role.trim();
|
|
287
294
|
if (typeof parsed.thinking === "string" && parsed.thinking.trim()) info.thinking = parsed.thinking.trim();
|
|
@@ -350,7 +357,7 @@ async function listSessionFiles(sessionDir: string): Promise<string[]> {
|
|
|
350
357
|
|
|
351
358
|
/** Reviewer parallel axis-leg sessions live under session/reviewer-legs/. */
|
|
352
359
|
async function listAxisLegSessionFiles(sessionDir: string): Promise<string[]> {
|
|
353
|
-
return listSessionFiles(
|
|
360
|
+
return listSessionFiles(sitianVolumeDirectory(sessionDir, "reviewer-legs"));
|
|
354
361
|
}
|
|
355
362
|
|
|
356
363
|
async function maxMtimeMs(paths: readonly string[]): Promise<number> {
|
|
@@ -372,7 +379,7 @@ async function parseRunDirectory(runDir: string, ledgerCoord: string): Promise<P
|
|
|
372
379
|
const runId = basename(runDir);
|
|
373
380
|
const evidenceTarget = await realpathOrLexicalIfMissing(runDir);
|
|
374
381
|
const evidenceHref = pathToFileURL(evidenceTarget).href;
|
|
375
|
-
const sessionDir =
|
|
382
|
+
const sessionDir = sessionDirectoryOf(runDir);
|
|
376
383
|
const sessionFiles = await listSessionFiles(sessionDir);
|
|
377
384
|
const axisLegFiles = await listAxisLegSessionFiles(sessionDir);
|
|
378
385
|
const rows: SessionRow[] = [];
|
|
@@ -425,7 +432,7 @@ async function parseRunDirectory(runDir: string, ledgerCoord: string): Promise<P
|
|
|
425
432
|
...(invocation?.role !== undefined ? { invocationRole: invocation.role } : {}),
|
|
426
433
|
});
|
|
427
434
|
|
|
428
|
-
// Session mechanical fields win; invocation
|
|
435
|
+
// Session mechanical fields win; the invocation section fills gaps only.
|
|
429
436
|
let { model, provider, thinking } = models;
|
|
430
437
|
if (invocation) {
|
|
431
438
|
if (!thinking && invocation.thinking) thinking = invocation.thinking;
|
|
@@ -653,12 +660,12 @@ ${stationBlocks || "<p data-empty=\"true\">no runs</p>"}
|
|
|
653
660
|
* Factory board reuses this — no parallel receipt parser.
|
|
654
661
|
*
|
|
655
662
|
* Book-run attribution (#176 / #859): read typed `ticketNumber` from each run's
|
|
656
|
-
*
|
|
663
|
+
* current.json invocation section under listBookRunDirectories (flat legacy `runs/` + subject-tree
|
|
657
664
|
* `<ticket|unbound>/runs/`). Legacy `issues/<N>/runs` remains a read-only
|
|
658
665
|
* compatibility entrance. Unbound runs never join a ticket.
|
|
659
666
|
*/
|
|
660
667
|
|
|
661
|
-
/** One book run's typed binding projection from invocation.
|
|
668
|
+
/** One book run's typed binding projection from the invocation section. */
|
|
662
669
|
export type FlatRunTicketBinding = {
|
|
663
670
|
readonly runDir: string;
|
|
664
671
|
readonly runFolder: string;
|
|
@@ -671,10 +678,10 @@ export type FlatRunTicketBinding = {
|
|
|
671
678
|
|
|
672
679
|
/**
|
|
673
680
|
* Book-level run index built once per lane/render and reused per ticket.
|
|
674
|
-
* Pure derived view of book runs'
|
|
681
|
+
* Pure derived view of book runs' current.json invocation sections — not a parallel ledger.
|
|
675
682
|
*/
|
|
676
683
|
export type TicketTrajectoryBookIndex = {
|
|
677
|
-
/** ticket number → runs bound via invocation
|
|
684
|
+
/** ticket number → runs bound via invocation ticketNumber. */
|
|
678
685
|
readonly runsByTicket: ReadonlyMap<number, readonly FlatRunTicketBinding[]>;
|
|
679
686
|
/** Runs with no typed ticketNumber (unknown/unbound seam). */
|
|
680
687
|
readonly unboundRuns: readonly FlatRunTicketBinding[];
|
|
@@ -749,7 +756,7 @@ export async function loadTicketTrajectoryRuns(
|
|
|
749
756
|
issueNumber: number,
|
|
750
757
|
bookIndex?: TicketTrajectoryBookIndex,
|
|
751
758
|
): Promise<TicketTrajectoryRun[]> {
|
|
752
|
-
if (!
|
|
759
|
+
if (!isPositiveTicketNumber(issueNumber)) {
|
|
753
760
|
throw new Error("issueNumber must be a positive integer");
|
|
754
761
|
}
|
|
755
762
|
const root = resolve(ledgerDir);
|
|
@@ -767,7 +774,7 @@ export async function loadTicketTrajectoryRuns(
|
|
|
767
774
|
runs.push(parsed);
|
|
768
775
|
}
|
|
769
776
|
|
|
770
|
-
// Book runs (flat + subject-tree) bound by typed invocation
|
|
777
|
+
// Book runs (flat + subject-tree) bound by typed invocation ticketNumber.
|
|
771
778
|
const flatBindings = index.runsByTicket.get(issueNumber) ?? [];
|
|
772
779
|
for (const binding of flatBindings) {
|
|
773
780
|
const resolvedRunDir = resolve(binding.runDir);
|
|
@@ -789,7 +796,7 @@ export async function renderTicketTrajectoryHtml(
|
|
|
789
796
|
now: Date,
|
|
790
797
|
options?: { refreshBoundarySeconds?: number },
|
|
791
798
|
): Promise<string> {
|
|
792
|
-
if (!isRecord(ticketSnapshot) ||
|
|
799
|
+
if (!isRecord(ticketSnapshot) || !isPositiveTicketNumber(ticketSnapshot.issueNumber)) {
|
|
793
800
|
throw new Error("ticketSnapshot.issueNumber must be a positive integer");
|
|
794
801
|
}
|
|
795
802
|
const issueNumber = ticketSnapshot.issueNumber;
|
|
@@ -807,118 +814,96 @@ export async function renderTicketTrajectoryHtml(
|
|
|
807
814
|
});
|
|
808
815
|
}
|
|
809
816
|
|
|
810
|
-
function
|
|
811
|
-
|
|
812
|
-
|
|
817
|
+
function landsInLedger(ledgerRoot: string, candidate: string): boolean {
|
|
818
|
+
// mkdir may have materialized a previously dangling ledger alias since the first gate.
|
|
819
|
+
const physicalRoot = physicalPathIdentity(ledgerRoot);
|
|
820
|
+
const lexical = resolve(candidate);
|
|
821
|
+
const physical = physicalPathIdentity(candidate);
|
|
822
|
+
const inside = (id: string) => physicalRoot === id || pathContainedIn(physicalRoot, id);
|
|
823
|
+
return inside(lexical) || inside(physical);
|
|
813
824
|
}
|
|
814
825
|
|
|
815
826
|
/**
|
|
816
|
-
*
|
|
817
|
-
*
|
|
818
|
-
* is a symlink into the ledger tree.
|
|
827
|
+
* Prospective output must stay outside every ledger root.
|
|
828
|
+
* Equality counts as inside. Symlink parents are followed by physical identity.
|
|
819
829
|
*/
|
|
820
|
-
export async function
|
|
821
|
-
|
|
830
|
+
export async function assertOutputOutsideLedgers(
|
|
831
|
+
ledgerDirs: readonly string[],
|
|
822
832
|
outputPath: string,
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
let ledgerRoot: string;
|
|
826
|
-
try {
|
|
827
|
-
ledgerRoot = await realpath(ledgerResolved);
|
|
828
|
-
} catch (error) {
|
|
829
|
-
if (!isMissingPathError(error)) throw error;
|
|
830
|
-
ledgerRoot = ledgerResolved;
|
|
831
|
-
}
|
|
833
|
+
refusalMessage: string,
|
|
834
|
+
): Promise<{ ledgerRoots: string[]; outputAbsolute: string; prospectiveReal: string }> {
|
|
832
835
|
const outputAbsolute = resolve(outputPath);
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
for (;;) {
|
|
836
|
+
const prospectiveReal = physicalPathIdentity(outputAbsolute);
|
|
837
|
+
const ledgerRoots: string[] = [];
|
|
838
|
+
for (const ledgerDir of ledgerDirs) {
|
|
839
|
+
const ledgerResolved = resolve(ledgerDir);
|
|
840
|
+
let ledgerRoot: string;
|
|
839
841
|
try {
|
|
840
|
-
await
|
|
841
|
-
break;
|
|
842
|
+
ledgerRoot = await realpath(ledgerResolved);
|
|
842
843
|
} catch (error) {
|
|
843
844
|
if (!isMissingPathError(error)) throw error;
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
845
|
+
ledgerRoot = physicalPathIdentity(ledgerResolved);
|
|
846
|
+
}
|
|
847
|
+
ledgerRoots.push(ledgerRoot);
|
|
848
|
+
if (landsInLedger(ledgerRoot, prospectiveReal) || landsInLedger(ledgerRoot, outputAbsolute)) {
|
|
849
|
+
throw new Error(refusalMessage);
|
|
848
850
|
}
|
|
849
851
|
}
|
|
850
|
-
|
|
851
|
-
let realPrefix: string;
|
|
852
|
-
try {
|
|
853
|
-
realPrefix = await realpath(cursor);
|
|
854
|
-
} catch (error) {
|
|
855
|
-
if (!isMissingPathError(error)) throw error;
|
|
856
|
-
realPrefix = resolve(cursor);
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
const prospectiveReal =
|
|
860
|
-
missingTail.length === 0 ? realPrefix : resolve(realPrefix, ...missingTail.reverse());
|
|
861
|
-
|
|
862
|
-
if (isPathInside(ledgerRoot, prospectiveReal) || isPathInside(ledgerRoot, realPrefix)) {
|
|
863
|
-
throw new Error("ticket trajectory outputPath must be outside the ledger directory");
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
// Lexical absolute path must also stay outside (defense in depth before mkdir).
|
|
867
|
-
if (isPathInside(ledgerRoot, outputAbsolute)) {
|
|
868
|
-
throw new Error("ticket trajectory outputPath must be outside the ledger directory");
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
return { ledgerRoot, outputAbsolute, prospectiveReal };
|
|
852
|
+
return { ledgerRoots, outputAbsolute, prospectiveReal };
|
|
872
853
|
}
|
|
873
854
|
|
|
874
855
|
/**
|
|
875
|
-
* Write HTML
|
|
876
|
-
* existing
|
|
877
|
-
* replaces the directory entry so a hard-linked ledger twin keeps its bytes.
|
|
856
|
+
* Write HTML outside the ledgers. Temp+rename does not open an existing inode.
|
|
857
|
+
* An existing symlink whose target is inside a ledger is refused.
|
|
878
858
|
*/
|
|
879
|
-
async function
|
|
880
|
-
|
|
881
|
-
outputAbsolute: string
|
|
882
|
-
html: string
|
|
883
|
-
|
|
884
|
-
|
|
859
|
+
export async function writePageOutsideLedgers(
|
|
860
|
+
ledgerRoots: readonly string[],
|
|
861
|
+
outputAbsolute: string,
|
|
862
|
+
html: string,
|
|
863
|
+
refusalMessage: string,
|
|
864
|
+
): Promise<string> {
|
|
865
|
+
const parent = dirname(outputAbsolute);
|
|
885
866
|
await mkdir(parent, { recursive: true });
|
|
886
|
-
|
|
887
|
-
// Re-resolve after mkdir: a race or symlink parent must still land outside.
|
|
888
867
|
const parentReal = await realpath(parent);
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
if (isPathInside(input.ledgerRoot, destinationReal)) {
|
|
895
|
-
throw new Error("ticket trajectory outputPath must be outside the ledger directory");
|
|
868
|
+
const destinationReal = resolve(parentReal, basename(outputAbsolute));
|
|
869
|
+
for (const root of ledgerRoots) {
|
|
870
|
+
if (landsInLedger(root, parentReal) || landsInLedger(root, destinationReal)) {
|
|
871
|
+
throw new Error(refusalMessage);
|
|
872
|
+
}
|
|
896
873
|
}
|
|
897
|
-
|
|
898
|
-
// Refuse to write through an existing symlink whose target is inside the ledger.
|
|
899
874
|
try {
|
|
900
|
-
const existing = await lstat(
|
|
875
|
+
const existing = await lstat(outputAbsolute);
|
|
901
876
|
if (existing.isSymbolicLink()) {
|
|
902
|
-
const target = await realpath(
|
|
903
|
-
|
|
904
|
-
throw new Error(
|
|
877
|
+
const target = await realpath(outputAbsolute);
|
|
878
|
+
for (const root of ledgerRoots) {
|
|
879
|
+
if (landsInLedger(root, target)) throw new Error(refusalMessage);
|
|
905
880
|
}
|
|
906
881
|
}
|
|
907
882
|
} catch (error) {
|
|
908
|
-
if (
|
|
883
|
+
if (errnoCode(error) !== "ENOENT" && errnoCode(error) !== "ENOTDIR") throw error;
|
|
909
884
|
}
|
|
885
|
+
await writeFileAtomically(outputAbsolute, html);
|
|
886
|
+
return realpath(outputAbsolute);
|
|
887
|
+
}
|
|
910
888
|
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
889
|
+
const TRAJECTORY_OUTPUT_REFUSAL =
|
|
890
|
+
"ticket trajectory outputPath must be outside the ledger directory";
|
|
891
|
+
|
|
892
|
+
/**
|
|
893
|
+
* Resolve the prospective on-disk target of outputPath and refuse any landing
|
|
894
|
+
* inside the ledger — including when the path, a parent, or a trailing segment
|
|
895
|
+
* is a symlink into the ledger tree.
|
|
896
|
+
*/
|
|
897
|
+
export async function assertTrajectoryOutputOutsideLedger(
|
|
898
|
+
ledgerDir: string,
|
|
899
|
+
outputPath: string,
|
|
900
|
+
): Promise<{ ledgerRoot: string; outputAbsolute: string; prospectiveReal: string }> {
|
|
901
|
+
const gate = await assertOutputOutsideLedgers([ledgerDir], outputPath, TRAJECTORY_OUTPUT_REFUSAL);
|
|
902
|
+
return {
|
|
903
|
+
ledgerRoot: gate.ledgerRoots[0]!,
|
|
904
|
+
outputAbsolute: gate.outputAbsolute,
|
|
905
|
+
prospectiveReal: gate.prospectiveReal,
|
|
906
|
+
};
|
|
922
907
|
}
|
|
923
908
|
|
|
924
909
|
/**
|
|
@@ -943,11 +928,12 @@ export async function writeTicketTrajectoryPage(input: {
|
|
|
943
928
|
: undefined,
|
|
944
929
|
);
|
|
945
930
|
|
|
946
|
-
const outputPath = await
|
|
947
|
-
|
|
948
|
-
|
|
931
|
+
const outputPath = await writePageOutsideLedgers(
|
|
932
|
+
[gate.ledgerRoot],
|
|
933
|
+
gate.outputAbsolute,
|
|
949
934
|
html,
|
|
950
|
-
|
|
935
|
+
TRAJECTORY_OUTPUT_REFUSAL,
|
|
936
|
+
);
|
|
951
937
|
return { outputPath, html };
|
|
952
938
|
}
|
|
953
939
|
|