@akagilnc/pi-workflow-roles 0.1.5504 → 0.1.5575
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 +5965 -5994
- package/dist/analyst-gate-cycles-read.js +23 -55
- package/dist/analyst-ledger.js +21 -35
- 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-pointer.js +38 -30
- package/dist/auditor-dossier-tool.js +4 -3
- package/dist/auditor-soul.js +3 -11
- package/dist/doctor-evidence.js +3 -3
- package/dist/engine-detour-usage.js +6 -14
- package/dist/headless-host/production-host.js +5316 -5324
- package/dist/host-descriptions.js +0 -4
- package/dist/host-session-record.js +266 -267
- package/dist/migrate-book-topology.js +1314 -696
- package/dist/notary-source-run.js +1 -1
- package/dist/packaged-role-registry.js +5 -44
- package/dist/pi/role-turn-host.js +53 -3
- package/dist/public-cli/auto-resume.js +8 -47
- package/dist/public-cli/instruction-seat-run.js +5 -3
- package/dist/public-cli/invocation.js +51 -56
- package/dist/public-cli/main.js +1916 -1748
- package/dist/public-cli/post-admission.js +96 -55
- package/dist/public-cli/run-lifecycle.js +27 -53
- package/dist/public-cli/settlement.js +157 -302
- package/dist/role-run-placement.js +1 -11
- package/dist/role-run-relocation.js +72 -50
- package/dist/run-dossier-files.js +30 -0
- package/dist/run-dossier.js +322 -0
- package/dist/run-terminal-artifacts.js +68 -197
- package/dist/run-ticket-number.js +16 -3
- package/dist/session-identity.js +49 -18
- package/dist/sitian-appender.js +13 -16
- package/dist/sitian-facade.js +11 -0
- package/dist/sitian-reader.js +44 -29
- package/dist/submission-gate.js +1 -1
- package/dist/ticket-provenance.js +22 -7
- 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 +9 -2
- package/src/analyst-gate-cycles-read.ts +26 -60
- package/src/analyst-ledger.ts +27 -42
- 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 +2 -2
- package/src/archivist-record-pointer.ts +50 -36
- package/src/auditor-dossier-tool.ts +7 -5
- package/src/auditor-soul.ts +3 -9
- 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/doctor-contracts.ts +1 -1
- package/src/doctor-evidence.ts +2 -2
- package/src/engine-detour-tool.ts +1 -1
- package/src/engine-detour-usage.ts +6 -16
- package/src/external-host-turn-loop.ts +25 -0
- package/src/headless-host/production-host.ts +1 -4
- package/src/headless-host/role-turn-host.ts +54 -20
- package/src/host-descriptions.ts +0 -6
- package/src/host-session-record.ts +224 -140
- package/src/notary-source-run.ts +1 -1
- package/src/packaged-role-registry.ts +1 -50
- package/src/pi/adapter.ts +50 -7
- package/src/pi/role-turn-host.ts +51 -3
- package/src/public-cli/auto-resume.ts +7 -43
- package/src/public-cli/instruction-seat-run.ts +9 -2
- package/src/public-cli/invocation.ts +61 -87
- package/src/public-cli/post-admission.ts +111 -72
- package/src/public-cli/run-lifecycle.ts +27 -72
- package/src/public-cli/settlement.ts +193 -418
- package/src/public-cli/terminal.ts +1 -1
- package/src/role-run-placement.ts +2 -13
- package/src/role-run-relocation.ts +71 -68
- package/src/role-runtime.ts +6 -10
- package/src/run-dossier-files.ts +33 -0
- package/src/run-dossier.ts +357 -0
- package/src/run-terminal-artifacts.ts +83 -237
- package/src/run-ticket-number.ts +41 -11
- package/src/session-identity.ts +52 -20
- package/src/sitian-appender.ts +17 -18
- package/src/sitian-facade.ts +22 -0
- package/src/sitian-reader.ts +62 -32
- package/src/submission-gate.ts +1 -1
- package/src/ticket-provenance.ts +20 -7
- package/src/ticket-trajectory.ts +10 -10
|
@@ -9,8 +9,12 @@ import { lstat, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
|
9
9
|
|
|
10
10
|
import { sessionFileOf } from "../role-run-placement.ts";
|
|
11
11
|
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
12
|
+
import { sitianReport, type RecordPointer } from "../sitian-facade.ts";
|
|
13
|
+
import {
|
|
14
|
+
ATTEMPT_HISTORY_IDENTITY_FIELD,
|
|
15
|
+
TERMINAL_COURT_ATTEMPT_FIELD,
|
|
16
|
+
writeRunTerminal,
|
|
17
|
+
} from "../run-terminal-artifacts.ts";
|
|
14
18
|
|
|
15
19
|
import {
|
|
16
20
|
readAttemptScopedSubmissionRows,
|
|
@@ -38,7 +42,6 @@ import {
|
|
|
38
42
|
import type { DoctorCaseCost } from "../doctor-contracts.ts";
|
|
39
43
|
import { DOCTOR_CANDIDATE_ENTRY_TYPE } from "../dossier-resolution.ts";
|
|
40
44
|
import {
|
|
41
|
-
ensureRunArtifactsDir,
|
|
42
45
|
homeFromRunDirectory,
|
|
43
46
|
type AdmittedRoleInvocation,
|
|
44
47
|
} from "./invocation.ts";
|
|
@@ -55,7 +58,6 @@ import {
|
|
|
55
58
|
packagedRoleAcceptedOutputTool,
|
|
56
59
|
packagedRoleMetadata,
|
|
57
60
|
type PackagedArtifactFace,
|
|
58
|
-
type PackagedArtifactLeaf,
|
|
59
61
|
} from "../packaged-role-registry.ts";
|
|
60
62
|
import {
|
|
61
63
|
NO_RECEIPT_LIFECYCLE_ENTRY_TYPE,
|
|
@@ -70,18 +72,8 @@ import type {
|
|
|
70
72
|
DurablePrincipalAuthority,
|
|
71
73
|
DurablePrincipalCoordinates,
|
|
72
74
|
} from "../host-contracts.ts";
|
|
73
|
-
import {
|
|
74
|
-
import {
|
|
75
|
-
listSeamOwnedUniqueErrorFacePaths,
|
|
76
|
-
uniqueErrorFallbackName,
|
|
77
|
-
UNIQUE_ERROR_FALLBACK_STEM,
|
|
78
|
-
RUN_TERMINAL_ARTIFACT_FILES,
|
|
79
|
-
RUN_TERMINAL_ERROR_FALLBACK_RELATIVE_PATHS,
|
|
80
|
-
RUN_TERMINAL_REPORT_FILE,
|
|
81
|
-
RUN_TERMINAL_ERROR_FILE,
|
|
82
|
-
RUN_TERMINAL_EVIDENCE_FILE,
|
|
83
|
-
RUN_TERMINAL_ERROR_SETTLEMENT_FILE,
|
|
84
|
-
} from "../run-terminal-artifacts.ts";
|
|
75
|
+
import { readStateRowsSync, runCurrentPath } from "../run-dossier.ts";
|
|
76
|
+
import { reportRunRecord } from "../sitian-facade.ts";
|
|
85
77
|
|
|
86
78
|
/** Ledger reads use the run's machine home — not ambient process HOME (child write vs parent settle). */
|
|
87
79
|
function sealedLedgerHome(admitted: Pick<AdmittedRoleInvocation, "runDirectory">): string {
|
|
@@ -107,32 +99,6 @@ export type SettlementCourtScope = {
|
|
|
107
99
|
readonly notePackageFault?: (diagnostic: string) => void | Promise<void>;
|
|
108
100
|
};
|
|
109
101
|
|
|
110
|
-
/**
|
|
111
|
-
* Hardened artifacts directory. A planted symlink at the run directory or the
|
|
112
|
-
* artifacts path must not receive a durable run artifact.
|
|
113
|
-
*/
|
|
114
|
-
export async function ensureRealArtifactsDirectory(runDirectory: string): Promise<string> {
|
|
115
|
-
const runStat = await lstat(runDirectory);
|
|
116
|
-
if (runStat.isSymbolicLink() || !runStat.isDirectory()) {
|
|
117
|
-
throw new Error("run artifact retention: run directory is not a real directory");
|
|
118
|
-
}
|
|
119
|
-
const artifactsDir = roleRunArtifactsDirectory(runDirectory);
|
|
120
|
-
try {
|
|
121
|
-
const existing = await lstat(artifactsDir);
|
|
122
|
-
if (existing.isSymbolicLink() || !existing.isDirectory()) {
|
|
123
|
-
throw new Error("run artifact retention: artifacts path is not a real directory");
|
|
124
|
-
}
|
|
125
|
-
} catch (error) {
|
|
126
|
-
if (!isEnoent(error)) throw error;
|
|
127
|
-
await mkdir(artifactsDir, { recursive: true });
|
|
128
|
-
const created = await lstat(artifactsDir);
|
|
129
|
-
if (created.isSymbolicLink() || !created.isDirectory()) {
|
|
130
|
-
throw new Error("run artifact retention: artifacts directory is not a real directory");
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
return artifactsDir;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
102
|
/**
|
|
137
103
|
* One retention path for a package fault beside an already chosen terminal.
|
|
138
104
|
* Session append is first when the caller has that channel. The artifact file
|
|
@@ -156,13 +122,14 @@ export async function retainPackageFault(input: {
|
|
|
156
122
|
...(Object.hasOwn(input, "error") ? { failure: projectThrownFailureLeaf(input.error) } : {}),
|
|
157
123
|
};
|
|
158
124
|
let retentionFailure: string | undefined;
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
125
|
+
// The run's log is the fallback after the session append throws, and the only
|
|
126
|
+
// channel when the caller has none.
|
|
127
|
+
const writeLog = (appendFailure?: ControlledFailure): void => {
|
|
128
|
+
reportRunRecord(input.runDirectory, "post-admission-diagnostic", {
|
|
162
129
|
version: 1,
|
|
163
130
|
...payload,
|
|
164
131
|
...(appendFailure === undefined ? {} : { retentionFailure: appendFailure }),
|
|
165
|
-
});
|
|
132
|
+
}, "settlement");
|
|
166
133
|
};
|
|
167
134
|
if (input.appendSession !== undefined) {
|
|
168
135
|
try {
|
|
@@ -171,18 +138,18 @@ export async function retainPackageFault(input: {
|
|
|
171
138
|
retentionFailure =
|
|
172
139
|
`post-dispatch diagnostic session append failed (best-effort continue): dossier=${describeErrorIdentity(appendError)}`;
|
|
173
140
|
try {
|
|
174
|
-
|
|
175
|
-
} catch (
|
|
141
|
+
writeLog(projectThrownFailureLeaf(appendError));
|
|
142
|
+
} catch (logError) {
|
|
176
143
|
retentionFailure =
|
|
177
|
-
`post-dispatch diagnostic durable retention failed on both channels (best-effort continue): dossier=${describeErrorIdentity(appendError)};
|
|
144
|
+
`post-dispatch diagnostic durable retention failed on both channels (best-effort continue): dossier=${describeErrorIdentity(appendError)}; log=${describeErrorIdentity(logError)}`;
|
|
178
145
|
}
|
|
179
146
|
}
|
|
180
147
|
} else {
|
|
181
148
|
try {
|
|
182
|
-
|
|
183
|
-
} catch (
|
|
149
|
+
writeLog();
|
|
150
|
+
} catch (logError) {
|
|
184
151
|
retentionFailure =
|
|
185
|
-
`post-dispatch diagnostic durable retention failed (best-effort continue):
|
|
152
|
+
`post-dispatch diagnostic durable retention failed (best-effort continue): log=${describeErrorIdentity(logError)}`;
|
|
186
153
|
}
|
|
187
154
|
}
|
|
188
155
|
const stderr = input.stderr ?? ((text: string) => {
|
|
@@ -460,13 +427,29 @@ async function settleNoReceiptTerminal(
|
|
|
460
427
|
kind: "no_receipt", role: admitted.role, status: "no-accepted-receipt",
|
|
461
428
|
...facts, decisiveFacts: facts,
|
|
462
429
|
};
|
|
430
|
+
let attemptHistoryIdentity: string | undefined;
|
|
463
431
|
if (scope?.recordAttemptHistory === true) {
|
|
464
|
-
await appendRunAttemptHistory(
|
|
465
|
-
|
|
432
|
+
const pointer = await appendRunAttemptHistory(
|
|
433
|
+
attemptHistorySource(admitted, coordinates),
|
|
466
434
|
roleOutcome,
|
|
467
435
|
);
|
|
436
|
+
attemptHistoryIdentity = pointer.identity;
|
|
437
|
+
} else {
|
|
438
|
+
attemptHistoryIdentity = await belongingAttemptHistoryIdentity(
|
|
439
|
+
admitted.runDirectory,
|
|
440
|
+
scope,
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
if (scope?.previewOnly !== true) {
|
|
444
|
+
// Belonging settlement only (#1161 甲 / R2): never inherit another court's
|
|
445
|
+
// latest terminal pointer, and never invent a history row on re-projection (N1).
|
|
446
|
+
writeRunTerminal(admitted.runDirectory, "no_receipt", {
|
|
447
|
+
role: admitted.role,
|
|
448
|
+
runId: admitted.runId,
|
|
449
|
+
outcome: roleOutcome,
|
|
450
|
+
...terminalBelongingFields(attemptHistoryIdentity, scope),
|
|
451
|
+
});
|
|
468
452
|
}
|
|
469
|
-
if (scope?.previewOnly !== true) await clearOppositeTerminalArtifactFace(admitted.runDirectory);
|
|
470
453
|
return attachEngineDetourToolUsage({
|
|
471
454
|
roleOutcome,
|
|
472
455
|
navigator: await extractNavigatorFactFromAdmittedSession(
|
|
@@ -526,7 +509,7 @@ export type ControlledFailure = RoleTurnKnownFailure & {
|
|
|
526
509
|
*/
|
|
527
510
|
readonly packageFact?: PackageSideFact;
|
|
528
511
|
/**
|
|
529
|
-
* Host stderr that is not already this failure's diagnostic. A stderr
|
|
512
|
+
* Host stderr that is not already this failure's diagnostic. A stderr log
|
|
530
513
|
* or artifact write can fail; the original bytes stay on this object.
|
|
531
514
|
*/
|
|
532
515
|
readonly stderr?: string;
|
|
@@ -555,7 +538,7 @@ export type PackageSideFact = {
|
|
|
555
538
|
readonly cancelName?: string;
|
|
556
539
|
/** An exception caught after the host already reported this call. */
|
|
557
540
|
readonly thrown?: RoleTurnKnownFailure & { readonly diagnostic: string };
|
|
558
|
-
/** A durable stderr
|
|
541
|
+
/** A durable stderr log-line write that failed while handling this call. */
|
|
559
542
|
readonly stderrLogWriteFailure?: unknown;
|
|
560
543
|
};
|
|
561
544
|
|
|
@@ -1052,12 +1035,23 @@ type AttemptHistorySource = {
|
|
|
1052
1035
|
readonly sessionFile: string;
|
|
1053
1036
|
};
|
|
1054
1037
|
|
|
1038
|
+
function attemptHistorySource(
|
|
1039
|
+
admitted: Pick<AdmittedRoleInvocation, "role" | "runId">,
|
|
1040
|
+
coordinates: Pick<DurablePrincipalCoordinates, "sessionFile">,
|
|
1041
|
+
): AttemptHistorySource {
|
|
1042
|
+
return {
|
|
1043
|
+
role: admitted.role,
|
|
1044
|
+
runId: admitted.runId,
|
|
1045
|
+
sessionFile: coordinates.sessionFile,
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1055
1049
|
/** Append the complete attempt to the package ledger before overwriting pointer artifacts (#419). */
|
|
1056
1050
|
export async function appendRunAttemptHistory(
|
|
1057
1051
|
source: AttemptHistorySource,
|
|
1058
1052
|
outcome: AttemptHistoryOutcome,
|
|
1059
|
-
): Promise<
|
|
1060
|
-
sitianReport({
|
|
1053
|
+
): Promise<RecordPointer> {
|
|
1054
|
+
return sitianReport({
|
|
1061
1055
|
level: "event",
|
|
1062
1056
|
kind: "attempt-history",
|
|
1063
1057
|
subject: { runId: source.runId },
|
|
@@ -1316,120 +1310,109 @@ async function extractNavigatorFactFromAdmittedSession(
|
|
|
1316
1310
|
}
|
|
1317
1311
|
|
|
1318
1312
|
/**
|
|
1319
|
-
*
|
|
1320
|
-
*
|
|
1321
|
-
*
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
} catch (error) {
|
|
1327
|
-
// force:true already ignores ENOENT; ENOTDIR = face path not enterable
|
|
1328
|
-
// (artifacts-as-file mid-path) — same absent-face semantics as the reader.
|
|
1329
|
-
// Other errno stay loud (失败诚实).
|
|
1330
|
-
if (isMissingPathError(error)) return;
|
|
1331
|
-
throw error;
|
|
1332
|
-
}
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
/**
|
|
1336
|
-
* #953: artifact face reflects the current terminal only.
|
|
1337
|
-
* Reader contract owns the full adoptable set (conventional trio + fixed
|
|
1338
|
-
* fallbacks + seam-owned unique). Clear every seam-owned face before the
|
|
1339
|
-
* new current publishes — including the conventional name about to be
|
|
1340
|
-
* rewritten. Retaining that name left directory collision plants in place
|
|
1341
|
-
* (supported publish input); writer then fell back while the reader stopped
|
|
1342
|
-
* at EISDIR on the stale conventional path and never adopted the fallback.
|
|
1343
|
-
* report / error / empty share one method (no per-branch face list).
|
|
1344
|
-
* empty publishes nothing; does not create a no_receipt-shaped public artifact.
|
|
1345
|
-
* Non-terminal materials stay. Face names may be directory collision plants —
|
|
1346
|
-
* remove recursively. Unique ownership is listSeamOwnedUniqueErrorFacePaths:
|
|
1347
|
-
* parent runs/ faces clear only when body.runId binds; unparseable runId → none.
|
|
1348
|
-
* Path absence (ENOENT/ENOTDIR) is silent; other delete failures stay loud.
|
|
1313
|
+
* Sole success-terminal publisher (#953): replace the leg's terminal in
|
|
1314
|
+
* current.json. The report carries the outcome's verdict facts; the role's
|
|
1315
|
+
* submitted payloads live in history.jsonl. The terminal names the
|
|
1316
|
+
* attempt-history row that belongs to this settlement — from this settle's
|
|
1317
|
+
* append when recording, otherwise that court's prior terminal row in
|
|
1318
|
+
* state.jsonl (#1161 甲 / R2). Never content-match payloads, never inherit the
|
|
1319
|
+
* leg's latest terminal, never invent a history row on re-projection (N1).
|
|
1349
1320
|
*/
|
|
1350
|
-
async function
|
|
1351
|
-
runDirectory: string,
|
|
1352
|
-
): Promise<void> {
|
|
1353
|
-
const artifactsDir = roleRunArtifactsDirectory(runDirectory);
|
|
1354
|
-
for (const file of RUN_TERMINAL_ARTIFACT_FILES) {
|
|
1355
|
-
await removeFaceIfPresent(join(artifactsDir, file));
|
|
1356
|
-
}
|
|
1357
|
-
// Fixed fallbacks + unique clear too; subsequent publish writes the new
|
|
1358
|
-
// durable path after this clear (conventional first when writable).
|
|
1359
|
-
for (const relative of RUN_TERMINAL_ERROR_FALLBACK_RELATIVE_PATHS) {
|
|
1360
|
-
await removeFaceIfPresent(join(runDirectory, relative));
|
|
1361
|
-
}
|
|
1362
|
-
for (const path of await listSeamOwnedUniqueErrorFacePaths(runDirectory)) {
|
|
1363
|
-
await removeFaceIfPresent(path);
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
|
-
|
|
1367
|
-
async function ensureTerminalArtifactFace(
|
|
1368
|
-
runDirectory: string,
|
|
1369
|
-
): Promise<string> {
|
|
1370
|
-
const artifactsDir = await ensureRunArtifactsDir(runDirectory);
|
|
1371
|
-
await clearOppositeTerminalArtifactFace(runDirectory);
|
|
1372
|
-
return artifactsDir;
|
|
1373
|
-
}
|
|
1374
|
-
|
|
1375
|
-
type AcceptedArtifactAttachment = {
|
|
1376
|
-
readonly provenancePath: string;
|
|
1377
|
-
readonly frozenPath: string;
|
|
1378
|
-
readonly sha256: string;
|
|
1379
|
-
readonly byteLength: number;
|
|
1380
|
-
};
|
|
1381
|
-
|
|
1382
|
-
function acceptedArtifactAttachmentRefs(
|
|
1383
|
-
attachments: readonly AcceptedArtifactAttachment[],
|
|
1384
|
-
): AcceptedArtifactAttachment[] {
|
|
1385
|
-
return attachments.map((a) => ({
|
|
1386
|
-
provenancePath: a.provenancePath,
|
|
1387
|
-
frozenPath: a.frozenPath,
|
|
1388
|
-
sha256: a.sha256,
|
|
1389
|
-
byteLength: a.byteLength,
|
|
1390
|
-
}));
|
|
1391
|
-
}
|
|
1392
|
-
|
|
1393
|
-
/**
|
|
1394
|
-
* Sole success-terminal artifact publisher (#953): append attempt history,
|
|
1395
|
-
* refresh the public terminal face, write report/evidence, return refs.
|
|
1396
|
-
* Seat-specific structured fields stay in callers; do not fork this flow.
|
|
1397
|
-
*/
|
|
1398
|
-
async function publishAcceptedTerminalArtifacts(
|
|
1321
|
+
async function publishAcceptedTerminal(
|
|
1399
1322
|
admitted: AdmittedRoleInvocation,
|
|
1400
1323
|
roleOutcome: TerminalRoleOutcome,
|
|
1401
1324
|
coordinates: DurablePrincipalCoordinates,
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
readonly report: Record<string, unknown>;
|
|
1405
|
-
readonly evidence: Record<string, unknown>;
|
|
1406
|
-
},
|
|
1325
|
+
scope: SettlementCourtScope | undefined,
|
|
1326
|
+
report: Record<string, unknown>,
|
|
1407
1327
|
): Promise<TerminalArtifactRef[]> {
|
|
1408
1328
|
// #419: a dispatched turn appends before rewriting last-write-wins views;
|
|
1409
1329
|
// a later projection may refresh views but must not invent another attempt.
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1330
|
+
let attemptHistoryIdentity: string | undefined;
|
|
1331
|
+
if (scope?.recordAttemptHistory === true) {
|
|
1332
|
+
const pointer = await appendRunAttemptHistory(
|
|
1333
|
+
attemptHistorySource(admitted, coordinates),
|
|
1413
1334
|
roleOutcome,
|
|
1414
1335
|
);
|
|
1336
|
+
attemptHistoryIdentity = pointer.identity;
|
|
1337
|
+
} else {
|
|
1338
|
+
attemptHistoryIdentity = await belongingAttemptHistoryIdentity(
|
|
1339
|
+
admitted.runDirectory,
|
|
1340
|
+
scope,
|
|
1341
|
+
);
|
|
1415
1342
|
}
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
return
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1343
|
+
writeRunTerminal(admitted.runDirectory, "report", {
|
|
1344
|
+
...report,
|
|
1345
|
+
...terminalBelongingFields(attemptHistoryIdentity, scope),
|
|
1346
|
+
});
|
|
1347
|
+
return [{ kind: "report", path: runCurrentPath(admitted.runDirectory) }];
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
/** 甲 pointer + court tag carried on the terminal body for later re-projection. */
|
|
1351
|
+
function terminalBelongingFields(
|
|
1352
|
+
attemptHistoryIdentity: string | undefined,
|
|
1353
|
+
scope: SettlementCourtScope | undefined,
|
|
1354
|
+
): Record<string, string> {
|
|
1355
|
+
const courtAttemptId = scope?.courtAttemptId;
|
|
1356
|
+
return {
|
|
1357
|
+
...(attemptHistoryIdentity === undefined
|
|
1358
|
+
? {}
|
|
1359
|
+
: { [ATTEMPT_HISTORY_IDENTITY_FIELD]: attemptHistoryIdentity }),
|
|
1360
|
+
...(courtAttemptId === undefined || courtAttemptId.length === 0
|
|
1361
|
+
? {}
|
|
1362
|
+
: { [TERMINAL_COURT_ATTEMPT_FIELD]: courtAttemptId }),
|
|
1363
|
+
};
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
/**
|
|
1367
|
+
* Belonging attempt-history identity for a non-recording re-projection (#1161 甲 / R2).
|
|
1368
|
+
* Court-tagged settle recovers from that court's own prior terminal row in
|
|
1369
|
+
* state.jsonl. Untagged normal rewrite (bare resume after a sealed face, no open
|
|
1370
|
+
* court) keeps only the face being rewritten — the last terminal row's own
|
|
1371
|
+
* identity. If that face has none (approved gap), return undefined: never walk
|
|
1372
|
+
* older terminals for a non-empty pointer, never invent-append (N1), never
|
|
1373
|
+
* subject.attemptId on attempt-history. Bare resume therefore cannot restore
|
|
1374
|
+
* court B's pointer while the current face is A (or A's gap). Court-tagged
|
|
1375
|
+
* lookup that finds no row still returns undefined — the approved gap.
|
|
1376
|
+
*/
|
|
1377
|
+
async function belongingAttemptHistoryIdentity(
|
|
1378
|
+
runDirectory: string,
|
|
1379
|
+
scope: SettlementCourtScope | undefined,
|
|
1380
|
+
): Promise<string | undefined> {
|
|
1381
|
+
const courtAttemptId = scope?.courtAttemptId;
|
|
1382
|
+
let rows: readonly Record<string, unknown>[];
|
|
1383
|
+
try {
|
|
1384
|
+
rows = readStateRowsSync(runDirectory);
|
|
1385
|
+
} catch (error) {
|
|
1386
|
+
// Documented: history/state failure domain does not block the terminal face
|
|
1387
|
+
// (#1161 / main history disposition). True cause must leave a trace.
|
|
1388
|
+
await noteSettlementFault(
|
|
1389
|
+
runDirectory,
|
|
1390
|
+
scope,
|
|
1391
|
+
`terminal state read failed beside terminal projection: ${describeErrorIdentity(error)}`,
|
|
1392
|
+
);
|
|
1393
|
+
return undefined;
|
|
1394
|
+
}
|
|
1395
|
+
if (courtAttemptId !== undefined && courtAttemptId.length > 0) {
|
|
1396
|
+
let found: string | undefined;
|
|
1397
|
+
for (const row of rows) {
|
|
1398
|
+
if (row.kind !== "terminal" || !isRecord(row.payload)) continue;
|
|
1399
|
+
const body = row.payload.body;
|
|
1400
|
+
if (!isRecord(body) || body[TERMINAL_COURT_ATTEMPT_FIELD] !== courtAttemptId) continue;
|
|
1401
|
+
const identity = body[ATTEMPT_HISTORY_IDENTITY_FIELD];
|
|
1402
|
+
if (typeof identity === "string" && identity.length > 0) found = identity;
|
|
1403
|
+
}
|
|
1404
|
+
return found;
|
|
1405
|
+
}
|
|
1406
|
+
// Untagged: only the current face (last terminal). Gap face stays a gap.
|
|
1407
|
+
for (let index = rows.length - 1; index >= 0; index -= 1) {
|
|
1408
|
+
const row = rows[index]!;
|
|
1409
|
+
if (row.kind !== "terminal" || !isRecord(row.payload)) continue;
|
|
1410
|
+
const body = row.payload.body;
|
|
1411
|
+
if (!isRecord(body)) return undefined;
|
|
1412
|
+
const identity = body[ATTEMPT_HISTORY_IDENTITY_FIELD];
|
|
1413
|
+
return typeof identity === "string" && identity.length > 0 ? identity : undefined;
|
|
1414
|
+
}
|
|
1415
|
+
return undefined;
|
|
1433
1416
|
}
|
|
1434
1417
|
|
|
1435
1418
|
type LawfulSessionRead =
|
|
@@ -1491,7 +1474,7 @@ async function finishLawfulSeat(
|
|
|
1491
1474
|
): Promise<TerminalResult> {
|
|
1492
1475
|
const artifacts = scope?.previewOnly === true
|
|
1493
1476
|
? []
|
|
1494
|
-
: await
|
|
1477
|
+
: await publishDeclaredSeatTerminal(admitted, roleOutcome, coordinates, entries, scope);
|
|
1495
1478
|
const terminal = await attachEngineDetourToolUsage({
|
|
1496
1479
|
roleOutcome,
|
|
1497
1480
|
navigator: extractNavigatorFact(entries),
|
|
@@ -1547,9 +1530,9 @@ function extractDoctorCandidateFacts(
|
|
|
1547
1530
|
return {};
|
|
1548
1531
|
}
|
|
1549
1532
|
|
|
1550
|
-
const EMPTY_ARTIFACT_FACE: PackagedArtifactFace = {
|
|
1533
|
+
const EMPTY_ARTIFACT_FACE: PackagedArtifactFace = {};
|
|
1551
1534
|
|
|
1552
|
-
/**
|
|
1535
|
+
/** Terminal face for one seat. Absent means the report carries only the shared facts. */
|
|
1553
1536
|
function seatArtifactFace(role: string): PackagedArtifactFace {
|
|
1554
1537
|
const record = packagedRoleMetadata(role);
|
|
1555
1538
|
if (record !== undefined && "artifactFace" in record && record.artifactFace !== undefined) {
|
|
@@ -1558,32 +1541,6 @@ function seatArtifactFace(role: string): PackagedArtifactFace {
|
|
|
1558
1541
|
return EMPTY_ARTIFACT_FACE;
|
|
1559
1542
|
}
|
|
1560
1543
|
|
|
1561
|
-
function readAdmittedPath(source: object, path: string): unknown {
|
|
1562
|
-
let current: unknown = source;
|
|
1563
|
-
for (const part of path.split(".")) {
|
|
1564
|
-
if (typeof current !== "object" || current === null) return undefined;
|
|
1565
|
-
current = (current as Record<string, unknown>)[part];
|
|
1566
|
-
}
|
|
1567
|
-
return current;
|
|
1568
|
-
}
|
|
1569
|
-
|
|
1570
|
-
function evidenceLeaves(
|
|
1571
|
-
admitted: AdmittedRoleInvocation,
|
|
1572
|
-
leaves: readonly PackagedArtifactLeaf[],
|
|
1573
|
-
): Record<string, unknown> {
|
|
1574
|
-
const evidence: Record<string, unknown> = {};
|
|
1575
|
-
for (const leaf of leaves) {
|
|
1576
|
-
if (leaf.callerProvenance === true) {
|
|
1577
|
-
if (!admitted.instructionEmpty) evidence[leaf.key] = admitted.instruction;
|
|
1578
|
-
continue;
|
|
1579
|
-
}
|
|
1580
|
-
const value = readAdmittedPath(admitted, leaf.from ?? leaf.key);
|
|
1581
|
-
if (value === undefined && leaf.omitUndefined === true) continue;
|
|
1582
|
-
evidence[leaf.key] = leaf.copyArray === true && Array.isArray(value) ? [...value] : value;
|
|
1583
|
-
}
|
|
1584
|
-
return evidence;
|
|
1585
|
-
}
|
|
1586
|
-
|
|
1587
1544
|
function doctorReportFacts(
|
|
1588
1545
|
face: PackagedArtifactFace,
|
|
1589
1546
|
roleOutcome: TerminalRoleOutcome,
|
|
@@ -1597,34 +1554,25 @@ function doctorReportFacts(
|
|
|
1597
1554
|
};
|
|
1598
1555
|
}
|
|
1599
1556
|
|
|
1600
|
-
async function
|
|
1557
|
+
async function publishDeclaredSeatTerminal(
|
|
1601
1558
|
admitted: AdmittedRoleInvocation,
|
|
1602
1559
|
roleOutcome: TerminalRoleOutcome,
|
|
1603
1560
|
coordinates: DurablePrincipalCoordinates,
|
|
1604
1561
|
entries: readonly SessionEntry[],
|
|
1605
|
-
|
|
1562
|
+
scope: SettlementCourtScope | undefined,
|
|
1606
1563
|
): Promise<TerminalArtifactRef[]> {
|
|
1607
1564
|
const face = seatArtifactFace(admitted.role);
|
|
1608
1565
|
const phase = face.reportPhase === true
|
|
1609
|
-
? { phase:
|
|
1566
|
+
? { phase: (admitted as { phase?: unknown }).phase }
|
|
1610
1567
|
: {};
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
evidence: {
|
|
1620
|
-
runId: admitted.runId,
|
|
1621
|
-
...(face.evidenceRole === true ? { role: admitted.role } : {}),
|
|
1622
|
-
...evidenceLeaves(admitted, face.leaves),
|
|
1623
|
-
sessionDirectory: coordinates.sessionDirectory,
|
|
1624
|
-
sessionFile: coordinates.sessionFile,
|
|
1625
|
-
admittedRequestPath: admitted.admittedRequestPath,
|
|
1626
|
-
attachments: acceptedArtifactAttachmentRefs(admitted.attachments),
|
|
1627
|
-
},
|
|
1568
|
+
// Payloads are the role's submitted words: they live in history.jsonl, not here.
|
|
1569
|
+
const { payloads: _payloads, ...verdict } = roleOutcome as TerminalRoleOutcome & { payloads?: unknown };
|
|
1570
|
+
return publishAcceptedTerminal(admitted, roleOutcome, coordinates, scope, {
|
|
1571
|
+
role: admitted.role,
|
|
1572
|
+
runId: admitted.runId,
|
|
1573
|
+
...phase,
|
|
1574
|
+
outcome: verdict,
|
|
1575
|
+
...doctorReportFacts(face, roleOutcome, entries),
|
|
1628
1576
|
});
|
|
1629
1577
|
}
|
|
1630
1578
|
|
|
@@ -1807,7 +1755,7 @@ async function applySecretariatCountersignTerminal(
|
|
|
1807
1755
|
/**
|
|
1808
1756
|
* After the audit gate returns, attach the secretariat officer fact onto the
|
|
1809
1757
|
* terminal this turn already settled. Does not publish again — a second publish
|
|
1810
|
-
* would
|
|
1758
|
+
* would settle the same attempt twice.
|
|
1811
1759
|
*/
|
|
1812
1760
|
export async function attachPostAuditCountersignFact(
|
|
1813
1761
|
admitted: AdmittedRoleInvocation,
|
|
@@ -1819,182 +1767,41 @@ export async function attachPostAuditCountersignFact(
|
|
|
1819
1767
|
return applySecretariatCountersignTerminal(admitted, authority, terminal, scope);
|
|
1820
1768
|
}
|
|
1821
1769
|
|
|
1822
|
-
/** One failed attempt to place a durable failure artifact (path is private layout). */
|
|
1823
|
-
type PublicationAttempt = {
|
|
1824
|
-
readonly path: string;
|
|
1825
|
-
readonly diagnostic: string;
|
|
1826
|
-
readonly identity?: {
|
|
1827
|
-
readonly name?: string;
|
|
1828
|
-
readonly code?: string | number;
|
|
1829
|
-
};
|
|
1830
|
-
};
|
|
1831
|
-
|
|
1832
|
-
function publicationAttemptFromError(
|
|
1833
|
-
path: string,
|
|
1834
|
-
error: unknown,
|
|
1835
|
-
): PublicationAttempt {
|
|
1836
|
-
if (error instanceof Error) {
|
|
1837
|
-
return {
|
|
1838
|
-
path,
|
|
1839
|
-
diagnostic: error.message || error.name || "write failed",
|
|
1840
|
-
identity: thrownIdentity(error),
|
|
1841
|
-
};
|
|
1842
|
-
}
|
|
1843
|
-
return { path, diagnostic: String(error) };
|
|
1844
|
-
}
|
|
1845
|
-
|
|
1846
1770
|
/**
|
|
1847
|
-
*
|
|
1848
|
-
*
|
|
1849
|
-
* unwritable run tree cannot strand the original controlled failure.
|
|
1771
|
+
* Replace the leg's terminal with the controlled failure. A write failure
|
|
1772
|
+
* propagates: the caller notes it beside the host terminal.
|
|
1850
1773
|
*/
|
|
1851
|
-
function
|
|
1852
|
-
runDirectory: string,
|
|
1853
|
-
baseDir: string,
|
|
1854
|
-
): string[] {
|
|
1855
|
-
const dirs: string[] = [];
|
|
1856
|
-
for (const dir of [baseDir, runDirectory, dirname(runDirectory)]) {
|
|
1857
|
-
if (!dirs.includes(dir)) dirs.push(dir);
|
|
1858
|
-
}
|
|
1859
|
-
return dirs;
|
|
1860
|
-
}
|
|
1861
|
-
|
|
1862
|
-
/**
|
|
1863
|
-
* Resolve a writable artifacts base directory. If `artifacts/` cannot be created
|
|
1864
|
-
* (e.g. a file occupies that name), fall back to the run directory itself.
|
|
1865
|
-
*/
|
|
1866
|
-
async function resolveFailureArtifactsBase(
|
|
1867
|
-
runDirectory: string,
|
|
1868
|
-
): Promise<{ baseDir: string; attempt?: PublicationAttempt }> {
|
|
1869
|
-
const artifactsDir = roleRunArtifactsDirectory(runDirectory);
|
|
1870
|
-
try {
|
|
1871
|
-
await ensureRunArtifactsDir(runDirectory);
|
|
1872
|
-
return { baseDir: artifactsDir };
|
|
1873
|
-
} catch (error) {
|
|
1874
|
-
return {
|
|
1875
|
-
baseDir: runDirectory,
|
|
1876
|
-
attempt: publicationAttemptFromError(artifactsDir, error),
|
|
1877
|
-
};
|
|
1878
|
-
}
|
|
1879
|
-
}
|
|
1880
|
-
|
|
1881
|
-
/**
|
|
1882
|
-
* Write JSON across preferred paths, then unique open-ended fallbacks.
|
|
1883
|
-
* Finite fixed names must not be able to exhaust durability and strand the
|
|
1884
|
-
* original controlled failure outside settlement.
|
|
1885
|
-
*/
|
|
1886
|
-
async function writeFailureJsonRetainingCause(
|
|
1887
|
-
preferredCandidates: readonly string[],
|
|
1888
|
-
uniqueFallbackDirs: readonly string[],
|
|
1889
|
-
stem: string,
|
|
1890
|
-
basePayload: Readonly<Record<string, unknown>>,
|
|
1891
|
-
priorIssues: readonly PublicationAttempt[],
|
|
1892
|
-
): Promise<{ path: string; issues: PublicationAttempt[] }> {
|
|
1893
|
-
const issues: PublicationAttempt[] = [...priorIssues];
|
|
1894
|
-
const candidates: string[] = [
|
|
1895
|
-
...preferredCandidates,
|
|
1896
|
-
// One unique name per fallback dir — collisions on fixed names cannot exhaust this.
|
|
1897
|
-
...uniqueFallbackDirs.map((dir) => join(dir, stem === UNIQUE_ERROR_FALLBACK_STEM ? uniqueErrorFallbackName() : `${stem}.${randomUUID()}.json`)),
|
|
1898
|
-
];
|
|
1899
|
-
for (let i = 0; i < candidates.length; i += 1) {
|
|
1900
|
-
const path = candidates[i]!;
|
|
1901
|
-
const payload =
|
|
1902
|
-
issues.length === 0
|
|
1903
|
-
? basePayload
|
|
1904
|
-
: { ...basePayload, publicationIssues: issues };
|
|
1905
|
-
try {
|
|
1906
|
-
await writeFile(
|
|
1907
|
-
path,
|
|
1908
|
-
`${JSON.stringify(payload, null, 2)}\n`,
|
|
1909
|
-
"utf8",
|
|
1910
|
-
);
|
|
1911
|
-
return { path, issues };
|
|
1912
|
-
} catch (error) {
|
|
1913
|
-
issues.push(publicationAttemptFromError(path, error));
|
|
1914
|
-
}
|
|
1915
|
-
}
|
|
1916
|
-
const last = issues.at(-1);
|
|
1917
|
-
const error = new Error(
|
|
1918
|
-
last?.diagnostic ?? "unable to write durable failure artifact",
|
|
1919
|
-
) as Error & {
|
|
1920
|
-
code?: string | number;
|
|
1921
|
-
publicationAttempts?: PublicationAttempt[];
|
|
1922
|
-
};
|
|
1923
|
-
if (last?.identity?.name !== undefined && last.identity.name !== "") {
|
|
1924
|
-
error.name = last.identity.name;
|
|
1925
|
-
}
|
|
1926
|
-
if (last?.identity?.code !== undefined) {
|
|
1927
|
-
error.code = last.identity.code;
|
|
1928
|
-
}
|
|
1929
|
-
error.publicationAttempts = issues;
|
|
1930
|
-
throw error;
|
|
1931
|
-
}
|
|
1932
|
-
|
|
1933
|
-
export async function publishFailureArtifacts(
|
|
1774
|
+
export async function publishFailureTerminal(
|
|
1934
1775
|
admitted: AdmittedRoleInvocation,
|
|
1935
1776
|
failure: ControlledFailure,
|
|
1936
|
-
|
|
1777
|
+
coordinates: DurablePrincipalCoordinates,
|
|
1937
1778
|
onErrorPublished?: (path: string) => void,
|
|
1938
|
-
|
|
1779
|
+
scope?: SettlementCourtScope,
|
|
1939
1780
|
): Promise<TerminalArtifactRef[]> {
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
const
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
// Re-projecting an existing failure does not append. History write failure
|
|
1948
|
-
// rides publicationIssues rather than stranding the controlled failure.
|
|
1949
|
-
if (recordAttemptHistory) {
|
|
1781
|
+
// #419: a dispatched failure joins history before the terminal changes. A history
|
|
1782
|
+
// write failure never strands the failure terminal; it is rethrown after it.
|
|
1783
|
+
let historyFailure: unknown;
|
|
1784
|
+
let historyFailed = false;
|
|
1785
|
+
const failureOutcome = { kind: "failure" as const, role: admitted.role, ...failure };
|
|
1786
|
+
let attemptHistoryIdentity: string | undefined;
|
|
1787
|
+
if (scope?.recordAttemptHistory === true) {
|
|
1950
1788
|
try {
|
|
1951
|
-
await appendRunAttemptHistory(
|
|
1952
|
-
|
|
1953
|
-
|
|
1789
|
+
const pointer = await appendRunAttemptHistory(
|
|
1790
|
+
attemptHistorySource(admitted, coordinates),
|
|
1791
|
+
failureOutcome,
|
|
1792
|
+
);
|
|
1793
|
+
attemptHistoryIdentity = pointer.identity;
|
|
1954
1794
|
} catch (error) {
|
|
1955
|
-
|
|
1795
|
+
historyFailure = error;
|
|
1796
|
+
historyFailed = true;
|
|
1956
1797
|
}
|
|
1798
|
+
} else {
|
|
1799
|
+
attemptHistoryIdentity = await belongingAttemptHistoryIdentity(
|
|
1800
|
+
admitted.runDirectory,
|
|
1801
|
+
scope,
|
|
1802
|
+
);
|
|
1957
1803
|
}
|
|
1958
|
-
|
|
1959
|
-
// the original controlled cause; preserve the issue beside the fallback.
|
|
1960
|
-
try {
|
|
1961
|
-
await clearOppositeTerminalArtifactFace(admitted.runDirectory);
|
|
1962
|
-
} catch (error) {
|
|
1963
|
-
priorIssues.push(publicationAttemptFromError(
|
|
1964
|
-
roleRunArtifactsDirectory(admitted.runDirectory), error,
|
|
1965
|
-
));
|
|
1966
|
-
}
|
|
1967
|
-
|
|
1968
|
-
// Prefer conventional names; unique fallback dirs keep colliding fixed paths
|
|
1969
|
-
// from stranding the original failure outside settlement. Include the ledger
|
|
1970
|
-
// runs/ parent so a locked run directory (EACCES) cannot exhaust durability.
|
|
1971
|
-
const underArtifacts = baseDir === roleRunArtifactsDirectory(admitted.runDirectory);
|
|
1972
|
-
const uniqueFallbackDirs = uniqueFailureFallbackDirs(
|
|
1973
|
-
admitted.runDirectory,
|
|
1974
|
-
baseDir,
|
|
1975
|
-
);
|
|
1976
|
-
const errorCandidates = underArtifacts
|
|
1977
|
-
? [
|
|
1978
|
-
join(baseDir, RUN_TERMINAL_ERROR_FILE),
|
|
1979
|
-
join(baseDir, RUN_TERMINAL_ERROR_SETTLEMENT_FILE),
|
|
1980
|
-
join(admitted.runDirectory, RUN_TERMINAL_ERROR_SETTLEMENT_FILE),
|
|
1981
|
-
]
|
|
1982
|
-
: [
|
|
1983
|
-
join(baseDir, RUN_TERMINAL_ERROR_SETTLEMENT_FILE),
|
|
1984
|
-
join(baseDir, RUN_TERMINAL_ERROR_FILE),
|
|
1985
|
-
];
|
|
1986
|
-
const evidenceCandidates = underArtifacts
|
|
1987
|
-
? [
|
|
1988
|
-
join(baseDir, RUN_TERMINAL_EVIDENCE_FILE),
|
|
1989
|
-
join(baseDir, "evidence.settlement.json"),
|
|
1990
|
-
join(admitted.runDirectory, "evidence.settlement.json"),
|
|
1991
|
-
]
|
|
1992
|
-
: [
|
|
1993
|
-
join(baseDir, "evidence.settlement.json"),
|
|
1994
|
-
join(baseDir, RUN_TERMINAL_EVIDENCE_FILE),
|
|
1995
|
-
];
|
|
1996
|
-
|
|
1997
|
-
const errorPayloadBase: Record<string, unknown> = {
|
|
1804
|
+
writeRunTerminal(admitted.runDirectory, "error", {
|
|
1998
1805
|
kind: "error",
|
|
1999
1806
|
role: admitted.role,
|
|
2000
1807
|
runId: admitted.runId,
|
|
@@ -2005,38 +1812,12 @@ export async function publishFailureArtifacts(
|
|
|
2005
1812
|
// This package's own facts, kept beside the host's report.
|
|
2006
1813
|
...(failure.packageFact === undefined ? {} : { packageFact: failure.packageFact }),
|
|
2007
1814
|
...(failure.stderr === undefined ? {} : { stderr: failure.stderr }),
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
const
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
errorPayloadBase,
|
|
2015
|
-
priorIssues,
|
|
2016
|
-
);
|
|
2017
|
-
onErrorPublished?.(errorWrite.path);
|
|
2018
|
-
|
|
2019
|
-
const evidencePayload: Record<string, unknown> = {
|
|
2020
|
-
runId: admitted.runId,
|
|
2021
|
-
sessionDirectory: sessionDirectory,
|
|
2022
|
-
sessionFile: sessionFile,
|
|
2023
|
-
admittedRequestPath: admitted.admittedRequestPath,
|
|
2024
|
-
attachments: acceptedArtifactAttachmentRefs(admitted.attachments),
|
|
2025
|
-
...(failure.cause === undefined ? {} : { failureCause: failure.cause }),
|
|
2026
|
-
};
|
|
2027
|
-
const evidenceWrite = await writeFailureJsonRetainingCause(
|
|
2028
|
-
evidenceCandidates,
|
|
2029
|
-
uniqueFallbackDirs,
|
|
2030
|
-
"evidence",
|
|
2031
|
-
evidencePayload,
|
|
2032
|
-
// Evidence records the same publication collisions observed placing the error body.
|
|
2033
|
-
errorWrite.issues,
|
|
2034
|
-
);
|
|
2035
|
-
|
|
2036
|
-
return [
|
|
2037
|
-
{ kind: "error", path: errorWrite.path },
|
|
2038
|
-
{ kind: "evidence", path: evidenceWrite.path },
|
|
2039
|
-
];
|
|
1815
|
+
...terminalBelongingFields(attemptHistoryIdentity, scope),
|
|
1816
|
+
});
|
|
1817
|
+
const path = runCurrentPath(admitted.runDirectory);
|
|
1818
|
+
onErrorPublished?.(path);
|
|
1819
|
+
if (historyFailed) throw historyFailure;
|
|
1820
|
+
return [{ kind: "error", path }];
|
|
2040
1821
|
}
|
|
2041
1822
|
|
|
2042
1823
|
/**
|
|
@@ -2062,13 +1843,7 @@ export async function settleFailureTerminalResult(
|
|
|
2062
1843
|
let artifacts: TerminalArtifactRef[] = [];
|
|
2063
1844
|
if (options.previewOnly !== true) {
|
|
2064
1845
|
try {
|
|
2065
|
-
artifacts = await
|
|
2066
|
-
admitted,
|
|
2067
|
-
failure,
|
|
2068
|
-
authority,
|
|
2069
|
-
options.onErrorPublished,
|
|
2070
|
-
options.recordAttemptHistory === true,
|
|
2071
|
-
);
|
|
1846
|
+
artifacts = await publishFailureTerminal(admitted, failure, coordinates, options.onErrorPublished, options);
|
|
2072
1847
|
} catch (error) {
|
|
2073
1848
|
await noteSettlementFault(
|
|
2074
1849
|
admitted.runDirectory,
|