@akagilnc/pi-workflow-roles 0.1.4021 → 0.1.4062
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 +682 -355
- package/dist/doctor-auditor.js +1 -1
- package/dist/dossier-resolution.js +21 -8
- package/dist/gatekeeper-role.js +34 -27
- package/dist/headless-host/description.js +7 -8
- package/dist/headless-host/production-host.js +709 -369
- package/dist/host-contracts.js +16 -0
- package/dist/inspector-contracts.js +8 -0
- package/dist/navigator-attendance.js +2 -0
- package/dist/navigator-public-session.js +18 -34
- package/dist/navigator-session-contracts.js +19 -0
- package/dist/packaged-role-registry.js +1 -1
- package/dist/pi/known-failure.js +3 -4
- package/dist/pi/role-turn-host.js +43 -13
- package/dist/public-cli/auto-resume.js +20 -7
- package/dist/public-cli/case-dossier-delivery.js +60 -1
- package/dist/public-cli/diarist-run.js +9 -23
- package/dist/public-cli/inspector-run.js +17 -3
- package/dist/public-cli/instruction-seat-run.js +12 -7
- package/dist/public-cli/judge-run.js +3 -1
- package/dist/public-cli/main.js +440 -222
- package/dist/public-cli/notary-run.js +7 -4
- package/dist/public-cli/post-admission.js +66 -59
- package/dist/public-cli/run-lifecycle.js +3 -0
- package/dist/public-cli/settlement.js +78 -58
- package/dist/public-cli/terminal.js +1 -11
- package/dist/public-role-summons.js +8 -0
- package/dist/submission-ledger.js +139 -19
- package/dist/user-dialogue-stdin.js +33 -0
- package/extensions/role-runtime.ts +1 -0
- package/package.json +1 -1
- package/resources/836-deleted-machine-instruction-inventory.md +1 -1
- package/src/doctor-auditor.ts +1 -1
- package/src/dossier-resolution.ts +26 -8
- package/src/external-host-turn-loop.ts +9 -0
- package/src/gatekeeper-pass-envelope.ts +6 -0
- package/src/gatekeeper-role.ts +47 -28
- package/src/headless-host/description.ts +8 -11
- package/src/headless-host/role-turn-host.ts +20 -5
- package/src/host-contracts.ts +30 -7
- package/src/inspector-contracts.ts +7 -0
- package/src/judge-role.ts +4 -0
- package/src/navigator-attendance.ts +2 -0
- package/src/navigator-public-session.ts +19 -55
- package/src/navigator-session-contracts.ts +39 -0
- package/src/navigator-work-context.ts +3 -4
- package/src/notary-role.ts +1 -0
- package/src/packaged-role-registry.ts +1 -1
- package/src/pi/adapter.ts +19 -4
- package/src/pi/known-failure.ts +3 -4
- package/src/pi/role-turn-host.ts +48 -14
- package/src/public-cli/auto-resume.ts +57 -20
- package/src/public-cli/case-dossier-delivery.ts +86 -1
- package/src/public-cli/cli.ts +2 -2
- package/src/public-cli/collector-run.ts +3 -1
- package/src/public-cli/countersign-run.ts +34 -27
- package/src/public-cli/diarist-run.ts +10 -27
- package/src/public-cli/inspector-run.ts +26 -2
- package/src/public-cli/instruction-seat-run.ts +20 -10
- package/src/public-cli/invocation.ts +2 -0
- package/src/public-cli/judge-run.ts +3 -1
- package/src/public-cli/notary-run.ts +13 -4
- package/src/public-cli/post-admission.ts +74 -65
- package/src/public-cli/reviewer-run.ts +3 -1
- package/src/public-cli/run-lifecycle.ts +3 -0
- package/src/public-cli/settlement.ts +105 -80
- package/src/public-cli/terminal.ts +7 -16
- package/src/public-role-summons.ts +31 -7
- package/src/role-envelope.ts +9 -30
- package/src/role-runtime-dependencies.ts +1 -0
- package/src/role-runtime.ts +95 -19
- package/src/submission-ledger.ts +195 -26
- package/src/user-dialogue-stdin.ts +37 -0
- package/src/worker-role.ts +4 -0
|
@@ -52,10 +52,10 @@ export async function runPublicNotary(argv, env, io, parseNotaryArgv) {
|
|
|
52
52
|
throw error;
|
|
53
53
|
}
|
|
54
54
|
// #747: officer resume key is this parent source-run path (not ticket number).
|
|
55
|
-
// #753/#
|
|
56
|
-
// (reask wins when both present; no parallel stack).
|
|
55
|
+
// #753/#879: gate re-ask and verbatim submission body share summons.instruction
|
|
56
|
+
// (reask wins when both present; no parallel stack). Binding stays sourceRunPath.
|
|
57
57
|
{
|
|
58
|
-
const resumeInstruction = env.reviewReask;
|
|
58
|
+
const resumeInstruction = env.reviewReask ?? env.gateReviewInstruction;
|
|
59
59
|
const summons = {
|
|
60
60
|
sourceRunPath: source.runDirectory,
|
|
61
61
|
sourceRun: source,
|
|
@@ -112,7 +112,10 @@ export async function runPublicNotary(argv, env, io, parseNotaryArgv) {
|
|
|
112
112
|
: { correlationId: env.correlationId }),
|
|
113
113
|
continuation: {
|
|
114
114
|
kind: "initial",
|
|
115
|
-
|
|
115
|
+
// #879: gate path first mint carries parent payload as dialogue content;
|
|
116
|
+
// external zero-prompt kickoff unchanged when no body/reask.
|
|
117
|
+
prompt: (env.reviewReask ?? env.gateReviewInstruction)
|
|
118
|
+
?? buildNotaryTransportPrompt(admitted, engineMaterial),
|
|
116
119
|
},
|
|
117
120
|
});
|
|
118
121
|
return await runPostAdmissionOneShot({
|
|
@@ -8,13 +8,14 @@
|
|
|
8
8
|
import { randomUUID } from "node:crypto";
|
|
9
9
|
import { readFile, writeFile } from "node:fs/promises";
|
|
10
10
|
import { isAbsolute, join, resolve } from "node:path";
|
|
11
|
-
import { buildResumeContinuationPrompt,
|
|
11
|
+
import { buildResumeContinuationPrompt, RESUME_TRANSPORT_ENVELOPE, } from "./run-lifecycle.js";
|
|
12
12
|
import { CliUsageError } from "./cli-errors.js";
|
|
13
13
|
import { buildInstructionTransportPrompt, freezeAttachmentsIntoRun, } from "./invocation.js";
|
|
14
14
|
import { pathContainedIn } from "../activation-ledger-topology.js";
|
|
15
15
|
import { pickEngineAxis } from "../package-resources/engine-material.js";
|
|
16
16
|
import { resolveHostAwareSessionAvailability } from "../session-identity.js";
|
|
17
|
-
import {
|
|
17
|
+
import { isOfficerReviewSeat } from "../host-contracts.js";
|
|
18
|
+
import { deliverCaseDossierAsAttachment, projectCaseDossierPointerSection, } from "./case-dossier-delivery.js";
|
|
18
19
|
/** Original error bytes, never relabeled — a secondary fact riding beside a classified cause. */
|
|
19
20
|
function describeCaughtError(error) {
|
|
20
21
|
if (error instanceof Error) {
|
|
@@ -30,6 +31,14 @@ function appendContinuationSection(continuation, section) {
|
|
|
30
31
|
? { kind: "initial", prompt }
|
|
31
32
|
: { kind: "resume", prompt };
|
|
32
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Nested gate summons (station child) on an officer seat: dialogue content is
|
|
36
|
+
* peer words only (#879). ADR 0081 case dossier still hangs via the existing
|
|
37
|
+
* attachments freeze seam — never into the peer body, never RoleTurnRequest.materials.
|
|
38
|
+
*/
|
|
39
|
+
function isStationChildOfficerDialogue(role, env) {
|
|
40
|
+
return env.stationChild === true && isOfficerReviewSeat(role);
|
|
41
|
+
}
|
|
33
42
|
import { projectHostTransitionPriorNative } from "../host-transition-prior-native.js";
|
|
34
43
|
import { missingCredentialPreDispatchFailure, postRunMissingCredentialFailure, } from "./public-run-credentials.js";
|
|
35
44
|
import { acquireRunWriterLease, clearCurrentCourt, clearTypedProviderHttpObservation, describeErrorIdentity, markRunRunning, readCurrentCourt, recordCurrentCourt, renderResumeCommand, RunWriterLeaseHeldError, } from "./run-lifecycle.js";
|
|
@@ -324,9 +333,9 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
324
333
|
// Turn request is assembled after beforeDispatch so this turn sees whatever it
|
|
325
334
|
// settled — the seat's ticket bind re-projection and any court diarist station
|
|
326
335
|
// writes (#742). Case dossier delivery (ADR 0081 / #709) rides here once for
|
|
327
|
-
// every public entry:
|
|
328
|
-
//
|
|
329
|
-
//
|
|
336
|
+
// every public entry. #879: station-child officer dialogue keeps peer body
|
|
337
|
+
// intact — 起居录 hangs via existing attachments freeze (not prompt wrap,
|
|
338
|
+
// not RoleTurnRequest.materials). Other entries keep the neutral prompt section.
|
|
330
339
|
let turnRequest = env.signal === undefined ? request : { ...request, signal: env.signal };
|
|
331
340
|
if (env.stationChild !== undefined) {
|
|
332
341
|
turnRequest = { ...turnRequest, stationChild: env.stationChild };
|
|
@@ -334,16 +343,31 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
334
343
|
if (hostTransition !== undefined) {
|
|
335
344
|
turnRequest = { ...turnRequest, hostTransition };
|
|
336
345
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
346
|
+
if (isStationChildOfficerDialogue(admitted.role, env)) {
|
|
347
|
+
// 0081 non-body face: freeze pointer section under run/attachments/.
|
|
348
|
+
// Peer dialogue continuation.prompt stays parent payload only — the seat
|
|
349
|
+
// consumes the freeze via loadCaseDossierReadingMaterial → existing
|
|
350
|
+
// agent-start readingMaterial / systemPrompt.materials fold (not prompt splice,
|
|
351
|
+
// not RoleTurnRequest.materials).
|
|
352
|
+
await deliverCaseDossierAsAttachment({
|
|
353
|
+
ticketNumber: admitted.ticketNumber,
|
|
354
|
+
projectRoot: admitted.projectRoot,
|
|
355
|
+
home: env.home,
|
|
356
|
+
runDirectory: admitted.runDirectory,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
const dossierSection = await projectCaseDossierPointerSection({
|
|
361
|
+
ticketNumber: admitted.ticketNumber,
|
|
362
|
+
projectRoot: admitted.projectRoot,
|
|
363
|
+
home: env.home,
|
|
364
|
+
});
|
|
365
|
+
if (dossierSection !== undefined) {
|
|
366
|
+
turnRequest = {
|
|
367
|
+
...turnRequest,
|
|
368
|
+
continuation: appendContinuationSection(turnRequest.continuation, dossierSection),
|
|
369
|
+
};
|
|
370
|
+
}
|
|
347
371
|
}
|
|
348
372
|
// Authoritative host write happens here, at the real dispatch boundary —
|
|
349
373
|
// immediately before the turn actually starts, after every retryable
|
|
@@ -643,46 +667,40 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
643
667
|
}
|
|
644
668
|
}
|
|
645
669
|
/**
|
|
646
|
-
* Shared resume continuation projection (#471 / #600 / #633 / #637 / #755):
|
|
670
|
+
* Shared resume continuation projection (#471 / #600 / #633 / #637 / #755 / #879):
|
|
647
671
|
* seat-table model/engine/timeout axes, restored correlation, and either
|
|
648
672
|
* - manual resume (no same-ticket summons): package envelope / optional caller
|
|
649
673
|
* message, with engine-axis handbook via buildResumeContinuationPrompt, or
|
|
650
674
|
* - same-ticket summons (审核循环续话): caller/peer words + optional frozen
|
|
651
|
-
* attachment paths only — no
|
|
652
|
-
* (#750/#755)
|
|
675
|
+
* attachment paths only — no「请重读」、no code-authored content substitute,
|
|
676
|
+
* no engine handbook packaging (#750/#755/#879).
|
|
653
677
|
* Caller message wins as prompt base when supplied (bytes unchanged, including
|
|
654
|
-
* blank/whitespace); else summons instruction
|
|
655
|
-
*
|
|
678
|
+
* blank/whitespace); else summons instruction (parent payload or reask).
|
|
679
|
+
* Binding pointer stays on summons.sourceRunPath / activation — not dialogue content.
|
|
656
680
|
* Seats add only their activation projection. Call prepareSummonsResumeMaterials
|
|
657
681
|
* first when request.summons carries instruction or attachment paths.
|
|
658
682
|
*/
|
|
659
683
|
export function resumeTurnRequestProjectionOptions(admitted, request, env, summonsPrepared) {
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
&& admitted.role !== "inspector"
|
|
665
|
-
&& admitted.role !== "auditor")) {
|
|
666
|
-
return body;
|
|
667
|
-
}
|
|
668
|
-
if (body.startsWith("请重读"))
|
|
669
|
-
return body;
|
|
670
|
-
return `请重读\n${body}`;
|
|
671
|
-
};
|
|
684
|
+
// #879: officer dialogue content is caller/peer words only — no「请重读」,
|
|
685
|
+
// no code-authored constant substitute, no attachment-list wrap of peer body.
|
|
686
|
+
// Binding pointer stays on summons sourceRunPath / activation / attachments.
|
|
687
|
+
const officerDialogue = isStationChildOfficerDialogue(admitted.role, env);
|
|
672
688
|
let prompt;
|
|
673
689
|
if (request.message !== undefined) {
|
|
674
690
|
if (summonsPrepared !== undefined) {
|
|
675
|
-
// #755: same-ticket review / open-court — caller words
|
|
676
|
-
//
|
|
677
|
-
prompt =
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
691
|
+
// #755: same-ticket review / open-court — caller words.
|
|
692
|
+
// #879 station-child officer: words only (attachments are independent freeze).
|
|
693
|
+
prompt = officerDialogue
|
|
694
|
+
? request.message
|
|
695
|
+
: buildInstructionTransportPrompt({
|
|
696
|
+
instruction: request.message,
|
|
697
|
+
instructionEmpty: false,
|
|
698
|
+
attachments: summonsPrepared.attachments,
|
|
699
|
+
});
|
|
682
700
|
}
|
|
683
701
|
else if (request.summons !== undefined) {
|
|
684
702
|
// #755: same-ticket summons without prepared materials — caller words only.
|
|
685
|
-
prompt =
|
|
703
|
+
prompt = request.message;
|
|
686
704
|
}
|
|
687
705
|
else {
|
|
688
706
|
// Bare manual resume — outsourcing engine axis keeps handbook (#600/#736).
|
|
@@ -694,27 +712,16 @@ export function resumeTurnRequestProjectionOptions(admitted, request, env, summo
|
|
|
694
712
|
}
|
|
695
713
|
}
|
|
696
714
|
else if (summonsPrepared !== undefined) {
|
|
697
|
-
// #
|
|
698
|
-
|
|
715
|
+
// #879 station-child officer: instruction bytes === peer body/reask (no wrap).
|
|
716
|
+
// Other seats keep #755 instruction + optional attachment path listing.
|
|
717
|
+
prompt = officerDialogue
|
|
718
|
+
? (summonsPrepared.instructionEmpty ? "" : summonsPrepared.instruction)
|
|
719
|
+
: buildInstructionTransportPrompt(summonsPrepared);
|
|
699
720
|
}
|
|
700
721
|
else if (request.summons !== undefined) {
|
|
701
|
-
// #
|
|
702
|
-
//
|
|
703
|
-
|
|
704
|
-
if (path !== undefined &&
|
|
705
|
-
(admitted.role === "notary" ||
|
|
706
|
-
admitted.role === "inspector" ||
|
|
707
|
-
admitted.role === "auditor")) {
|
|
708
|
-
prompt = `请重读\n${GATE_DOSSIER_POINTER_PREFIX}${path}`;
|
|
709
|
-
}
|
|
710
|
-
else if (admitted.role === "notary" ||
|
|
711
|
-
admitted.role === "inspector" ||
|
|
712
|
-
admitted.role === "auditor") {
|
|
713
|
-
prompt = "请重读";
|
|
714
|
-
}
|
|
715
|
-
else {
|
|
716
|
-
prompt = "";
|
|
717
|
-
}
|
|
722
|
+
// #879: same-ticket summons with no instruction (e.g. notary source-run binding
|
|
723
|
+
// only). Pointer is activation/sourceRun material — not dialogue content.
|
|
724
|
+
prompt = "";
|
|
718
725
|
}
|
|
719
726
|
else {
|
|
720
727
|
// Bare manual resume — outsourcing engine axis keeps handbook (#600/#736).
|
|
@@ -1389,6 +1389,9 @@ export async function loadResumableInspectorRun(home, runId, authority) {
|
|
|
1389
1389
|
const admitted = {
|
|
1390
1390
|
role: "inspector",
|
|
1391
1391
|
...resumedBaseAdmitted(loaded),
|
|
1392
|
+
...(loaded.admittedFields.sourceRunPath === undefined
|
|
1393
|
+
? {}
|
|
1394
|
+
: { sourceRunPath: loaded.admittedFields.sourceRunPath }),
|
|
1392
1395
|
};
|
|
1393
1396
|
return seatLoadedResult(loaded, admitted);
|
|
1394
1397
|
}
|
|
@@ -8,7 +8,7 @@ import { appendFile, lstat, mkdir, readFile, readdir, writeFile } from "node:fs/
|
|
|
8
8
|
import { dirname, join } from "node:path";
|
|
9
9
|
import { readAnalystGateCyclesFromAuditorRoles, } from "../analyst-gate-cycles-read.js";
|
|
10
10
|
import { readSitianRecords, resolveSitianRecordPath, sitianReport } from "../sitian-facade.js";
|
|
11
|
-
import { hasFreshAttemptSubmission, readRecordedSubmissionRows, readRecordedSubmissions, } from "../submission-ledger.js";
|
|
11
|
+
import { hasFreshAttemptSubmission, readAttemptScopedSubmissionRows, readRecordedSubmissionRows, readRecordedSubmissions, } from "../submission-ledger.js";
|
|
12
12
|
import { isAuditEscalationResult } from "../audit-escalation.js";
|
|
13
13
|
import { AUDITOR_SOUL_ROLES } from "../auditor-soul.js";
|
|
14
14
|
import { DOCTOR_AUDIT_TOOL_NAME } from "../doctor-auditor.js";
|
|
@@ -54,15 +54,30 @@ function ledgerReadScope(admitted, scope) {
|
|
|
54
54
|
}
|
|
55
55
|
function roleOutcomeFromRows(role, rows) {
|
|
56
56
|
const mine = rows.filter((row) => row.role === role);
|
|
57
|
-
|
|
57
|
+
// Terminal acceptance kind still only follows sealed / audit-escalation (#881):
|
|
58
|
+
// correctable-rejection / infrastructure / candidate stay payloads, not acceptance.
|
|
59
|
+
const terminal = mine.filter((row) => row.kind === "accepted" || row.kind === "audit-escalation");
|
|
60
|
+
if (terminal.length === 0)
|
|
58
61
|
return undefined;
|
|
62
|
+
// Role-result block is the full recorded sequence for this seat, not a sole pick.
|
|
59
63
|
const payloads = mine.map((row) => row.accepted);
|
|
60
|
-
if (
|
|
64
|
+
if (terminal.some((row) => row.kind === "audit-escalation")) {
|
|
61
65
|
return { kind: "audit_escalation", role, status: "audit_escalation", payloads };
|
|
62
66
|
}
|
|
63
67
|
return { kind: "accepted", role, payloads };
|
|
64
68
|
}
|
|
65
69
|
async function sealedLedgerOutcome(admitted, role, scope) {
|
|
70
|
+
const home = sealedLedgerHome(admitted);
|
|
71
|
+
// #879: when court scope is present, roleOutcome is this-court original only —
|
|
72
|
+
// never last-wins over an undivided historical array, and never falls back to
|
|
73
|
+
// full-run history when this court sealed zero rows. #836 presentation of full
|
|
74
|
+
// history stays on attachRecordedSubmissions / terminal.submissions (run-scoped).
|
|
75
|
+
if (scope?.courtAttemptId !== undefined && scope.courtAttemptId.length > 0) {
|
|
76
|
+
const thisCourt = await readAttemptScopedSubmissionRows(admitted.projectRoot, admitted.runId, scope.courtAttemptId, home);
|
|
77
|
+
// Scope present + zero this-court rows → no this-court outcome (not run history).
|
|
78
|
+
return roleOutcomeFromRows(role, thisCourt);
|
|
79
|
+
}
|
|
80
|
+
// No court scope: keep run-scoped ledger face (#836).
|
|
66
81
|
const rows = await readRecordedSubmissionRows(admitted.projectRoot, admitted.runId, ledgerReadScope(admitted, scope));
|
|
67
82
|
return roleOutcomeFromRows(role, rows);
|
|
68
83
|
}
|
|
@@ -89,6 +104,8 @@ export function withSubmissions(terminal, submissions) {
|
|
|
89
104
|
if (submissions.length === 0)
|
|
90
105
|
return terminal;
|
|
91
106
|
const roleOutcome = terminal.roleOutcome;
|
|
107
|
+
// #879 keeps an already scoped this-court result; #881 full run history is
|
|
108
|
+
// presented independently on terminal.submissions. Only fill a missing result payload.
|
|
92
109
|
const withPayloads = roleOutcome.kind === "accepted" || roleOutcome.kind === "audit_escalation"
|
|
93
110
|
? { ...roleOutcome, payloads: (roleOutcome.payloads ?? []).length > 0 ? roleOutcome.payloads : submissions }
|
|
94
111
|
: roleOutcome.kind === "failure"
|
|
@@ -98,7 +115,11 @@ export function withSubmissions(terminal, submissions) {
|
|
|
98
115
|
}
|
|
99
116
|
/** Attach full ledger submissions onto any settled terminal (#836). */
|
|
100
117
|
export async function attachRecordedSubmissions(admitted, terminal, scope) {
|
|
101
|
-
|
|
118
|
+
// #836 / #879: submissions presentation is always run-scoped history. Do not
|
|
119
|
+
// pass courtAttemptId here — roleOutcome already carries this-court payloads
|
|
120
|
+
// from sealedLedgerOutcome when scoped; withSubmissions keeps them.
|
|
121
|
+
void scope;
|
|
122
|
+
return withSubmissions(terminal, await recordedSubmissionPayloads(admitted, undefined));
|
|
102
123
|
}
|
|
103
124
|
/**
|
|
104
125
|
* Host ended with no recorded submission — lawful no_receipt (exit 0).
|
|
@@ -133,7 +154,7 @@ async function closedLedgerOutcome(admitted, role, scope) {
|
|
|
133
154
|
function coordinatesFromAdmitted(authority, admitted) {
|
|
134
155
|
return authority.decode(admitted.principal);
|
|
135
156
|
}
|
|
136
|
-
import { exitCodeForTerminalOutcome, formatTerminalResult, isLawfulTypedTerminalOutcome,
|
|
157
|
+
import { exitCodeForTerminalOutcome, formatTerminalResult, isLawfulTypedTerminalOutcome, recommendationNavigatorFact, } from "./terminal.js";
|
|
137
158
|
export { exitCodeForTerminalOutcome, formatTerminalResult, isLawfulTypedTerminalOutcome, };
|
|
138
159
|
/**
|
|
139
160
|
* Host stderr as recorded — full bytes, no flood filter, no char clip (#836).
|
|
@@ -223,8 +244,7 @@ function isTypedActivationError(error) {
|
|
|
223
244
|
cause === "activation" ||
|
|
224
245
|
cause === "session" ||
|
|
225
246
|
cause === "output" ||
|
|
226
|
-
cause === "timeout"
|
|
227
|
-
cause === "unrecognized");
|
|
247
|
+
cause === "timeout");
|
|
228
248
|
}
|
|
229
249
|
/** Flatten nested AggregateError leaves; non-aggregate values stay as one fact. */
|
|
230
250
|
function flattenThrownFailureLeaves(error) {
|
|
@@ -250,21 +270,20 @@ export function projectThrownFailureLeaf(error) {
|
|
|
250
270
|
}
|
|
251
271
|
return {
|
|
252
272
|
cause: error.knownCause,
|
|
253
|
-
diagnostic: error.message || error.name || "
|
|
273
|
+
diagnostic: error.message || error.name || "exception",
|
|
254
274
|
identity,
|
|
255
275
|
...(error.details === undefined ? {} : { details: error.details }),
|
|
256
276
|
};
|
|
257
277
|
}
|
|
258
278
|
if (error instanceof Error) {
|
|
259
279
|
const identity = thrownIdentity(error);
|
|
280
|
+
// No typed confirmation → keep original diagnostic/identity; do not mint a class (#881).
|
|
260
281
|
return {
|
|
261
|
-
|
|
262
|
-
diagnostic: error.message || error.name || "unrecognized exception",
|
|
282
|
+
diagnostic: error.message || error.name || "exception",
|
|
263
283
|
identity,
|
|
264
284
|
};
|
|
265
285
|
}
|
|
266
286
|
return {
|
|
267
|
-
cause: "unrecognized",
|
|
268
287
|
diagnostic: String(error),
|
|
269
288
|
};
|
|
270
289
|
}
|
|
@@ -294,7 +313,7 @@ function classifyThrownFailure(error) {
|
|
|
294
313
|
...leaves.slice(1).map((leaf) => {
|
|
295
314
|
const secondary = projectThrownFailureLeaf(leaf);
|
|
296
315
|
return {
|
|
297
|
-
cause: secondary.cause,
|
|
316
|
+
...(secondary.cause === undefined ? {} : { cause: secondary.cause }),
|
|
298
317
|
diagnostic: secondary.diagnostic,
|
|
299
318
|
...(secondary.identity === undefined ? {} : { identity: secondary.identity }),
|
|
300
319
|
...(secondary.details === undefined ? {} : { details: secondary.details }),
|
|
@@ -302,7 +321,7 @@ function classifyThrownFailure(error) {
|
|
|
302
321
|
}),
|
|
303
322
|
];
|
|
304
323
|
return {
|
|
305
|
-
cause: primary.cause,
|
|
324
|
+
...(primary.cause === undefined ? {} : { cause: primary.cause }),
|
|
306
325
|
diagnostic: primary.diagnostic,
|
|
307
326
|
...(primary.identity === undefined ? {} : { identity: primary.identity }),
|
|
308
327
|
details: {
|
|
@@ -325,7 +344,7 @@ function withKnownDetails(failure, knownDetails) {
|
|
|
325
344
|
};
|
|
326
345
|
}
|
|
327
346
|
/**
|
|
328
|
-
* Classify a controlled post-admission failure without washing
|
|
347
|
+
* Classify a controlled post-admission failure without washing original identities.
|
|
329
348
|
* Cause classes are closed; diagnostic text retains the original identity when known.
|
|
330
349
|
*
|
|
331
350
|
* Order: thrown → knownCause → timeout → activation (nonzero) → session → output.
|
|
@@ -365,6 +384,30 @@ export function classifyPostAdmissionFailure(input) {
|
|
|
365
384
|
: { identity: input.knownIdentity }),
|
|
366
385
|
};
|
|
367
386
|
}
|
|
387
|
+
// #881: original failure testimony without a typed class — keep diagnostic/identity;
|
|
388
|
+
// do not fall through to activation/output wash that would mint a substitute class.
|
|
389
|
+
// Bare knownDetails alone is secondary evidence for later branches (withKnownDetails),
|
|
390
|
+
// not a stand-in primary failure record.
|
|
391
|
+
if ((input.knownDiagnostic !== undefined && input.knownDiagnostic.trim() !== "") ||
|
|
392
|
+
input.knownIdentity !== undefined) {
|
|
393
|
+
const diagnostic = input.knownDiagnostic !== undefined && input.knownDiagnostic.trim() !== ""
|
|
394
|
+
? input.knownDiagnostic
|
|
395
|
+
: conciseChildDiagnostic(input.stderr, "role run failed");
|
|
396
|
+
const { timedOut: _knownTimedOut, ...knownDetails } = input.knownDetails ?? {};
|
|
397
|
+
const remoteCode = knownDetails.code;
|
|
398
|
+
return withKnownDetails({
|
|
399
|
+
diagnostic,
|
|
400
|
+
details: {
|
|
401
|
+
...knownDetails,
|
|
402
|
+
...(remoteCode === undefined ? {} : { code: remoteCode }),
|
|
403
|
+
exitCode: input.code,
|
|
404
|
+
...(input.timedOut ? { timedOut: true } : {}),
|
|
405
|
+
},
|
|
406
|
+
...(input.knownIdentity === undefined
|
|
407
|
+
? {}
|
|
408
|
+
: { identity: input.knownIdentity }),
|
|
409
|
+
}, undefined);
|
|
410
|
+
}
|
|
368
411
|
if (input.timedOut) {
|
|
369
412
|
return withKnownDetails({
|
|
370
413
|
cause: "timeout",
|
|
@@ -405,7 +448,7 @@ export function explicitInternalKnownFailureClassificationInput(failure) {
|
|
|
405
448
|
if (failure === undefined)
|
|
406
449
|
return {};
|
|
407
450
|
return {
|
|
408
|
-
knownCause: failure.cause,
|
|
451
|
+
...(failure.cause === undefined ? {} : { knownCause: failure.cause }),
|
|
409
452
|
...(failure.identity === undefined ? {} : { knownIdentity: failure.identity }),
|
|
410
453
|
...(failure.diagnostic === undefined ? {} : { knownDiagnostic: failure.diagnostic }),
|
|
411
454
|
...(failure.details === undefined ? {} : { knownDetails: failure.details }),
|
|
@@ -755,11 +798,21 @@ function complianceFailureFromAuditorVolumes(volumes) {
|
|
|
755
798
|
continue;
|
|
756
799
|
const parent = isRecord(entry.data.parent) ? entry.data.parent : undefined;
|
|
757
800
|
const failure = isRecord(entry.data.failure) ? entry.data.failure : undefined;
|
|
758
|
-
if (parent?.sessionId !== parentId || parent.sessionFile !== sessionFile || parent.attemptEntryId !== attemptEntryId
|
|
801
|
+
if (parent?.sessionId !== parentId || parent.sessionFile !== sessionFile || parent.attemptEntryId !== attemptEntryId)
|
|
802
|
+
continue;
|
|
803
|
+
// #881: keep the recorded failure as written — typed cause when present, else raw diagnostic only.
|
|
804
|
+
if (failure === undefined)
|
|
759
805
|
continue;
|
|
760
806
|
const identity = isRecord(failure.identity) ? failure.identity : undefined;
|
|
807
|
+
const typedCause = failure.cause === "provider" ||
|
|
808
|
+
failure.cause === "activation" ||
|
|
809
|
+
failure.cause === "session" ||
|
|
810
|
+
failure.cause === "output" ||
|
|
811
|
+
failure.cause === "timeout"
|
|
812
|
+
? failure.cause
|
|
813
|
+
: undefined;
|
|
761
814
|
return {
|
|
762
|
-
|
|
815
|
+
...(typedCause === undefined ? {} : { cause: typedCause }),
|
|
763
816
|
...(identity === undefined ? {} : { identity: {
|
|
764
817
|
...(typeof identity.name === "string" ? { name: identity.name } : {}),
|
|
765
818
|
...(typeof identity.code === "string" || typeof identity.code === "number" ? { code: identity.code } : {}),
|
|
@@ -1683,9 +1736,6 @@ export async function publishCoderArtifacts(admitted, roleOutcome, coordinates,
|
|
|
1683
1736
|
runId: admitted.runId,
|
|
1684
1737
|
phase: admitted.phase,
|
|
1685
1738
|
outcome: roleOutcome,
|
|
1686
|
-
...(options.coderOutput === undefined
|
|
1687
|
-
? {}
|
|
1688
|
-
: { receipt: options.coderOutput }),
|
|
1689
1739
|
}, null, 2)}\n`, "utf8");
|
|
1690
1740
|
await writeFile(evidencePath, `${JSON.stringify({
|
|
1691
1741
|
runId: admitted.runId,
|
|
@@ -1789,14 +1839,10 @@ async function settleLawfulCoderTerminalResult(admitted, authority, options = {}
|
|
|
1789
1839
|
if (ledgerOutcome === undefined)
|
|
1790
1840
|
return undefined;
|
|
1791
1841
|
const roleOutcome = ledgerOutcome;
|
|
1792
|
-
const output = ledgerOutcome.kind === "accepted"
|
|
1793
|
-
? lastRolePayloadRecord(ledgerOutcome.payloads ?? [])
|
|
1794
|
-
: undefined;
|
|
1795
1842
|
const coordinates = coordinatesFromAdmitted(authority, admitted);
|
|
1796
1843
|
const entries = await readLawfulSettlementEntries(coordinates.sessionFile) ?? [];
|
|
1797
1844
|
const navigator = extractNavigatorFact(entries);
|
|
1798
1845
|
const artifacts = await publishCoderArtifacts(admitted, roleOutcome, coordinates, {
|
|
1799
|
-
...(output === undefined ? {} : { coderOutput: output }),
|
|
1800
1846
|
...(options.methodProvenance === undefined
|
|
1801
1847
|
? {}
|
|
1802
1848
|
: { methodProvenance: options.methodProvenance }),
|
|
@@ -1873,9 +1919,6 @@ export async function publishFixerArtifacts(admitted, roleOutcome, coordinates,
|
|
|
1873
1919
|
runId: admitted.runId,
|
|
1874
1920
|
phase: admitted.phase,
|
|
1875
1921
|
outcome: roleOutcome,
|
|
1876
|
-
...(options.fixerOutput === undefined
|
|
1877
|
-
? {}
|
|
1878
|
-
: { receipt: options.fixerOutput }),
|
|
1879
1922
|
}, null, 2)}\n`, "utf8");
|
|
1880
1923
|
await writeFile(evidencePath, `${JSON.stringify({
|
|
1881
1924
|
runId: admitted.runId,
|
|
@@ -1910,9 +1953,6 @@ async function settleLawfulFixerTerminalResult(admitted, authority, options, sco
|
|
|
1910
1953
|
if (ledgerOutcome === undefined)
|
|
1911
1954
|
return undefined;
|
|
1912
1955
|
const roleOutcome = ledgerOutcome;
|
|
1913
|
-
const output = ledgerOutcome.kind === "accepted"
|
|
1914
|
-
? lastRolePayloadRecord(ledgerOutcome.payloads ?? [])
|
|
1915
|
-
: undefined;
|
|
1916
1956
|
const coordinates = coordinatesFromAdmitted(authority, admitted);
|
|
1917
1957
|
const { sessionDirectory, sessionFile } = coordinates;
|
|
1918
1958
|
const entries = await readLawfulSettlementEntries(sessionFile) ?? [];
|
|
@@ -1924,7 +1964,6 @@ async function settleLawfulFixerTerminalResult(admitted, authority, options, sco
|
|
|
1924
1964
|
],
|
|
1925
1965
|
});
|
|
1926
1966
|
const artifacts = await publishFixerArtifacts(admitted, roleOutcome, coordinates, {
|
|
1927
|
-
...(output === undefined ? {} : { fixerOutput: output }),
|
|
1928
1967
|
methodProvenance: options.methodProvenance,
|
|
1929
1968
|
methodInvocations,
|
|
1930
1969
|
});
|
|
@@ -1943,7 +1982,7 @@ export async function settleFixerTerminalResult(admitted, authority, options, sc
|
|
|
1943
1982
|
}
|
|
1944
1983
|
return settled;
|
|
1945
1984
|
}
|
|
1946
|
-
export async function publishCollectorArtifacts(admitted, roleOutcome, coordinates
|
|
1985
|
+
export async function publishCollectorArtifacts(admitted, roleOutcome, coordinates) {
|
|
1947
1986
|
await appendRunAttemptHistory({ role: admitted.role, runId: admitted.runId, sessionFile: coordinates.sessionFile }, roleOutcome);
|
|
1948
1987
|
const artifactsDir = await ensureRunArtifactsDir(admitted.runDirectory);
|
|
1949
1988
|
const reportPath = join(artifactsDir, "report.json");
|
|
@@ -1952,9 +1991,6 @@ export async function publishCollectorArtifacts(admitted, roleOutcome, coordinat
|
|
|
1952
1991
|
role: "collector",
|
|
1953
1992
|
runId: admitted.runId,
|
|
1954
1993
|
outcome: roleOutcome,
|
|
1955
|
-
...(options.collectorReceipt === undefined
|
|
1956
|
-
? {}
|
|
1957
|
-
: { receipt: options.collectorReceipt }),
|
|
1958
1994
|
}, null, 2)}\n`, "utf8");
|
|
1959
1995
|
await writeFile(evidencePath, `${JSON.stringify({
|
|
1960
1996
|
runId: admitted.runId,
|
|
@@ -2004,10 +2040,9 @@ async function settleLawfulCollectorTerminalResult(admitted, authority, scope) {
|
|
|
2004
2040
|
}
|
|
2005
2041
|
return undefined;
|
|
2006
2042
|
}
|
|
2007
|
-
const receipt = (lastRolePayloadRecord(roleOutcome.payloads ?? []) ?? {});
|
|
2008
2043
|
const accepted = roleOutcome;
|
|
2009
2044
|
const navigator = extractNavigatorFact(entries);
|
|
2010
|
-
const artifacts = await publishCollectorArtifacts(admitted, accepted, coordinates
|
|
2045
|
+
const artifacts = await publishCollectorArtifacts(admitted, accepted, coordinates);
|
|
2011
2046
|
return attachRecordedSubmissions(admitted, await withOptionalGateProjection({
|
|
2012
2047
|
roleOutcome: accepted,
|
|
2013
2048
|
navigator,
|
|
@@ -2069,10 +2104,6 @@ export async function publishDoctorArtifacts(admitted, roleOutcome, coordinates,
|
|
|
2069
2104
|
role: "doctor",
|
|
2070
2105
|
runId: admitted.runId,
|
|
2071
2106
|
outcome: roleOutcome,
|
|
2072
|
-
...(options.doctorOutput === undefined
|
|
2073
|
-
? {}
|
|
2074
|
-
: { receipt: options.doctorOutput }),
|
|
2075
|
-
// Independent machine fields beside the receipt — not merged into it.
|
|
2076
2107
|
...(options.cost === undefined ? {} : { cost: options.cost }),
|
|
2077
2108
|
...(options.auditNoReceipt === undefined ? {} : { auditNoReceipt: options.auditNoReceipt }),
|
|
2078
2109
|
}, null, 2)}\n`, "utf8");
|
|
@@ -2113,13 +2144,11 @@ async function settleLawfulDoctorTerminalResult(admitted, authority, scope) {
|
|
|
2113
2144
|
runId: admitted.runId,
|
|
2114
2145
|
}, sessionDirectory);
|
|
2115
2146
|
}
|
|
2116
|
-
const output = (lastRolePayloadRecord(sealed.payloads ?? []) ?? {});
|
|
2117
2147
|
const roleOutcome = sealed;
|
|
2118
2148
|
const navigator = extractNavigatorFact(entries);
|
|
2119
2149
|
const cost = extractDoctorCandidateCostFact(entries);
|
|
2120
2150
|
const auditNoReceipt = extractDoctorCandidateAuditNoReceiptFact(entries);
|
|
2121
2151
|
const artifacts = await publishDoctorArtifacts(admitted, roleOutcome, coordinates, {
|
|
2122
|
-
doctorOutput: output,
|
|
2123
2152
|
...(cost === undefined ? {} : { cost }),
|
|
2124
2153
|
...(auditNoReceipt === undefined ? {} : { auditNoReceipt }),
|
|
2125
2154
|
});
|
|
@@ -2346,9 +2375,6 @@ export async function publishReviewerArtifacts(admitted, roleOutcome, coordinate
|
|
|
2346
2375
|
role: "reviewer",
|
|
2347
2376
|
runId: admitted.runId,
|
|
2348
2377
|
outcome: roleOutcome,
|
|
2349
|
-
...(options.reviewerReceipt === undefined
|
|
2350
|
-
? {}
|
|
2351
|
-
: { receipt: options.reviewerReceipt }),
|
|
2352
2378
|
}, null, 2)}\n`, "utf8");
|
|
2353
2379
|
await writeFile(evidencePath, `${JSON.stringify({
|
|
2354
2380
|
runId: admitted.runId,
|
|
@@ -2384,7 +2410,6 @@ async function settleLawfulReviewerTerminalResult(admitted, authority, options,
|
|
|
2384
2410
|
const coordinates = coordinatesFromAdmitted(authority, admitted);
|
|
2385
2411
|
const { sessionDirectory, sessionFile } = coordinates;
|
|
2386
2412
|
const entries = await readLawfulSettlementEntries(sessionFile) ?? [];
|
|
2387
|
-
const receipt = lastRolePayloadRecord(sealed.payloads ?? []);
|
|
2388
2413
|
const roleOutcome = sealed;
|
|
2389
2414
|
const navigator = extractNavigatorFact(entries);
|
|
2390
2415
|
const methodInvocations = extractReviewerMethodInvocations(entries, {
|
|
@@ -2394,7 +2419,6 @@ async function settleLawfulReviewerTerminalResult(admitted, authority, options,
|
|
|
2394
2419
|
],
|
|
2395
2420
|
});
|
|
2396
2421
|
const artifacts = await publishReviewerArtifacts(admitted, roleOutcome, coordinates, {
|
|
2397
|
-
...(receipt === undefined ? {} : { reviewerReceipt: receipt }),
|
|
2398
2422
|
methodProvenance: options.methodProvenance,
|
|
2399
2423
|
methodInvocations,
|
|
2400
2424
|
});
|
|
@@ -2455,9 +2479,6 @@ export async function publishMergerArtifacts(admitted, roleOutcome, coordinates,
|
|
|
2455
2479
|
role: "merger",
|
|
2456
2480
|
runId: admitted.runId,
|
|
2457
2481
|
outcome: roleOutcome,
|
|
2458
|
-
...(options.mergerOutput === undefined
|
|
2459
|
-
? {}
|
|
2460
|
-
: { receipt: options.mergerOutput }),
|
|
2461
2482
|
}, null, 2)}\n`, "utf8");
|
|
2462
2483
|
await writeFile(evidencePath, `${JSON.stringify({
|
|
2463
2484
|
runId: admitted.runId,
|
|
@@ -2513,7 +2534,6 @@ async function settleLawfulMergerTerminalResult(admitted, authority, options, sc
|
|
|
2513
2534
|
allowedLocations: [options.methodSkillPath, options.methodSkillConfiguredPath],
|
|
2514
2535
|
});
|
|
2515
2536
|
const artifacts = await publishMergerArtifacts(admitted, accepted, coordinates, {
|
|
2516
|
-
mergerOutput: (lastRolePayloadRecord(accepted.payloads ?? []) ?? {}),
|
|
2517
2537
|
methodProvenance: options.methodProvenance,
|
|
2518
2538
|
methodInvocations,
|
|
2519
2539
|
});
|
|
@@ -2666,7 +2686,7 @@ export async function publishFailureArtifacts(admitted, failure, authority) {
|
|
|
2666
2686
|
kind: "error",
|
|
2667
2687
|
role: admitted.role,
|
|
2668
2688
|
runId: admitted.runId,
|
|
2669
|
-
cause: failure.cause,
|
|
2689
|
+
...(failure.cause === undefined ? {} : { cause: failure.cause }),
|
|
2670
2690
|
diagnostic: failure.diagnostic,
|
|
2671
2691
|
...(failure.identity === undefined ? {} : { identity: failure.identity }),
|
|
2672
2692
|
...(failure.details === undefined ? {} : { details: failure.details }),
|
|
@@ -2683,7 +2703,7 @@ export async function publishFailureArtifacts(admitted, failure, authority) {
|
|
|
2683
2703
|
sha256: a.sha256,
|
|
2684
2704
|
byteLength: a.byteLength,
|
|
2685
2705
|
})),
|
|
2686
|
-
failureCause: failure.cause,
|
|
2706
|
+
...(failure.cause === undefined ? {} : { failureCause: failure.cause }),
|
|
2687
2707
|
};
|
|
2688
2708
|
const evidenceWrite = await writeFailureJsonRetainingCause(evidenceCandidates, uniqueFallbackDirs, "evidence", evidencePayload,
|
|
2689
2709
|
// Evidence records the same publication collisions observed placing the error body.
|
|
@@ -2750,7 +2770,7 @@ export async function settleFailureTerminalResult(admitted, failure, authority,
|
|
|
2750
2770
|
// Private durable artifacts retain the original diagnostic identity (including run ID).
|
|
2751
2771
|
const artifacts = await publishFailureArtifacts(admitted, failure, authority);
|
|
2752
2772
|
const decisiveFacts = {
|
|
2753
|
-
cause: failure.cause,
|
|
2773
|
+
...(failure.cause === undefined ? {} : { cause: failure.cause }),
|
|
2754
2774
|
diagnostic: failure.diagnostic,
|
|
2755
2775
|
};
|
|
2756
2776
|
if (failure.identity?.name !== undefined) {
|
|
@@ -2770,7 +2790,7 @@ export async function settleFailureTerminalResult(admitted, failure, authority,
|
|
|
2770
2790
|
const roleOutcome = {
|
|
2771
2791
|
kind: "failure",
|
|
2772
2792
|
role: admitted.role,
|
|
2773
|
-
cause: failure.cause,
|
|
2793
|
+
...(failure.cause === undefined ? {} : { cause: failure.cause }),
|
|
2774
2794
|
diagnostic: failure.diagnostic,
|
|
2775
2795
|
decisiveFacts,
|
|
2776
2796
|
};
|
|
@@ -2784,7 +2804,7 @@ export async function settleFailureTerminalResult(admitted, failure, authority,
|
|
|
2784
2804
|
const roleOutcome = {
|
|
2785
2805
|
kind: "failure",
|
|
2786
2806
|
role: admitted.role,
|
|
2787
|
-
cause: failure.cause,
|
|
2807
|
+
...(failure.cause === undefined ? {} : { cause: failure.cause }),
|
|
2788
2808
|
diagnostic: failure.diagnostic,
|
|
2789
2809
|
decisiveFacts,
|
|
2790
2810
|
};
|
|
@@ -2811,7 +2831,7 @@ export function presentFailureTerminal(terminal, io) {
|
|
|
2811
2831
|
io.stdout(formatTerminalResult(terminal));
|
|
2812
2832
|
if (terminal.roleOutcome.kind === "failure") {
|
|
2813
2833
|
io.stderr(formatFailureStderrDiagnostic({
|
|
2814
|
-
cause: terminal.roleOutcome.cause,
|
|
2834
|
+
...(terminal.roleOutcome.cause === undefined ? {} : { cause: terminal.roleOutcome.cause }),
|
|
2815
2835
|
diagnostic: terminal.roleOutcome.diagnostic,
|
|
2816
2836
|
}));
|
|
2817
2837
|
return;
|
|
@@ -33,16 +33,6 @@ export function roleResultPayloads(outcome) {
|
|
|
33
33
|
return outcome.payloads ?? [];
|
|
34
34
|
return [];
|
|
35
35
|
}
|
|
36
|
-
/** Last object payload the role actually wrote. No field remapping. */
|
|
37
|
-
export function lastRolePayloadRecord(payloads) {
|
|
38
|
-
for (let index = payloads.length - 1; index >= 0; index -= 1) {
|
|
39
|
-
const payload = payloads[index];
|
|
40
|
-
if (typeof payload === "object" && payload !== null && !Array.isArray(payload)) {
|
|
41
|
-
return payload;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return undefined;
|
|
45
|
-
}
|
|
46
36
|
/**
|
|
47
37
|
* Build a recommendation navigator fact. Model command is kept when present;
|
|
48
38
|
* registry render is fallback only. Unknown seats stay recommendations (#836 B4.1).
|
|
@@ -71,7 +61,7 @@ export function formatTerminalResult(result) {
|
|
|
71
61
|
const lines = [];
|
|
72
62
|
lines.push("role\toutcome\tstatus");
|
|
73
63
|
const outcomeStatus = result.roleOutcome.kind === "failure"
|
|
74
|
-
? result.roleOutcome.cause
|
|
64
|
+
? result.roleOutcome.cause ?? ""
|
|
75
65
|
: result.roleOutcome.kind === "accepted"
|
|
76
66
|
? "accepted"
|
|
77
67
|
: result.roleOutcome.status;
|