@akagilnc/pi-workflow-roles 0.1.4800 → 0.1.4870

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.
Files changed (143) hide show
  1. package/dist/acp-host/production-host.js +19434 -18218
  2. package/dist/analyst-book-key.js +63 -0
  3. package/dist/analyst-cohort.js +167 -0
  4. package/dist/analyst-entry.js +320 -0
  5. package/dist/analyst-index.js +247 -0
  6. package/dist/analyst-ledger.js +561 -0
  7. package/dist/analyst-median.js +16 -0
  8. package/dist/analyst-metric-families/acceptance-success-rework.js +249 -0
  9. package/dist/analyst-metric-families/b2-frame-buckets-actions.js +189 -0
  10. package/dist/analyst-metric-families/gate-cycles.js +77 -0
  11. package/dist/analyst-metric-families/leg-wall-clock.js +56 -0
  12. package/dist/analyst-metric-families/round-timeline.js +126 -0
  13. package/dist/analyst-metric-families.js +23 -0
  14. package/dist/analyst-metric-family.js +11 -0
  15. package/dist/analyst-model-groups.js +135 -0
  16. package/dist/analyst-page.js +182 -0
  17. package/dist/atomic-write.js +23 -0
  18. package/dist/auditor-soul.js +1 -1
  19. package/dist/countersign-contracts.js +0 -2
  20. package/dist/diarist-contracts.js +0 -1
  21. package/dist/doctor-contracts.js +2 -2
  22. package/dist/gatekeeper-role.js +15 -16
  23. package/dist/gleaner-left-contracts.js +0 -2
  24. package/dist/headless-host/production-host.js +16967 -15749
  25. package/dist/host-contracts.js +0 -4
  26. package/dist/inspector-contracts.js +0 -2
  27. package/dist/institutional-resolution.js +4 -2
  28. package/dist/merger-contracts.js +0 -2
  29. package/dist/migrate-book-topology.js +3908 -3089
  30. package/dist/navigator-public-session.js +4 -3
  31. package/dist/notary-contracts.js +0 -2
  32. package/dist/package-contracts/auditor-output.js +0 -1
  33. package/dist/package-contracts/collector-output.js +0 -2
  34. package/dist/package-contracts/fixer-output.js +0 -2
  35. package/dist/package-contracts/gatekeeper-output.js +0 -2
  36. package/dist/package-contracts/judge-output.js +0 -2
  37. package/dist/package-contracts/navigator-output.js +0 -2
  38. package/dist/package-contracts/reviewer-output.js +0 -2
  39. package/dist/package-contracts/terminating-tools.js +32 -92
  40. package/dist/package-contracts/worker-output.js +0 -4
  41. package/dist/packaged-role-registry.js +518 -36
  42. package/dist/pi/role-turn-host.js +2 -1
  43. package/dist/public-cli/analyst-run.js +185 -0
  44. package/dist/public-cli/cli.js +885 -0
  45. package/dist/public-cli/config.js +12 -9
  46. package/dist/public-cli/countersign-run.js +4 -311
  47. package/dist/public-cli/instruction-seat-run.js +628 -158
  48. package/dist/public-cli/invocation.js +806 -1550
  49. package/dist/public-cli/main.js +6962 -9882
  50. package/dist/public-cli/option-definitions.js +51 -99
  51. package/dist/public-cli/post-admission.js +30 -2
  52. package/dist/public-cli/run-lifecycle.js +234 -353
  53. package/dist/public-cli/seat-ticket-binding.js +6 -0
  54. package/dist/public-cli/settlement.js +261 -675
  55. package/dist/public-cli/turn-request.js +76 -0
  56. package/dist/public-role-summons.js +38 -112
  57. package/dist/role-activation-flags.js +23 -45
  58. package/dist/secretariat-contracts.js +0 -2
  59. package/dist/session-identity.js +17 -8
  60. package/dist/session-opening-materials.js +3 -2
  61. package/extensions/role-runtime.ts +6 -80
  62. package/package.json +1 -1
  63. package/souls/coder.md +1 -2
  64. package/src/acp-host/role-turn-host.ts +4 -1
  65. package/src/analyst-metric-families/acceptance-success-rework.ts +3 -2
  66. package/src/auditor-role.ts +1 -2
  67. package/src/auditor-soul.ts +1 -1
  68. package/src/collector-role.ts +2 -2
  69. package/src/countersign-contracts.ts +0 -1
  70. package/src/countersign-role.ts +1 -1
  71. package/src/diarist-contracts.ts +0 -1
  72. package/src/diarist-role.ts +0 -1
  73. package/src/doctor-auditor.ts +1 -2
  74. package/src/doctor-contracts.ts +1 -1
  75. package/src/doctor-role.ts +3 -2
  76. package/src/external-host-turn-loop.ts +1 -1
  77. package/src/factory-board.ts +10 -7
  78. package/src/gatekeeper-role.ts +16 -10
  79. package/src/gleaner-left-contracts.ts +0 -1
  80. package/src/gleaner-left-role.ts +0 -1
  81. package/src/headless-host/role-turn-host.ts +7 -1
  82. package/src/host-contracts.ts +10 -6
  83. package/src/inspector-contracts.ts +0 -1
  84. package/src/inspector-role.ts +0 -2
  85. package/src/institutional-resolution.ts +6 -4
  86. package/src/judge-role.ts +3 -3
  87. package/src/merger-contracts.ts +0 -1
  88. package/src/merger-role.ts +2 -2
  89. package/src/navigator-public-session.ts +7 -6
  90. package/src/navigator-work-context.ts +6 -8
  91. package/src/notary-contracts.ts +0 -1
  92. package/src/notary-role.ts +2 -3
  93. package/src/package-contracts/auditor-output.ts +0 -1
  94. package/src/package-contracts/collector-output.ts +0 -1
  95. package/src/package-contracts/fixer-output.ts +0 -1
  96. package/src/package-contracts/gatekeeper-output.ts +0 -1
  97. package/src/package-contracts/judge-output.ts +0 -1
  98. package/src/package-contracts/navigator-output.ts +0 -1
  99. package/src/package-contracts/reviewer-output.ts +0 -1
  100. package/src/package-contracts/terminating-tools.ts +27 -99
  101. package/src/package-contracts/worker-output.ts +0 -2
  102. package/src/packaged-role-registry.ts +604 -35
  103. package/src/pi/role-turn-host.ts +2 -1
  104. package/src/public-cli/cli.ts +64 -259
  105. package/src/public-cli/config.ts +18 -10
  106. package/src/public-cli/countersign-run.ts +14 -449
  107. package/src/public-cli/instruction-seat-run.ts +758 -277
  108. package/src/public-cli/invocation.ts +1097 -2051
  109. package/src/public-cli/option-definitions.ts +62 -127
  110. package/src/public-cli/post-admission.ts +47 -2
  111. package/src/public-cli/run-lifecycle.ts +303 -613
  112. package/src/public-cli/seat-ticket-binding.ts +6 -0
  113. package/src/public-cli/settlement.ts +399 -1185
  114. package/src/public-cli/turn-request.ts +92 -1
  115. package/src/public-role-summons.ts +44 -128
  116. package/src/reviewer-role.ts +3 -2
  117. package/src/role-activation-flags.ts +20 -41
  118. package/src/role-runtime-dependencies.ts +25 -26
  119. package/src/role-runtime.ts +121 -232
  120. package/src/secretariat-contracts.ts +0 -1
  121. package/src/secretariat-role.ts +0 -2
  122. package/src/session-identity.ts +23 -8
  123. package/src/session-opening-materials.ts +2 -1
  124. package/src/worker-role.ts +3 -4
  125. package/dist/doctor-auditor.js +0 -25
  126. package/dist/public-cli/diarist-run.js +0 -130
  127. package/dist/public-cli/doctor-run.js +0 -87
  128. package/dist/public-cli/inspector-run.js +0 -151
  129. package/dist/public-cli/judge-run.js +0 -114
  130. package/dist/public-cli/notary-run.js +0 -174
  131. package/dist/public-cli/reviewer-run.js +0 -339
  132. package/src/public-cli/coder-run.ts +0 -255
  133. package/src/public-cli/collector-run.ts +0 -189
  134. package/src/public-cli/diarist-run.ts +0 -233
  135. package/src/public-cli/doctor-run.ts +0 -167
  136. package/src/public-cli/fixer-run.ts +0 -265
  137. package/src/public-cli/gleaner-left-run.ts +0 -167
  138. package/src/public-cli/inspector-run.ts +0 -261
  139. package/src/public-cli/judge-run.ts +0 -196
  140. package/src/public-cli/merger-run.ts +0 -294
  141. package/src/public-cli/notary-run.ts +0 -279
  142. package/src/public-cli/reviewer-run.ts +0 -480
  143. package/src/public-cli/secretariat-run.ts +0 -265
@@ -22,8 +22,6 @@ import {
22
22
 
23
23
  import { isAuditEscalationResult } from "../audit-escalation.ts";
24
24
  import { AUDITOR_SOUL_ROLES } from "../auditor-soul.ts";
25
- import { DOCTOR_AUDIT_TOOL_NAME } from "../doctor-auditor.ts";
26
- import { JUDGE_AUDIT_TOOL_NAME } from "../judge-auditor.ts";
27
25
  import type { RoleTurnKnownFailure } from "../host-contracts.ts";
28
26
  import { knownFailureFromProviderStop } from "../pi/known-failure.ts";
29
27
  import {
@@ -79,40 +77,26 @@ import {
79
77
  REVIEWER_OUTPUT_TOOL_NAME,
80
78
  } from "../package-contracts/reviewer-output.ts";
81
79
  import {
82
- MERGER_OUTPUT_TOOL_NAME,
83
- } from "../merger-contracts.ts";
84
- import {
85
- NOTARY_OUTPUT_TOOL_NAME,
86
- } from "../notary-contracts.ts";
87
- import {
88
- COUNTERSIGN_OUTPUT_TOOL_NAME,
89
- } from "../countersign-contracts.ts";
90
- import {
91
- GLEANER_LEFT_OUTPUT_TOOL_NAME,
92
- } from "../gleaner-left-contracts.ts";
93
- import {
94
- DIARIST_OUTPUT_TOOL_NAME,
95
- } from "../diarist-contracts.ts";
80
+ packagedAuditToolName,
81
+ packagedDurableOfficerEntry,
82
+ packagedProjectsTargetBindRejection,
83
+ packagedRoleAcceptedOutputTool,
84
+ packagedRoleMetadata,
85
+ packagedSkipsGateOnInfrastructureStage,
86
+ type PackagedArtifactFace,
87
+ type PackagedArtifactLeaf,
88
+ type PackagedRole,
89
+ } from "../packaged-role-registry.ts";
96
90
  import {
97
91
  SECRETARIAT_COUNTERSIGN_TERMINAL_FACT_KEY,
98
92
  SECRETARIAT_GATE_OFFICER_ENTRY_TYPE,
99
- SECRETARIAT_OUTPUT_TOOL_NAME,
100
93
  } from "../secretariat-contracts.ts";
101
- import {
102
- INSPECTOR_OUTPUT_TOOL_NAME,
103
- } from "../inspector-contracts.ts";
104
- import {
105
- GATEKEEPER_OUTPUT_TOOL_NAME,
106
- } from "../package-contracts/gatekeeper-output.ts";
107
- import {
108
- NAVIGATOR_OUTPUT_TOOL_NAME,
109
- } from "../package-contracts/navigator-output.ts";
110
- import {
111
- AUDITOR_OUTPUT_TOOL_NAME,
112
- } from "../package-contracts/auditor-output.ts";
113
94
  import {
114
95
  observePackagedMethodSkillInvocation,
96
+ resolvePackagedMethodSkillPath,
115
97
  type ObservedPackagedMethodSkillInvocation,
98
+ type PackagedMethodSkillMaterial,
99
+ type PackagedMethodSkillName,
116
100
  type PackagedMethodSkillProvenance,
117
101
  } from "../package-resources/method-skill.ts";
118
102
  import {
@@ -146,21 +130,7 @@ import {
146
130
  import {
147
131
  ensureRunArtifactsDir,
148
132
  homeFromRunDirectory,
149
- type AdmittedCoderInvocation,
150
- type AdmittedCollectorInvocation,
151
- type AdmittedDoctorInvocation,
152
- type AdmittedFixerInvocation,
153
133
  type AdmittedJudgeInvocation,
154
- type AdmittedMergerInvocation,
155
- type AdmittedCountersignInvocation,
156
- type AdmittedDiaristInvocation,
157
- type AdmittedSecretariatInvocation,
158
- type AdmittedGleanerLeftInvocation,
159
- type AdmittedInspectorInvocation,
160
- type AdmittedGatekeeperInvocation,
161
- type AdmittedNavigatorInvocation,
162
- type AdmittedNotaryInvocation,
163
- type AdmittedReviewerInvocation,
164
134
  type AdmittedRoleInvocation,
165
135
  } from "./invocation.ts";
166
136
 
@@ -356,14 +326,6 @@ export async function settleHostEndedNoReceipt(
356
326
  );
357
327
  }
358
328
 
359
- async function closedLedgerOutcome(
360
- admitted: AdmittedRoleInvocation,
361
- role: TerminalRoleName,
362
- scope?: SettlementCourtScope,
363
- ): Promise<Extract<TerminalRoleOutcome, { kind: "accepted" | "audit_escalation" }> | undefined> {
364
- return sealedLedgerOutcome(admitted, role, scope);
365
- }
366
-
367
329
  /** Transitional host-session reads remain only for non-sealed failure and audit evidence. */
368
330
  function coordinatesFromAdmitted(
369
331
  authority: DurablePrincipalAuthority,
@@ -1696,15 +1658,63 @@ export async function readEngineDetourInfrastructureFailure(
1696
1658
  );
1697
1659
  }
1698
1660
 
1661
+ function knownFailureFromControlled(failure: ControlledFailure): RoleTurnKnownFailure {
1662
+ return {
1663
+ ...(failure.cause === undefined ? {} : { cause: failure.cause }),
1664
+ diagnostic: failure.diagnostic,
1665
+ ...(failure.identity === undefined ? {} : { identity: failure.identity }),
1666
+ };
1667
+ }
1668
+
1669
+ const RUNNER_FAILURE_POLICY = {
1670
+ "engine-detour-known-first": {
1671
+ read: readEngineDetourInfrastructureFailure,
1672
+ rank: "known-first",
1673
+ },
1674
+ "engine-detour-record-first": {
1675
+ read: readEngineDetourInfrastructureFailure,
1676
+ rank: "record-first",
1677
+ },
1678
+ "collector-known-first": {
1679
+ read: readCollectorInfrastructureFailure,
1680
+ rank: "known-first",
1681
+ },
1682
+ } as const;
1683
+
1684
+ /**
1685
+ * Runner-failure rank for one seat. The composition-root `runnerFailure`
1686
+ * leaf is the only seat difference; absent means the runner knownFailure stands.
1687
+ */
1688
+ export function seatKnownFailureResolver(
1689
+ role: PackagedRole,
1690
+ ): ((input: {
1691
+ result: { knownFailure?: RoleTurnKnownFailure };
1692
+ sessionFile: string;
1693
+ }) => Promise<RoleTurnKnownFailure | undefined>) | undefined {
1694
+ const record = packagedRoleMetadata(role);
1695
+ if (record === undefined || !("runnerFailure" in record)) return undefined;
1696
+ const policy = RUNNER_FAILURE_POLICY[record.runnerFailure];
1697
+ return async ({ result, sessionFile }) => {
1698
+ const infrastructureFailure = await policy.read(sessionFile);
1699
+ if (policy.rank === "record-first") {
1700
+ return infrastructureFailure === undefined
1701
+ ? result.knownFailure
1702
+ : knownFailureFromControlled(infrastructureFailure);
1703
+ }
1704
+ return result.knownFailure ?? (
1705
+ infrastructureFailure === undefined ? undefined : knownFailureFromControlled(infrastructureFailure)
1706
+ );
1707
+ };
1708
+ }
1709
+
1699
1710
  function auditToolNameForRole(
1700
1711
  role: (typeof AUDITOR_SOUL_ROLES)[number],
1701
1712
  ): string {
1702
- switch (role) {
1703
- case "judge":
1704
- return JUDGE_AUDIT_TOOL_NAME;
1705
- case "doctor":
1706
- return DOCTOR_AUDIT_TOOL_NAME;
1713
+ const name = packagedAuditToolName(role);
1714
+ if (name === undefined) {
1715
+ throw new Error(`audit tool is not declared for ${role}`);
1707
1716
  }
1717
+ return name;
1708
1718
  }
1709
1719
 
1710
1720
  type BoundRoleToolCall = {
@@ -1958,11 +1968,6 @@ export async function appendRunAttemptHistory(
1958
1968
  } catch {}
1959
1969
  }
1960
1970
 
1961
- /** Lawful Judge outcomes extracted from session (never a fabricated failure Receipt). */
1962
- export type LawfulJudgeRoleOutcome = Extract<
1963
- TerminalRoleOutcome,
1964
- { kind: "accepted" } | { kind: "audit_escalation" }
1965
- >;
1966
1971
  /**
1967
1972
  * Minimal attendance provenance against the bound marker (ADR 0043).
1968
1973
  * Keep only invocationId + post-terminal ordering. Runtime-self-produced
@@ -2223,11 +2228,7 @@ async function withOptionalGateProjection<
2223
2228
  const skipGate =
2224
2229
  isRecord(secondaryEvidence)
2225
2230
  && secondaryEvidence.kind === "role_infrastructure_failure"
2226
- && (
2227
- secondaryEvidence.stage === "gatekeeper"
2228
- || secondaryEvidence.stage === "inspector"
2229
- || secondaryEvidence.stage === "notary"
2230
- );
2231
+ && packagedSkipsGateOnInfrastructureStage(secondaryEvidence.stage);
2231
2232
 
2232
2233
  let next: T & { gate?: TerminalGateFact } = base;
2233
2234
  if (!skipGate) {
@@ -2464,65 +2465,23 @@ async function publishAcceptedTerminalArtifacts(
2464
2465
  ];
2465
2466
  }
2466
2467
 
2467
- export async function publishJudgeArtifacts(
2468
- admitted: AdmittedJudgeInvocation,
2469
- roleOutcome: TerminalRoleOutcome,
2470
- coordinates: DurablePrincipalCoordinates,
2471
- ): Promise<TerminalArtifactRef[]> {
2472
- return publishAcceptedTerminalArtifacts(admitted, roleOutcome, coordinates, {
2473
- report: {
2474
- role: "judge",
2475
- runId: admitted.runId,
2476
- outcome: roleOutcome,
2477
- },
2478
- evidence: {
2479
- runId: admitted.runId,
2480
- sessionDirectory: coordinates.sessionDirectory,
2481
- sessionFile: coordinates.sessionFile,
2482
- admittedRequestPath: admitted.admittedRequestPath,
2483
- attachments: acceptedArtifactAttachmentRefs(admitted.attachments),
2484
- },
2485
- });
2486
- }
2468
+ type MethodPublicationOptions = {
2469
+ readonly methodProvenance?: PackagedMethodSkillProvenance;
2470
+ readonly methodSkillPath?: string;
2471
+ readonly methodSkillConfiguredPath?: string;
2472
+ };
2487
2473
 
2488
- /**
2489
- * Publish lawful Coder success Artifacts on the shared #106 success interface.
2490
- * Evidence records package method provenance without ambient home Skill paths.
2491
- */
2492
- export async function publishCoderArtifacts(
2493
- admitted: AdmittedCoderInvocation,
2474
+ /** Publish one accepted seat's report/evidence face from the shared leaf table. */
2475
+ export async function publishSeatAcceptedArtifacts(
2476
+ admitted: AdmittedRoleInvocation,
2494
2477
  roleOutcome: TerminalRoleOutcome,
2495
2478
  coordinates: DurablePrincipalCoordinates,
2496
- options: {
2497
- readonly methodProvenance?: PackagedMethodSkillProvenance;
2498
- } = {},
2479
+ entries: readonly SessionEntry[] = [],
2480
+ options: MethodPublicationOptions = {},
2499
2481
  ): Promise<TerminalArtifactRef[]> {
2500
- return publishAcceptedTerminalArtifacts(admitted, roleOutcome, coordinates, {
2501
- report: {
2502
- role: "coder",
2503
- runId: admitted.runId,
2504
- phase: admitted.phase,
2505
- outcome: roleOutcome,
2506
- },
2507
- evidence: {
2508
- runId: admitted.runId,
2509
- role: "coder",
2510
- phase: admitted.phase,
2511
- sessionDirectory: coordinates.sessionDirectory,
2512
- sessionFile: coordinates.sessionFile,
2513
- admittedRequestPath: admitted.admittedRequestPath,
2514
- taskPath: admitted.taskPath,
2515
- attachments: acceptedArtifactAttachmentRefs(admitted.attachments),
2516
- ...(options.methodProvenance === undefined
2517
- ? {}
2518
- : { methodProvenance: options.methodProvenance }),
2519
- },
2520
- });
2482
+ return publishDeclaredSeatArtifacts(admitted, roleOutcome, coordinates, entries, options);
2521
2483
  }
2522
2484
 
2523
- /** Lawful Coder accepted outcome extracted from session (shared success interface). */
2524
- export type LawfulCoderRoleOutcome = Extract<TerminalRoleOutcome, { kind: "accepted" }>;
2525
-
2526
2485
  /**
2527
2486
  * Read session entries for lawful settlement. Missing path → undefined (absence).
2528
2487
  * Malformed JSONL / other read failures throw with knownCause=session.
@@ -2544,49 +2503,34 @@ async function readLawfulSettlementEntries(
2544
2503
  }
2545
2504
 
2546
2505
  /**
2547
- * Lawful Judge outcome presence only — no artifact publication.
2548
- * Returns undefined for genuine absence (missing path / no lawful verdict).
2549
- * Session-read failures propagate with typed identity.
2550
- */
2551
- export async function readLawfulJudgeRoleOutcome(
2552
- admitted: AdmittedJudgeInvocation,
2553
- authority: DurablePrincipalAuthority,
2554
- scope?: SettlementCourtScope,
2555
- ): Promise<LawfulJudgeRoleOutcome | undefined> {
2556
- return sealedLedgerOutcome(admitted, "judge", scope);
2557
- }
2558
-
2559
- /**
2560
- * Single lawful-settlement implementation (session → outcome/Navigator/artifacts).
2561
- *
2562
- * - Returns undefined only for genuine absence (missing session path, or no
2563
- * lawful verdict in an otherwise readable session).
2564
- * - Malformed JSONL / other session-read failures throw with knownCause=session
2565
- * and original identity (SyntaxError name retained).
2566
- * - Artifact publication failures propagate with their original typed identity.
2567
- * - Lawful outcome presence is decided before publication so a later write error
2568
- * cannot erase the fact that a lawful result already exists.
2506
+ * Sealed-ledger seats that publish on a lawful outcome and do not scan a
2507
+ * residual tool or attach run-scoped submission history.
2508
+ * Absence stays undefined. Session-read and publication errors keep their identity.
2509
+ * Outcome is read before publication so a later write error cannot erase it.
2569
2510
  */
2570
- async function settleLawfulJudgeTerminalResult(
2571
- admitted: AdmittedJudgeInvocation,
2511
+ async function settleSealedLedgerTerminal(
2512
+ admitted: AdmittedRoleInvocation,
2572
2513
  authority: DurablePrincipalAuthority,
2573
- scope?: SettlementCourtScope,
2514
+ scope: SettlementCourtScope | undefined,
2515
+ publish: (
2516
+ roleOutcome: Extract<TerminalRoleOutcome, { kind: "accepted" | "audit_escalation" }>,
2517
+ coordinates: DurablePrincipalCoordinates,
2518
+ entries: readonly SessionEntry[],
2519
+ ) => Promise<TerminalArtifactRef[]>,
2520
+ accept?: (
2521
+ roleOutcome: Extract<TerminalRoleOutcome, { kind: "accepted" | "audit_escalation" }>,
2522
+ ) => boolean,
2574
2523
  ): Promise<TerminalResult | undefined> {
2575
- const roleOutcome = await readLawfulJudgeRoleOutcome(admitted, authority, scope);
2524
+ const roleOutcome = await sealedLedgerOutcome(admitted, admitted.role, scope);
2576
2525
  if (roleOutcome === undefined) return undefined;
2526
+ if (accept !== undefined && !accept(roleOutcome)) return undefined;
2577
2527
  const coordinates = coordinatesFromAdmitted(authority, admitted);
2578
2528
  const entries = await readLawfulSettlementEntries(coordinates.sessionFile) ?? [];
2579
- const navigator = extractNavigatorFact(entries);
2580
- // Lawful outcome exists — artifact publication keeps original errno/name.
2581
- const artifacts = await publishJudgeArtifacts(
2582
- admitted,
2583
- roleOutcome,
2584
- coordinates,
2585
- );
2529
+ const artifacts = await publish(roleOutcome, coordinates, entries);
2586
2530
  return withOptionalGateProjection(
2587
2531
  {
2588
2532
  roleOutcome,
2589
- navigator,
2533
+ navigator: extractNavigatorFact(entries),
2590
2534
  artifacts,
2591
2535
  runId: admitted.runId,
2592
2536
  },
@@ -2596,86 +2540,139 @@ async function settleLawfulJudgeTerminalResult(
2596
2540
  }
2597
2541
 
2598
2542
  /**
2599
- * Settle a lawful typed terminal result from the admitted session.
2600
- * Throws when no lawful outcome is present (tests/callers that require success).
2601
- * Session-read and publication failures retain their typed identity.
2543
+ * Sealed accepted outcome publishes. Any other ledger face falls through to
2544
+ * the latest bound errored tool result in the scan window, which settles as
2545
+ * output failure. Both faces attach run-scoped submissions. Absence of both
2546
+ * stays undefined. Session is read first so a read failure keeps its identity
2547
+ * even when the ledger has no accepted row.
2602
2548
  */
2603
- export async function settleJudgeTerminalResult(
2604
- admitted: AdmittedJudgeInvocation,
2549
+ async function settleSealedAcceptedOrToolResidual(
2550
+ admitted: AdmittedRoleInvocation,
2605
2551
  authority: DurablePrincipalAuthority,
2606
- scope?: SettlementCourtScope,
2607
- ): Promise<TerminalResult> {
2608
- const settled = await settleLawfulJudgeTerminalResult(admitted, authority, scope);
2609
- if (settled === undefined) {
2610
- throw new Error(
2611
- "Judge Role run completed without a lawful typed terminal result",
2612
- );
2552
+ scope: SettlementCourtScope | undefined,
2553
+ role: TerminalRoleName,
2554
+ residualToolName: string,
2555
+ scan: "current-attempt" | "session",
2556
+ publish: (
2557
+ roleOutcome: Extract<TerminalRoleOutcome, { kind: "accepted" }>,
2558
+ coordinates: DurablePrincipalCoordinates,
2559
+ entries: readonly SessionEntry[],
2560
+ ) => Promise<TerminalArtifactRef[]>,
2561
+ ): Promise<TerminalResult | undefined> {
2562
+ const coordinates = coordinatesFromAdmitted(authority, admitted);
2563
+ const { sessionDirectory, sessionFile } = coordinates;
2564
+ const entries = await readLawfulSettlementEntries(sessionFile) ?? [];
2565
+ const roleOutcome = await sealedLedgerOutcome(admitted, role, scope);
2566
+ if (roleOutcome?.role !== role || roleOutcome.kind !== "accepted") {
2567
+ const scanStart = scan === "current-attempt" ? currentAttemptStartIndex(entries) : 0;
2568
+ for (let index = entries.length - 1; index >= scanStart; index -= 1) {
2569
+ const message = entries[index]?.message;
2570
+ if (message?.role !== "toolResult") continue;
2571
+ const residual = boundErroredToolCandidate(entries, index, message, residualToolName);
2572
+ if (residual === undefined) continue;
2573
+ const candidate = residual.candidate;
2574
+ const details = isRecord(candidate) ? candidate : { candidate };
2575
+ const failed = await settleFailureTerminalResult(
2576
+ admitted,
2577
+ {
2578
+ cause: "output",
2579
+ diagnostic: residual.diagnostic,
2580
+ details,
2581
+ },
2582
+ authority,
2583
+ scope ?? {},
2584
+ );
2585
+ return attachRecordedSubmissions(admitted, failed, scope);
2586
+ }
2587
+ return undefined;
2613
2588
  }
2614
- return settled;
2589
+ const artifacts = await publish(roleOutcome, coordinates, entries);
2590
+ return attachRecordedSubmissions(
2591
+ admitted,
2592
+ await withOptionalGateProjection(
2593
+ {
2594
+ roleOutcome,
2595
+ navigator: extractNavigatorFact(entries),
2596
+ artifacts,
2597
+ runId: admitted.runId,
2598
+ },
2599
+ sessionDirectory,
2600
+ detourGateContext(admitted, scope),
2601
+ ),
2602
+ scope,
2603
+ );
2615
2604
  }
2616
2605
 
2617
2606
  /**
2618
- * Try to settle a lawful typed terminal result from the admitted session.
2619
- * Returns undefined only for genuine absence (no lawful verdict / missing path).
2620
- * Session malformation and publication exceptions propagate with typed identity.
2607
+ * One settlement for every registered seat. The registry `settlement` leaf
2608
+ * picks sealed ledger, sealed-or-residual, or the accepted-tool scan.
2609
+ * Seat evidence and the observed-method tail stay on the artifact face.
2621
2610
  */
2622
- export async function trySettleJudgeTerminalResult(
2623
- admitted: AdmittedJudgeInvocation,
2611
+ async function settleSeat(
2612
+ admitted: AdmittedRoleInvocation,
2624
2613
  authority: DurablePrincipalAuthority,
2625
- scope?: SettlementCourtScope,
2614
+ scope: SettlementCourtScope | undefined,
2615
+ options: MethodPublicationOptions,
2626
2616
  ): Promise<TerminalResult | undefined> {
2627
- return settleLawfulJudgeTerminalResult(admitted, authority, scope);
2617
+ const record = packagedRoleMetadata(admitted.role);
2618
+ if (record === undefined) {
2619
+ throw new Error(`no settlement for ${admitted.role}`);
2620
+ }
2621
+ if (record.settlement === "accepted") {
2622
+ return trySettleAcceptedSeatTerminalResult(admitted, authority, scope);
2623
+ }
2624
+ const publish = (
2625
+ roleOutcome: Extract<TerminalRoleOutcome, { kind: "accepted" | "audit_escalation" }>,
2626
+ coordinates: DurablePrincipalCoordinates,
2627
+ entries: readonly SessionEntry[],
2628
+ ) => publishDeclaredSeatArtifacts(admitted, roleOutcome, coordinates, entries, options);
2629
+ if (record.settlement === "residual") {
2630
+ return settleSealedAcceptedOrToolResidual(
2631
+ admitted,
2632
+ authority,
2633
+ scope,
2634
+ admitted.role,
2635
+ record.residualTool,
2636
+ record.residualScan,
2637
+ publish,
2638
+ );
2639
+ }
2640
+ const accept = "sealedAcceptedOnly" in record && record.sealedAcceptedOnly === true
2641
+ ? (
2642
+ roleOutcome: Extract<TerminalRoleOutcome, { kind: "accepted" | "audit_escalation" }>,
2643
+ ) => roleOutcome.role === admitted.role && roleOutcome.kind === "accepted"
2644
+ : undefined;
2645
+ return settleSealedLedgerTerminal(admitted, authority, scope, publish, accept);
2628
2646
  }
2629
2647
 
2630
- async function settleLawfulCoderTerminalResult(
2631
- admitted: AdmittedCoderInvocation,
2648
+ /**
2649
+ * Try to settle any registered seat. Undefined only for genuine absence.
2650
+ * Session malformation and publication exceptions keep their typed identity.
2651
+ */
2652
+ export async function trySettleSeatTerminalResult(
2653
+ admitted: AdmittedRoleInvocation,
2632
2654
  authority: DurablePrincipalAuthority,
2633
- options: {
2634
- readonly methodProvenance?: PackagedMethodSkillProvenance;
2635
- } = {},
2655
+ options: MethodPublicationOptions = {},
2636
2656
  scope?: SettlementCourtScope,
2637
2657
  ): Promise<TerminalResult | undefined> {
2638
- const ledgerOutcome = await closedLedgerOutcome(admitted, "coder", scope);
2639
- if (ledgerOutcome === undefined) return undefined;
2640
- const roleOutcome: TerminalRoleOutcome = ledgerOutcome;
2641
- const coordinates = coordinatesFromAdmitted(authority, admitted);
2642
- const entries = await readLawfulSettlementEntries(coordinates.sessionFile) ?? [];
2643
- const navigator = extractNavigatorFact(entries);
2644
- const artifacts = await publishCoderArtifacts(
2645
- admitted,
2646
- roleOutcome,
2647
- coordinates,
2648
- {
2649
- ...(options.methodProvenance === undefined
2650
- ? {}
2651
- : { methodProvenance: options.methodProvenance }),
2652
- },
2653
- );
2654
- return withOptionalGateProjection(
2655
- {
2656
- roleOutcome,
2657
- navigator,
2658
- artifacts,
2659
- runId: admitted.runId,
2660
- },
2661
- coordinates.sessionDirectory,
2662
- detourGateContext(admitted, scope),
2663
- );
2658
+ return settleSeat(admitted, authority, scope, options);
2664
2659
  }
2665
2660
 
2666
- /** Settle a lawful Coder Terminal from the admitted session (shared #106 success interface). */
2667
- export async function settleCoderTerminalResult(
2668
- admitted: AdmittedCoderInvocation,
2661
+ /**
2662
+ * Settle any registered seat. Throws when no lawful outcome is present.
2663
+ * Session-read and publication failures retain their typed identity.
2664
+ */
2665
+ export async function settleSeatTerminalResult(
2666
+ admitted: AdmittedRoleInvocation,
2669
2667
  authority: DurablePrincipalAuthority,
2670
- options: {
2671
- readonly methodProvenance?: PackagedMethodSkillProvenance;
2672
- } = {},
2668
+ options: MethodPublicationOptions = {},
2673
2669
  scope?: SettlementCourtScope,
2674
2670
  ): Promise<TerminalResult> {
2675
- const settled = await settleLawfulCoderTerminalResult(admitted, authority, options, scope);
2671
+ const settled = await settleSeat(admitted, authority, scope, options);
2676
2672
  if (settled === undefined) {
2673
+ const label = admitted.role.charAt(0).toUpperCase() + admitted.role.slice(1);
2677
2674
  throw new Error(
2678
- "Coder Role run completed without a lawful typed terminal result",
2675
+ `${label} Role run completed without a lawful typed terminal result`,
2679
2676
  );
2680
2677
  }
2681
2678
  return settled;
@@ -2701,12 +2698,32 @@ function sessionMessageText(message: SessionMessage | undefined): string {
2701
2698
  }
2702
2699
 
2703
2700
  /**
2704
- * Observe optional Fixer diagnosing-bugs Skill expansions from the session.
2705
- * Availability is always package-bound; invocation is recorded only when observed.
2701
+ * Locations a loaded packaged method may be observed at.
2702
+ * The name is the loaded material; the configured path is the package copy.
2703
+ */
2704
+ export function observedMethodSkillOptions(
2705
+ packageRoot: string,
2706
+ material: PackagedMethodSkillMaterial,
2707
+ ): {
2708
+ readonly methodProvenance: PackagedMethodSkillProvenance;
2709
+ readonly methodSkillPath: string;
2710
+ readonly methodSkillConfiguredPath: string;
2711
+ } {
2712
+ return {
2713
+ methodProvenance: material.provenance,
2714
+ methodSkillPath: material.skillPath,
2715
+ methodSkillConfiguredPath: resolvePackagedMethodSkillPath(packageRoot, material.name),
2716
+ };
2717
+ }
2718
+
2719
+ /**
2720
+ * Observe packaged method Skill expansions from the session.
2721
+ * The skill name is the caller's packaged method; home locations never count.
2706
2722
  */
2707
- export function extractFixerMethodInvocations(
2723
+ function extractObservedMethodInvocations(
2708
2724
  entries: readonly SessionEntry[],
2709
2725
  options: {
2726
+ readonly name: PackagedMethodSkillName;
2710
2727
  readonly allowedLocations: readonly string[];
2711
2728
  },
2712
2729
  ): readonly ObservedPackagedMethodSkillInvocation[] {
@@ -2718,7 +2735,7 @@ export function extractFixerMethodInvocations(
2718
2735
  const text = sessionMessageText(message);
2719
2736
  if (text.length === 0) continue;
2720
2737
  const hit = observePackagedMethodSkillInvocation(text, {
2721
- name: "diagnosing-bugs",
2738
+ name: options.name,
2722
2739
  allowedLocations: options.allowedLocations,
2723
2740
  });
2724
2741
  if (hit !== undefined) observed.push(hit);
@@ -2727,385 +2744,155 @@ export function extractFixerMethodInvocations(
2727
2744
  }
2728
2745
 
2729
2746
  /**
2730
- * Publish lawful Fixer success Artifacts on the shared #106 success interface.
2731
- * Evidence records package diagnosis provenance and optional observed invocation.
2747
+ * #836: cost and the auditor's no-receipt facts ride beside the role's
2748
+ * testimony on the current attempt's candidate entry. Absence is omitted,
2749
+ * never invented. Both facts come from that one entry.
2732
2750
  */
2733
- export async function publishFixerArtifacts(
2734
- admitted: AdmittedFixerInvocation,
2751
+ function extractDoctorCandidateFacts(
2752
+ entries: readonly SessionEntry[],
2753
+ ): { readonly cost?: DoctorCaseCost; readonly auditNoReceipt?: unknown } {
2754
+ const scanStart = currentAttemptStartIndex(entries);
2755
+ for (let i = entries.length - 1; i >= scanStart; i -= 1) {
2756
+ const entry = entries[i];
2757
+ if (entry?.type !== "custom" || entry.customType !== DOCTOR_CANDIDATE_ENTRY_TYPE) {
2758
+ continue;
2759
+ }
2760
+ const data = entry.data;
2761
+ if (!isRecord(data)) return {};
2762
+ return {
2763
+ ...(data.cost === undefined ? {} : { cost: data.cost as DoctorCaseCost }),
2764
+ ...(data.auditNoReceipt === undefined ? {} : { auditNoReceipt: data.auditNoReceipt }),
2765
+ };
2766
+ }
2767
+ return {};
2768
+ }
2769
+
2770
+ const EMPTY_ARTIFACT_FACE: PackagedArtifactFace = { leaves: [] };
2771
+
2772
+ /** Artifact face for one seat. Absent means report/evidence carry only shared leaves. */
2773
+ function seatArtifactFace(role: string): PackagedArtifactFace {
2774
+ const record = packagedRoleMetadata(role);
2775
+ if (record !== undefined && "artifactFace" in record && record.artifactFace !== undefined) {
2776
+ return record.artifactFace;
2777
+ }
2778
+ return EMPTY_ARTIFACT_FACE;
2779
+ }
2780
+
2781
+ function readAdmittedPath(source: object, path: string): unknown {
2782
+ let current: unknown = source;
2783
+ for (const part of path.split(".")) {
2784
+ if (typeof current !== "object" || current === null) return undefined;
2785
+ current = (current as Record<string, unknown>)[part];
2786
+ }
2787
+ return current;
2788
+ }
2789
+
2790
+ function evidenceLeaves(
2791
+ admitted: AdmittedRoleInvocation,
2792
+ leaves: readonly PackagedArtifactLeaf[],
2793
+ ): Record<string, unknown> {
2794
+ const evidence: Record<string, unknown> = {};
2795
+ for (const leaf of leaves) {
2796
+ if (leaf.callerProvenance === true) {
2797
+ if (!admitted.instructionEmpty) evidence[leaf.key] = admitted.instruction;
2798
+ continue;
2799
+ }
2800
+ const value = readAdmittedPath(admitted, leaf.from ?? leaf.key);
2801
+ if (value === undefined && leaf.omitUndefined === true) continue;
2802
+ evidence[leaf.key] = leaf.copyArray === true && Array.isArray(value) ? [...value] : value;
2803
+ }
2804
+ return evidence;
2805
+ }
2806
+
2807
+ function doctorReportFacts(
2808
+ face: PackagedArtifactFace,
2735
2809
  roleOutcome: TerminalRoleOutcome,
2736
- coordinates: DurablePrincipalCoordinates,
2810
+ entries: readonly SessionEntry[],
2811
+ ): Record<string, unknown> {
2812
+ if (face.doctorReportFacts !== true || roleOutcome.kind === "audit_escalation") return {};
2813
+ const facts = extractDoctorCandidateFacts(entries);
2814
+ return {
2815
+ ...(facts.cost === undefined ? {} : { cost: facts.cost }),
2816
+ ...(facts.auditNoReceipt === undefined ? {} : { auditNoReceipt: facts.auditNoReceipt }),
2817
+ };
2818
+ }
2819
+
2820
+ function observedMethodTail(
2821
+ face: PackagedArtifactFace,
2822
+ entries: readonly SessionEntry[],
2737
2823
  options: {
2738
- readonly methodProvenance: PackagedMethodSkillProvenance;
2739
- readonly methodInvocations?: readonly ObservedPackagedMethodSkillInvocation[];
2824
+ readonly methodProvenance?: PackagedMethodSkillProvenance;
2825
+ readonly methodSkillPath?: string;
2826
+ readonly methodSkillConfiguredPath?: string;
2740
2827
  },
2828
+ ): Record<string, unknown> {
2829
+ if (face.method === undefined) return {};
2830
+ if (face.method === "optional") {
2831
+ return options.methodProvenance === undefined
2832
+ ? {}
2833
+ : { methodProvenance: options.methodProvenance };
2834
+ }
2835
+ if (
2836
+ options.methodProvenance === undefined
2837
+ || options.methodSkillPath === undefined
2838
+ || options.methodSkillConfiguredPath === undefined
2839
+ ) {
2840
+ throw new Error("observed method publication is missing packaged method coordinates");
2841
+ }
2842
+ const methodInvocations = extractObservedMethodInvocations(entries, {
2843
+ name: options.methodProvenance.name,
2844
+ allowedLocations: [options.methodSkillPath, options.methodSkillConfiguredPath],
2845
+ });
2846
+ return {
2847
+ methodProvenance: options.methodProvenance,
2848
+ methodInvocationObserved: methodInvocations.length > 0,
2849
+ methodInvocations,
2850
+ };
2851
+ }
2852
+
2853
+ async function publishDeclaredSeatArtifacts(
2854
+ admitted: AdmittedRoleInvocation,
2855
+ roleOutcome: TerminalRoleOutcome,
2856
+ coordinates: DurablePrincipalCoordinates,
2857
+ entries: readonly SessionEntry[],
2858
+ options: {
2859
+ readonly methodProvenance?: PackagedMethodSkillProvenance;
2860
+ readonly methodSkillPath?: string;
2861
+ readonly methodSkillConfiguredPath?: string;
2862
+ } = {},
2741
2863
  ): Promise<TerminalArtifactRef[]> {
2864
+ const face = seatArtifactFace(admitted.role);
2865
+ const phase = face.reportPhase === true
2866
+ ? { phase: readAdmittedPath(admitted, "phase") }
2867
+ : {};
2742
2868
  return publishAcceptedTerminalArtifacts(admitted, roleOutcome, coordinates, {
2743
2869
  report: {
2744
- role: "fixer",
2870
+ role: admitted.role,
2745
2871
  runId: admitted.runId,
2746
- phase: admitted.phase,
2872
+ ...phase,
2747
2873
  outcome: roleOutcome,
2874
+ ...doctorReportFacts(face, roleOutcome, entries),
2748
2875
  },
2749
2876
  evidence: {
2750
2877
  runId: admitted.runId,
2751
- role: "fixer",
2752
- phase: admitted.phase,
2878
+ ...(face.evidenceRole === true ? { role: admitted.role } : {}),
2879
+ ...evidenceLeaves(admitted, face.leaves),
2753
2880
  sessionDirectory: coordinates.sessionDirectory,
2754
2881
  sessionFile: coordinates.sessionFile,
2755
2882
  admittedRequestPath: admitted.admittedRequestPath,
2756
- packetPath: admitted.packetPath,
2757
- ...(admitted.prerequisitesPath === undefined
2758
- ? {}
2759
- : { prerequisitesPath: admitted.prerequisitesPath }),
2760
- prerequisites: admitted.prerequisites,
2761
2883
  attachments: acceptedArtifactAttachmentRefs(admitted.attachments),
2762
- methodProvenance: options.methodProvenance,
2763
- // Optional diagnosis: availability is package-bound; invocation only when observed.
2764
- methodInvocationObserved: (options.methodInvocations ?? []).length > 0,
2765
- methodInvocations: options.methodInvocations ?? [],
2884
+ ...observedMethodTail(face, entries, options),
2766
2885
  },
2767
2886
  });
2768
2887
  }
2769
2888
 
2770
- /** Lawful Fixer accepted outcome extracted from session (no LLM auditor after #242). */
2771
- export type LawfulFixerRoleOutcome = Extract<TerminalRoleOutcome, { kind: "accepted" }>;
2772
- async function settleLawfulFixerTerminalResult(
2773
- admitted: AdmittedFixerInvocation,
2774
- authority: DurablePrincipalAuthority,
2775
- options: {
2776
- readonly methodProvenance: PackagedMethodSkillProvenance;
2777
- readonly methodSkillPath: string;
2778
- readonly methodSkillConfiguredPath: string;
2779
- },
2780
- scope?: SettlementCourtScope,
2781
- ): Promise<TerminalResult | undefined> {
2782
- const ledgerOutcome = await closedLedgerOutcome(admitted, "fixer", scope);
2783
- if (ledgerOutcome === undefined) return undefined;
2784
- const roleOutcome: TerminalRoleOutcome = ledgerOutcome;
2785
- const coordinates = coordinatesFromAdmitted(authority, admitted);
2786
- const { sessionDirectory, sessionFile } = coordinates;
2787
- const entries = await readLawfulSettlementEntries(sessionFile) ?? [];
2788
- const navigator = extractNavigatorFact(entries);
2789
- const methodInvocations = extractFixerMethodInvocations(entries, {
2790
- allowedLocations: [
2791
- options.methodSkillPath,
2792
- options.methodSkillConfiguredPath,
2793
- ],
2794
- });
2795
- const artifacts = await publishFixerArtifacts(
2796
- admitted,
2797
- roleOutcome,
2798
- coordinates,
2799
- {
2800
- methodProvenance: options.methodProvenance,
2801
- methodInvocations,
2802
- },
2803
- );
2804
- return withOptionalGateProjection(
2805
- {
2806
- roleOutcome,
2807
- navigator,
2808
- artifacts,
2809
- runId: admitted.runId,
2810
- },
2811
- sessionDirectory,
2812
- detourGateContext(admitted, scope),
2813
- );
2814
- }
2815
-
2816
- /** Settle a lawful Fixer Terminal from the admitted session (shared #106 success interface). */
2817
- export async function settleFixerTerminalResult(
2818
- admitted: AdmittedFixerInvocation,
2819
- authority: DurablePrincipalAuthority,
2820
- options: {
2821
- readonly methodProvenance: PackagedMethodSkillProvenance;
2822
- readonly methodSkillPath: string;
2823
- readonly methodSkillConfiguredPath: string;
2824
- },
2825
- scope?: SettlementCourtScope,
2826
- ): Promise<TerminalResult> {
2827
- const settled = await settleLawfulFixerTerminalResult(admitted, authority, options, scope);
2828
- if (settled === undefined) {
2829
- throw new Error(
2830
- "Fixer Role run completed without a lawful typed terminal result",
2831
- );
2832
- }
2833
- return settled;
2834
- }
2835
-
2836
- export async function publishCollectorArtifacts(
2837
- admitted: AdmittedCollectorInvocation,
2838
- roleOutcome: TerminalRoleOutcome,
2839
- coordinates: DurablePrincipalCoordinates,
2840
- ): Promise<TerminalArtifactRef[]> {
2841
- return publishAcceptedTerminalArtifacts(admitted, roleOutcome, coordinates, {
2842
- report: {
2843
- role: "collector",
2844
- runId: admitted.runId,
2845
- outcome: roleOutcome,
2846
- },
2847
- evidence: {
2848
- runId: admitted.runId,
2849
- role: "collector",
2850
- ...(admitted.prNumber === undefined ? {} : { prNumber: admitted.prNumber }),
2851
- repository: admitted.repository.canonical,
2852
- manifestDigest: admitted.manifestDigest,
2853
- sessionDirectory: coordinates.sessionDirectory,
2854
- sessionFile: coordinates.sessionFile,
2855
- admittedRequestPath: admitted.admittedRequestPath,
2856
- attachments: acceptedArtifactAttachmentRefs(admitted.attachments),
2857
- },
2858
- });
2859
- }
2860
-
2861
- /** Lawful Collector accepted outcome extracted from session. */
2862
- export type LawfulCollectorRoleOutcome = Extract<TerminalRoleOutcome, { kind: "accepted" }>;
2863
- async function settleLawfulCollectorTerminalResult(
2864
- admitted: AdmittedCollectorInvocation,
2865
- authority: DurablePrincipalAuthority,
2866
- scope?: SettlementCourtScope,
2867
- ): Promise<TerminalResult | undefined> {
2868
- const coordinates = coordinatesFromAdmitted(authority, admitted);
2869
- const { sessionDirectory, sessionFile } = coordinates;
2870
- const entries = await readLawfulSettlementEntries(sessionFile) ?? [];
2871
- const roleOutcome = await sealedLedgerOutcome(admitted, "collector", scope);
2872
- if (roleOutcome?.role !== "collector" || roleOutcome.kind !== "accepted") {
2873
- // Bounded to the current attempt so multi-attempt resume timeout/no-output
2874
- // is not masked by a prior wait-tool residual (#633).
2875
- const scanStart = currentAttemptStartIndex(entries);
2876
- for (let index = entries.length - 1; index >= scanStart; index -= 1) {
2877
- const message = entries[index]?.message;
2878
- if (message?.role !== "toolResult") continue;
2879
- const residual = boundErroredToolCandidate(entries, index, message, COLLECTOR_WAIT_TOOL);
2880
- if (residual === undefined) continue;
2881
- const candidate = residual.candidate;
2882
- const details = isRecord(candidate) ? candidate : { candidate };
2883
- const failed = await settleFailureTerminalResult(
2884
- admitted,
2885
- {
2886
- cause: "output",
2887
- diagnostic: residual.diagnostic,
2888
- details,
2889
- },
2890
- authority,
2891
- scope ?? {},
2892
- );
2893
- return attachRecordedSubmissions(admitted, failed, scope);
2894
- }
2895
- return undefined;
2896
- }
2897
- const accepted: LawfulCollectorRoleOutcome = roleOutcome;
2898
- const navigator = extractNavigatorFact(entries);
2899
- const artifacts = await publishCollectorArtifacts(
2900
- admitted,
2901
- accepted,
2902
- coordinates,
2903
- );
2904
- return attachRecordedSubmissions(
2905
- admitted,
2906
- await withOptionalGateProjection(
2907
- {
2908
- roleOutcome: accepted,
2909
- navigator,
2910
- artifacts,
2911
- runId: admitted.runId,
2912
- },
2913
- sessionDirectory,
2914
- detourGateContext(admitted, scope),
2915
- ),
2916
- scope,
2917
- );
2918
- }
2919
-
2920
- /** Settle a lawful Collector Terminal from the admitted session. */
2921
- export async function settleCollectorTerminalResult(
2922
- admitted: AdmittedCollectorInvocation,
2923
- authority: DurablePrincipalAuthority,
2924
- scope?: SettlementCourtScope,
2925
- ): Promise<TerminalResult> {
2926
- const settled = await settleLawfulCollectorTerminalResult(admitted, authority, scope);
2927
- if (settled === undefined) {
2928
- throw new Error(
2929
- "Collector Role run completed without a lawful typed terminal result",
2930
- );
2931
- }
2932
- return settled;
2933
- }
2934
-
2935
- /** Try to settle a lawful Collector Terminal; undefined only for genuine absence. */
2936
- export async function trySettleCollectorTerminalResult(
2937
- admitted: AdmittedCollectorInvocation,
2938
- authority: DurablePrincipalAuthority,
2939
- scope?: SettlementCourtScope,
2940
- ): Promise<TerminalResult | undefined> {
2941
- return settleLawfulCollectorTerminalResult(admitted, authority, scope);
2942
- }
2943
-
2944
- /**
2945
- * #836: runtime cost is a machine fact recorded beside the role's original
2946
- * testimony (`ak_doctor_audit_candidate` custom entry), never merged into the
2947
- * accepted payload itself. Absence (no candidate entry, or entry without a
2948
- * cost sibling) reads as undefined — never invented.
2949
- */
2950
- function extractDoctorCandidateCostFact(
2951
- entries: readonly SessionEntry[],
2952
- ): DoctorCaseCost | undefined {
2953
- const scanStart = currentAttemptStartIndex(entries);
2954
- for (let i = entries.length - 1; i >= scanStart; i -= 1) {
2955
- const entry = entries[i];
2956
- if (entry?.type === "custom" && entry.customType === DOCTOR_CANDIDATE_ENTRY_TYPE) {
2957
- const data = entry.data;
2958
- return isRecord(data) ? (data.cost as DoctorCaseCost | undefined) : undefined;
2959
- }
2960
- }
2961
- return undefined;
2962
- }
2963
-
2964
- /**
2965
- * #836: the auditor's own no-receipt lifecycle facts are a machine fact about
2966
- * the audit leg, recorded beside (never merged into) the role's accepted
2967
- * testimony. Absence reads as undefined — never invented.
2968
- */
2969
- function extractDoctorCandidateAuditNoReceiptFact(
2970
- entries: readonly SessionEntry[],
2971
- ): unknown {
2972
- const scanStart = currentAttemptStartIndex(entries);
2973
- for (let i = entries.length - 1; i >= scanStart; i -= 1) {
2974
- const entry = entries[i];
2975
- if (entry?.type === "custom" && entry.customType === DOCTOR_CANDIDATE_ENTRY_TYPE) {
2976
- const data = entry.data;
2977
- return isRecord(data) ? data.auditNoReceipt : undefined;
2978
- }
2979
- }
2980
- return undefined;
2981
- }
2982
-
2983
- export async function publishDoctorArtifacts(
2984
- admitted: AdmittedDoctorInvocation,
2985
- roleOutcome: TerminalRoleOutcome,
2986
- coordinates: DurablePrincipalCoordinates,
2987
- options: {
2988
- readonly cost?: DoctorCaseCost;
2989
- readonly auditNoReceipt?: unknown;
2990
- } = {},
2991
- ): Promise<TerminalArtifactRef[]> {
2992
- return publishAcceptedTerminalArtifacts(admitted, roleOutcome, coordinates, {
2993
- report: {
2994
- role: "doctor",
2995
- runId: admitted.runId,
2996
- outcome: roleOutcome,
2997
- ...(options.cost === undefined ? {} : { cost: options.cost }),
2998
- ...(options.auditNoReceipt === undefined
2999
- ? {}
3000
- : { auditNoReceipt: options.auditNoReceipt }),
3001
- },
3002
- evidence: {
3003
- runId: admitted.runId,
3004
- role: "doctor",
3005
- issueNumber: admitted.issueNumber,
3006
- caseRunsPath: admitted.caseRunsPath,
3007
- caseIdentity: admitted.caseIdentity,
3008
- sessionDirectory: coordinates.sessionDirectory,
3009
- sessionFile: coordinates.sessionFile,
3010
- admittedRequestPath: admitted.admittedRequestPath,
3011
- attachments: acceptedArtifactAttachmentRefs(admitted.attachments),
3012
- },
3013
- });
3014
- }
3015
-
3016
- /** Lawful Doctor accepted/refused/audit_escalation outcome extracted from session. */
3017
- export type LawfulDoctorRoleOutcome = Extract<
3018
- TerminalRoleOutcome,
3019
- { kind: "accepted" } | { kind: "audit_escalation" }
3020
- >;
3021
- async function settleLawfulDoctorTerminalResult(
3022
- admitted: AdmittedDoctorInvocation,
3023
- authority: DurablePrincipalAuthority,
3024
- scope?: SettlementCourtScope,
3025
- ): Promise<TerminalResult | undefined> {
3026
- const sealed = await sealedLedgerOutcome(admitted, "doctor", scope);
3027
- const coordinates = coordinatesFromAdmitted(authority, admitted);
3028
- const { sessionDirectory, sessionFile } = coordinates;
3029
- const entries = await readLawfulSettlementEntries(sessionFile) ?? [];
3030
- if (sealed === undefined) return undefined;
3031
- if (sealed.kind === "audit_escalation") {
3032
- const artifacts = await publishDoctorArtifacts(admitted, sealed, coordinates);
3033
- return withOptionalGateProjection(
3034
- {
3035
- roleOutcome: sealed,
3036
- navigator: extractNavigatorFact(entries),
3037
- artifacts,
3038
- runId: admitted.runId,
3039
- },
3040
- sessionDirectory,
3041
- detourGateContext(admitted, scope),
3042
- );
3043
- }
3044
- const roleOutcome = sealed;
3045
- const navigator = extractNavigatorFact(entries);
3046
- const cost = extractDoctorCandidateCostFact(entries);
3047
- const auditNoReceipt = extractDoctorCandidateAuditNoReceiptFact(entries);
3048
- const artifacts = await publishDoctorArtifacts(
3049
- admitted,
3050
- roleOutcome,
3051
- coordinates,
3052
- {
3053
- ...(cost === undefined ? {} : { cost }),
3054
- ...(auditNoReceipt === undefined ? {} : { auditNoReceipt }),
3055
- },
3056
- );
3057
- return withOptionalGateProjection(
3058
- {
3059
- roleOutcome,
3060
- navigator,
3061
- artifacts,
3062
- runId: admitted.runId,
3063
- },
3064
- sessionDirectory,
3065
- detourGateContext(admitted, scope),
3066
- );
3067
- }
3068
-
3069
- /** Settle a lawful Doctor Terminal from the admitted session. */
3070
- export async function settleDoctorTerminalResult(
3071
- admitted: AdmittedDoctorInvocation,
3072
- authority: DurablePrincipalAuthority,
3073
- scope?: SettlementCourtScope,
3074
- ): Promise<TerminalResult> {
3075
- const settled = await settleLawfulDoctorTerminalResult(admitted, authority, scope);
3076
- if (settled === undefined) {
3077
- throw new Error(
3078
- "Doctor Role run completed without a lawful typed terminal result",
3079
- );
3080
- }
3081
- return settled;
3082
- }
3083
-
3084
- /** Try to settle a lawful Doctor Terminal; undefined only for genuine absence. */
3085
- export async function trySettleDoctorTerminalResult(
3086
- admitted: AdmittedDoctorInvocation,
3087
- authority: DurablePrincipalAuthority,
3088
- scope?: SettlementCourtScope,
3089
- ): Promise<TerminalResult | undefined> {
3090
- return settleLawfulDoctorTerminalResult(admitted, authority, scope);
3091
- }
3092
-
3093
2889
  /**
3094
2890
  * Shared accepted-settlement skeleton for seats that scan residual tool
3095
2891
  * candidates then project sealed ledger outcome (#502 DRY).
3096
2892
  * #757: sealed receipts pass through full decisiveFacts — one path, no per-seat projector.
3097
2893
  */
3098
2894
  type SeatAcceptedSettlementSpec = {
3099
- readonly role:
3100
- | "notary"
3101
- | "countersign"
3102
- | "gleaner-left"
3103
- | "inspector"
3104
- | "gatekeeper"
3105
- | "navigator"
3106
- | "auditor"
3107
- | "diarist"
3108
- | "secretariat";
2895
+ readonly role: TerminalRoleName;
3109
2896
  readonly toolName: string;
3110
2897
  };
3111
2898
 
@@ -3120,47 +2907,8 @@ function currentAttemptStartIndex(entries: readonly SessionEntry[]): number {
3120
2907
  return 0;
3121
2908
  }
3122
2909
 
3123
- /**
3124
- * Filed-seat success face via the sole accepted publisher (#953).
3125
- */
3126
- async function publishSeatAcceptedArtifacts(
3127
- admitted: {
3128
- readonly role: TerminalRoleName;
3129
- readonly runId: string;
3130
- readonly runDirectory: string;
3131
- readonly admittedRequestPath: string;
3132
- readonly attachments: readonly AcceptedArtifactAttachment[];
3133
- },
3134
- roleOutcome: TerminalRoleOutcome,
3135
- coordinates: DurablePrincipalCoordinates,
3136
- ): Promise<TerminalArtifactRef[]> {
3137
- return publishAcceptedTerminalArtifacts(admitted, roleOutcome, coordinates, {
3138
- report: {
3139
- role: admitted.role,
3140
- runId: admitted.runId,
3141
- outcome: roleOutcome,
3142
- },
3143
- evidence: {
3144
- runId: admitted.runId,
3145
- sessionDirectory: coordinates.sessionDirectory,
3146
- sessionFile: coordinates.sessionFile,
3147
- admittedRequestPath: admitted.admittedRequestPath,
3148
- attachments: acceptedArtifactAttachmentRefs(admitted.attachments),
3149
- },
3150
- });
3151
- }
3152
-
3153
2910
  async function settleLawfulSeatAcceptedTerminalResult(
3154
- admitted:
3155
- | AdmittedNotaryInvocation
3156
- | AdmittedCountersignInvocation
3157
- | AdmittedGleanerLeftInvocation
3158
- | AdmittedInspectorInvocation
3159
- | AdmittedGatekeeperInvocation
3160
- | AdmittedNavigatorInvocation
3161
- | import("./invocation.ts").AdmittedAuditorInvocation
3162
- | AdmittedDiaristInvocation
3163
- | AdmittedSecretariatInvocation,
2911
+ admitted: AdmittedRoleInvocation,
3164
2912
  authority: DurablePrincipalAuthority,
3165
2913
  spec: SeatAcceptedSettlementSpec,
3166
2914
  scope?: SettlementCourtScope,
@@ -3203,13 +2951,14 @@ async function settleLawfulSeatAcceptedTerminalResult(
3203
2951
  );
3204
2952
  }
3205
2953
  }
3206
- const roleOutcome = await closedLedgerOutcome(admitted, spec.role as TerminalRoleName, scope);
2954
+ const roleOutcome = await sealedLedgerOutcome(admitted, spec.role as TerminalRoleName, scope);
3207
2955
  if (roleOutcome !== undefined && (thisAttemptHasSeatSuccess || residual === undefined)) {
3208
2956
  const navigator = extractNavigatorFact(entries);
3209
- const artifacts = await publishSeatAcceptedArtifacts(
2957
+ const artifacts = await publishDeclaredSeatArtifacts(
3210
2958
  admitted,
3211
2959
  roleOutcome,
3212
2960
  coordinates,
2961
+ entries,
3213
2962
  );
3214
2963
  return withSubmissions(
3215
2964
  await withOptionalGateProjection(
@@ -3244,142 +2993,49 @@ async function settleLawfulSeatAcceptedTerminalResult(
3244
2993
  return undefined;
3245
2994
  }
3246
2995
 
3247
- /** Lawful Notary accepted outcome (pass/bounce/escalate). */
3248
- export type LawfulNotaryRoleOutcome = Extract<TerminalRoleOutcome, { kind: "accepted" }>;
3249
-
3250
- async function settleLawfulNotaryTerminalResult(
3251
- admitted: AdmittedNotaryInvocation,
2996
+ /** Accepted-tool seats: one scan, tool name from the composition-root record. */
2997
+ export async function trySettleAcceptedSeatTerminalResult(
2998
+ admitted: AdmittedRoleInvocation,
3252
2999
  authority: DurablePrincipalAuthority,
3253
3000
  scope?: SettlementCourtScope,
3254
3001
  ): Promise<TerminalResult | undefined> {
3255
- // #757: default sealed pass-through — no projectAccepted identity wrapper.
3256
- return settleLawfulSeatAcceptedTerminalResult(admitted, authority, {
3257
- role: "notary",
3258
- toolName: NOTARY_OUTPUT_TOOL_NAME,
3259
- }, scope);
3260
- }
3261
-
3262
- /** Settle a lawful Notary Terminal from the admitted session. */
3263
- export async function settleNotaryTerminalResult(
3264
- admitted: AdmittedNotaryInvocation,
3265
- authority: DurablePrincipalAuthority,
3266
- scope?: SettlementCourtScope,
3267
- ): Promise<TerminalResult> {
3268
- const settled = await settleLawfulNotaryTerminalResult(admitted, authority, scope);
3269
- if (settled === undefined) {
3270
- throw new Error(
3271
- "Notary Role run completed without a lawful typed terminal result",
3272
- );
3002
+ const toolName = packagedRoleAcceptedOutputTool(admitted.role);
3003
+ if (toolName === undefined) {
3004
+ throw new Error(`accepted-seat settlement is not declared for ${admitted.role}`);
3273
3005
  }
3274
- return settled;
3275
- }
3276
-
3277
- /** Try to settle a lawful Notary Terminal; undefined only for genuine absence. */
3278
- export async function trySettleNotaryTerminalResult(
3279
- admitted: AdmittedNotaryInvocation,
3280
- authority: DurablePrincipalAuthority,
3281
- scope?: SettlementCourtScope,
3282
- ): Promise<TerminalResult | undefined> {
3283
- return settleLawfulNotaryTerminalResult(admitted, authority, scope);
3284
- }
3285
-
3286
- /** Lawful Countersign accepted outcome (署/封驳/上呈, #572 / ADR 0074). */
3287
- export type LawfulCountersignRoleOutcome = Extract<TerminalRoleOutcome, { kind: "accepted" }>;
3288
-
3289
- async function settleLawfulCountersignTerminalResult(
3290
- admitted: AdmittedCountersignInvocation,
3291
- authority: DurablePrincipalAuthority,
3292
- scope?: SettlementCourtScope,
3293
- ): Promise<TerminalResult | undefined> {
3294
- return settleLawfulSeatAcceptedTerminalResult(admitted, authority, {
3295
- role: "countersign",
3296
- toolName: COUNTERSIGN_OUTPUT_TOOL_NAME,
3006
+ const settled = await settleLawfulSeatAcceptedTerminalResult(admitted, authority, {
3007
+ role: admitted.role,
3008
+ toolName,
3297
3009
  }, scope);
3298
- }
3299
-
3300
- /** Settle a lawful Countersign Terminal from the admitted session. */
3301
- export async function settleCountersignTerminalResult(
3302
- admitted: AdmittedCountersignInvocation,
3303
- authority: DurablePrincipalAuthority,
3304
- scope?: SettlementCourtScope,
3305
- ): Promise<TerminalResult> {
3306
- const settled = await settleLawfulCountersignTerminalResult(admitted, authority, scope);
3307
- if (settled === undefined) {
3308
- throw new Error(
3309
- "Countersign Role run completed without a lawful typed terminal result",
3310
- );
3010
+ const record = packagedRoleMetadata(admitted.role);
3011
+ if (
3012
+ settled === undefined
3013
+ || record === undefined
3014
+ || !("projectCountersignTerminal" in record)
3015
+ || record.projectCountersignTerminal !== true
3016
+ ) {
3017
+ return settled;
3311
3018
  }
3312
- return settled;
3019
+ return applySecretariatCountersignTerminal(admitted, authority, settled);
3313
3020
  }
3314
3021
 
3315
- /** Try to settle a lawful Countersign Terminal; undefined only for genuine absence. */
3316
- export async function trySettleCountersignTerminalResult(
3317
- admitted: AdmittedCountersignInvocation,
3022
+ /** One settlement dispatch. The registry `settlement` leaf selects the path. */
3023
+ export async function trySettlePublicSeat(
3024
+ admitted: AdmittedRoleInvocation,
3318
3025
  authority: DurablePrincipalAuthority,
3319
- scope?: SettlementCourtScope,
3026
+ scope: { readonly courtAttemptId?: string } | undefined,
3027
+ material: PackagedMethodSkillMaterial | undefined,
3028
+ packageRoot: string,
3320
3029
  ): Promise<TerminalResult | undefined> {
3321
- return settleLawfulCountersignTerminalResult(admitted, authority, scope);
3322
- }
3323
-
3324
- /** Lawful Gleaner-Left accepted outcome (completed 弹章, #502). */
3325
- export type LawfulGleanerLeftRoleOutcome = Extract<TerminalRoleOutcome, { kind: "accepted" }>;
3326
-
3327
- async function settleLawfulGleanerLeftTerminalResult(
3328
- admitted: AdmittedGleanerLeftInvocation,
3329
- authority: DurablePrincipalAuthority,
3330
- scope?: SettlementCourtScope,
3331
- ): Promise<TerminalResult | undefined> {
3332
- return settleLawfulSeatAcceptedTerminalResult(admitted, authority, {
3333
- role: "gleaner-left",
3334
- toolName: GLEANER_LEFT_OUTPUT_TOOL_NAME,
3335
- }, scope);
3336
- }
3337
-
3338
- /** Settle a lawful Gleaner-Left Terminal from the admitted session. */
3339
- export async function settleGleanerLeftTerminalResult(
3340
- admitted: AdmittedGleanerLeftInvocation,
3341
- authority: DurablePrincipalAuthority,
3342
- scope?: SettlementCourtScope,
3343
- ): Promise<TerminalResult> {
3344
- const settled = await settleLawfulGleanerLeftTerminalResult(admitted, authority, scope);
3345
- if (settled === undefined) {
3346
- throw new Error(
3347
- "Gleaner-Left Role run completed without a lawful typed terminal result",
3348
- );
3349
- }
3350
- return settled;
3351
- }
3352
-
3353
- /** Try to settle a lawful Gleaner-Left Terminal; undefined only for genuine absence. */
3354
- export async function trySettleGleanerLeftTerminalResult(
3355
- admitted: AdmittedGleanerLeftInvocation,
3356
- authority: DurablePrincipalAuthority,
3357
- scope?: SettlementCourtScope,
3358
- ): Promise<TerminalResult | undefined> {
3359
- return settleLawfulGleanerLeftTerminalResult(admitted, authority, scope);
3360
- }
3361
-
3362
- /** Lawful Diarist accepted outcome (completed 入录选择, #708). */
3363
- export type LawfulDiaristRoleOutcome = Extract<TerminalRoleOutcome, { kind: "accepted" }>;
3364
-
3365
- async function settleLawfulDiaristTerminalResult(
3366
- admitted: AdmittedDiaristInvocation,
3367
- authority: DurablePrincipalAuthority,
3368
- scope?: SettlementCourtScope,
3369
- ): Promise<TerminalResult | undefined> {
3370
- return settleLawfulSeatAcceptedTerminalResult(admitted, authority, {
3371
- role: "diarist",
3372
- toolName: DIARIST_OUTPUT_TOOL_NAME,
3373
- }, scope);
3374
- }
3375
-
3376
- /** Try to settle a lawful Diarist Terminal; undefined only for genuine absence. */
3377
- export async function trySettleDiaristTerminalResult(
3378
- admitted: AdmittedDiaristInvocation,
3379
- authority: DurablePrincipalAuthority,
3380
- scope?: SettlementCourtScope,
3381
- ): Promise<TerminalResult | undefined> {
3382
- return settleLawfulDiaristTerminalResult(admitted, authority, scope);
3030
+ const face = seatArtifactFace(admitted.role);
3031
+ if (face.method === "observed" && material === undefined) return undefined;
3032
+ const options: MethodPublicationOptions =
3033
+ face.method === "observed" && material !== undefined
3034
+ ? observedMethodSkillOptions(packageRoot, material)
3035
+ : face.method === "optional" && material !== undefined
3036
+ ? { methodProvenance: material.provenance }
3037
+ : {};
3038
+ return settleSeat(admitted, authority, scope, options);
3383
3039
  }
3384
3040
 
3385
3041
  /**
@@ -3405,7 +3061,7 @@ function countersignTerminalFromEntries(
3405
3061
  entry.data !== null && typeof entry.data === "object" && !Array.isArray(entry.data)
3406
3062
  ? (entry.data as Record<string, unknown>)
3407
3063
  : undefined;
3408
- if (data?.officer !== "countersign") continue;
3064
+ if (data === undefined || !packagedDurableOfficerEntry(data.officer)) continue;
3409
3065
  if (data.receipt === undefined) continue;
3410
3066
  const runId =
3411
3067
  typeof data.runId === "string" && data.runId.trim() !== ""
@@ -3462,16 +3118,11 @@ function acceptedSecretariatDefinesOfficerProjection(
3462
3118
  return false;
3463
3119
  }
3464
3120
 
3465
- async function settleLawfulSecretariatTerminalResult(
3466
- admitted: AdmittedSecretariatInvocation,
3121
+ async function applySecretariatCountersignTerminal(
3122
+ admitted: AdmittedRoleInvocation,
3467
3123
  authority: DurablePrincipalAuthority,
3468
- scope?: SettlementCourtScope,
3469
- ): Promise<TerminalResult | undefined> {
3470
- const settled = await settleLawfulSeatAcceptedTerminalResult(admitted, authority, {
3471
- role: "secretariat",
3472
- toolName: SECRETARIAT_OUTPUT_TOOL_NAME,
3473
- }, scope);
3474
- if (settled === undefined) return undefined;
3124
+ settled: TerminalResult,
3125
+ ): Promise<TerminalResult> {
3475
3126
  const coordinates = coordinatesFromAdmitted(authority, admitted);
3476
3127
  const entries = await readLawfulSettlementEntries(coordinates.sessionFile) ?? [];
3477
3128
  const officer = countersignTerminalFromEntries(entries);
@@ -3507,443 +3158,6 @@ async function settleLawfulSecretariatTerminalResult(
3507
3158
  return settled;
3508
3159
  }
3509
3160
 
3510
- /** Try to settle a lawful Secretariat Terminal; undefined only for genuine absence. */
3511
- export async function trySettleSecretariatTerminalResult(
3512
- admitted: AdmittedSecretariatInvocation,
3513
- authority: DurablePrincipalAuthority,
3514
- scope?: SettlementCourtScope,
3515
- ): Promise<TerminalResult | undefined> {
3516
- return settleLawfulSecretariatTerminalResult(admitted, authority, scope);
3517
- }
3518
-
3519
- /** Lawful Inspector accepted outcome (pass/bounce/escalate). */
3520
- export type LawfulInspectorRoleOutcome = Extract<TerminalRoleOutcome, { kind: "accepted" }>;
3521
-
3522
- async function settleLawfulInspectorTerminalResult(
3523
- admitted: AdmittedInspectorInvocation,
3524
- authority: DurablePrincipalAuthority,
3525
- scope?: SettlementCourtScope,
3526
- ): Promise<TerminalResult | undefined> {
3527
- return settleLawfulSeatAcceptedTerminalResult(admitted, authority, {
3528
- role: "inspector",
3529
- toolName: INSPECTOR_OUTPUT_TOOL_NAME,
3530
- }, scope);
3531
- }
3532
-
3533
- export async function trySettleInspectorTerminalResult(
3534
- admitted: AdmittedInspectorInvocation,
3535
- authority: DurablePrincipalAuthority,
3536
- scope?: SettlementCourtScope,
3537
- ): Promise<TerminalResult | undefined> {
3538
- return settleLawfulInspectorTerminalResult(admitted, authority, scope);
3539
- }
3540
-
3541
- /** Lawful Gatekeeper accepted outcome (dispatch | pass, #639). */
3542
- export type LawfulGatekeeperRoleOutcome = Extract<TerminalRoleOutcome, { kind: "accepted" }>;
3543
-
3544
- async function settleLawfulGatekeeperTerminalResult(
3545
- admitted: AdmittedGatekeeperInvocation,
3546
- authority: DurablePrincipalAuthority,
3547
- scope?: SettlementCourtScope,
3548
- ): Promise<TerminalResult | undefined> {
3549
- return settleLawfulSeatAcceptedTerminalResult(admitted, authority, {
3550
- role: "gatekeeper",
3551
- toolName: GATEKEEPER_OUTPUT_TOOL_NAME,
3552
- }, scope);
3553
- }
3554
-
3555
- /** Try to settle a lawful Gatekeeper Terminal; undefined only for genuine absence. */
3556
- export async function trySettleGatekeeperTerminalResult(
3557
- admitted: AdmittedGatekeeperInvocation,
3558
- authority: DurablePrincipalAuthority,
3559
- scope?: SettlementCourtScope,
3560
- ): Promise<TerminalResult | undefined> {
3561
- return settleLawfulGatekeeperTerminalResult(admitted, authority, scope);
3562
- }
3563
-
3564
- /** Lawful Navigator accepted outcome (route advice, #639). */
3565
- export type LawfulNavigatorRoleOutcome = Extract<TerminalRoleOutcome, { kind: "accepted" }>;
3566
-
3567
- async function settleLawfulNavigatorTerminalResult(
3568
- admitted: AdmittedNavigatorInvocation,
3569
- authority: DurablePrincipalAuthority,
3570
- scope?: SettlementCourtScope,
3571
- ): Promise<TerminalResult | undefined> {
3572
- return settleLawfulSeatAcceptedTerminalResult(admitted, authority, {
3573
- role: "navigator",
3574
- toolName: NAVIGATOR_OUTPUT_TOOL_NAME,
3575
- }, scope);
3576
- }
3577
-
3578
- /** Try to settle a lawful Navigator Terminal; undefined only for genuine absence. */
3579
- export async function trySettleNavigatorTerminalResult(
3580
- admitted: AdmittedNavigatorInvocation,
3581
- authority: DurablePrincipalAuthority,
3582
- scope?: SettlementCourtScope,
3583
- ): Promise<TerminalResult | undefined> {
3584
- return settleLawfulNavigatorTerminalResult(admitted, authority, scope);
3585
- }
3586
-
3587
- /** Lawful Auditor accepted outcome (pass/bounce/escalate, #675 / #754). */
3588
- export type LawfulAuditorRoleOutcome = Extract<TerminalRoleOutcome, { kind: "accepted" }>;
3589
-
3590
- async function settleLawfulAuditorTerminalResult(
3591
- admitted: import("./invocation.ts").AdmittedAuditorInvocation,
3592
- authority: DurablePrincipalAuthority,
3593
- scope?: SettlementCourtScope,
3594
- ): Promise<TerminalResult | undefined> {
3595
- return settleLawfulSeatAcceptedTerminalResult(admitted, authority, {
3596
- role: "auditor",
3597
- toolName: AUDITOR_OUTPUT_TOOL_NAME,
3598
- }, scope);
3599
- }
3600
-
3601
- export async function trySettleAuditorTerminalResult(
3602
- admitted: import("./invocation.ts").AdmittedAuditorInvocation,
3603
- authority: DurablePrincipalAuthority,
3604
- scope?: SettlementCourtScope,
3605
- ): Promise<TerminalResult | undefined> {
3606
- return settleLawfulAuditorTerminalResult(admitted, authority, scope);
3607
- }
3608
-
3609
- /** Try to settle a lawful Coder Terminal; undefined only for genuine absence. */
3610
- export async function trySettleCoderTerminalResult(
3611
- admitted: AdmittedCoderInvocation,
3612
- authority: DurablePrincipalAuthority,
3613
- options: {
3614
- readonly methodProvenance?: PackagedMethodSkillProvenance;
3615
- } = {},
3616
- scope?: SettlementCourtScope,
3617
- ): Promise<TerminalResult | undefined> {
3618
- return settleLawfulCoderTerminalResult(admitted, authority, options, scope);
3619
- }
3620
-
3621
- /** Try to settle a lawful Fixer Terminal; undefined only for genuine absence. */
3622
- export async function trySettleFixerTerminalResult(
3623
- admitted: AdmittedFixerInvocation,
3624
- authority: DurablePrincipalAuthority,
3625
- options: {
3626
- readonly methodProvenance: PackagedMethodSkillProvenance;
3627
- readonly methodSkillPath: string;
3628
- readonly methodSkillConfiguredPath: string;
3629
- },
3630
- scope?: SettlementCourtScope,
3631
- ): Promise<TerminalResult | undefined> {
3632
- return settleLawfulFixerTerminalResult(admitted, authority, options, scope);
3633
- }
3634
-
3635
- /**
3636
- * Observe forced Reviewer ak-cross-m-review Skill expansions from the session.
3637
- * Expansion evidence is package-path only; ambient home locations never count.
3638
- */
3639
- export function extractReviewerMethodInvocations(
3640
- entries: readonly SessionEntry[],
3641
- options: {
3642
- readonly allowedLocations: readonly string[];
3643
- },
3644
- ): readonly ObservedPackagedMethodSkillInvocation[] {
3645
- const observed: ObservedPackagedMethodSkillInvocation[] = [];
3646
- for (const entry of entries) {
3647
- if (entry?.type !== "message") continue;
3648
- const message = entry.message;
3649
- if (message?.role !== "user") continue;
3650
- const text = sessionMessageText(message);
3651
- if (text.length === 0) continue;
3652
- const hit = observePackagedMethodSkillInvocation(text, {
3653
- name: "ak-cross-m-review",
3654
- allowedLocations: options.allowedLocations,
3655
- });
3656
- if (hit !== undefined) observed.push(hit);
3657
- }
3658
- return Object.freeze(observed);
3659
- }
3660
-
3661
- /**
3662
- * Publish lawful Reviewer success Artifacts on the shared #106 success interface.
3663
- * Evidence records package ak-cross-m-review provenance and typed expansion
3664
- * observation without ambient home Skill paths.
3665
- */
3666
- export async function publishReviewerArtifacts(
3667
- admitted: AdmittedReviewerInvocation,
3668
- roleOutcome: TerminalRoleOutcome,
3669
- coordinates: DurablePrincipalCoordinates,
3670
- options: {
3671
- readonly methodProvenance: PackagedMethodSkillProvenance;
3672
- readonly methodInvocations?: readonly ObservedPackagedMethodSkillInvocation[];
3673
- },
3674
- ): Promise<TerminalArtifactRef[]> {
3675
- return publishAcceptedTerminalArtifacts(admitted, roleOutcome, coordinates, {
3676
- report: {
3677
- role: "reviewer",
3678
- runId: admitted.runId,
3679
- outcome: roleOutcome,
3680
- },
3681
- evidence: {
3682
- runId: admitted.runId,
3683
- role: "reviewer",
3684
- sessionDirectory: coordinates.sessionDirectory,
3685
- sessionFile: coordinates.sessionFile,
3686
- admittedRequestPath: admitted.admittedRequestPath,
3687
- baseRevision: admitted.baseRevision,
3688
- lens: admitted.lens,
3689
- authorityRefs: [...admitted.authorityRefs],
3690
- ...(admitted.instructionEmpty
3691
- ? {}
3692
- : { callerProvenance: admitted.instruction }),
3693
- attachments: acceptedArtifactAttachmentRefs(admitted.attachments),
3694
- methodProvenance: options.methodProvenance,
3695
- // Forced package method: availability is package-bound; expansion only when observed.
3696
- methodInvocationObserved: (options.methodInvocations ?? []).length > 0,
3697
- methodInvocations: options.methodInvocations ?? [],
3698
- },
3699
- });
3700
- }
3701
-
3702
- /** Lawful Reviewer accepted outcome extracted from session (no LLM auditor after #495 S6). */
3703
- export type LawfulReviewerRoleOutcome = Extract<TerminalRoleOutcome, { kind: "accepted" }>;
3704
- async function settleLawfulReviewerTerminalResult(
3705
- admitted: AdmittedReviewerInvocation,
3706
- authority: DurablePrincipalAuthority,
3707
- options: {
3708
- readonly methodProvenance: PackagedMethodSkillProvenance;
3709
- readonly methodSkillPath: string;
3710
- readonly methodSkillConfiguredPath: string;
3711
- },
3712
- scope?: SettlementCourtScope,
3713
- ): Promise<TerminalResult | undefined> {
3714
- const sealed = await sealedLedgerOutcome(admitted, "reviewer", scope);
3715
- if (sealed?.role !== "reviewer" || sealed.kind !== "accepted") return undefined;
3716
- const coordinates = coordinatesFromAdmitted(authority, admitted);
3717
- const { sessionDirectory, sessionFile } = coordinates;
3718
- const entries = await readLawfulSettlementEntries(sessionFile) ?? [];
3719
- const roleOutcome: LawfulReviewerRoleOutcome = sealed;
3720
- const navigator = extractNavigatorFact(entries);
3721
- const methodInvocations = extractReviewerMethodInvocations(entries, {
3722
- allowedLocations: [
3723
- options.methodSkillPath,
3724
- options.methodSkillConfiguredPath,
3725
- ],
3726
- });
3727
- const artifacts = await publishReviewerArtifacts(
3728
- admitted,
3729
- roleOutcome,
3730
- coordinates,
3731
- {
3732
- methodProvenance: options.methodProvenance,
3733
- methodInvocations,
3734
- },
3735
- );
3736
- return withOptionalGateProjection(
3737
- {
3738
- roleOutcome,
3739
- navigator,
3740
- artifacts,
3741
- runId: admitted.runId,
3742
- },
3743
- sessionDirectory,
3744
- detourGateContext(admitted, scope),
3745
- );
3746
- }
3747
-
3748
- /** Settle a lawful Reviewer Terminal from the admitted session (shared #106 success interface). */
3749
- export async function settleReviewerTerminalResult(
3750
- admitted: AdmittedReviewerInvocation,
3751
- authority: DurablePrincipalAuthority,
3752
- options: {
3753
- readonly methodProvenance: PackagedMethodSkillProvenance;
3754
- readonly methodSkillPath: string;
3755
- readonly methodSkillConfiguredPath: string;
3756
- },
3757
- scope?: SettlementCourtScope,
3758
- ): Promise<TerminalResult> {
3759
- const settled = await settleLawfulReviewerTerminalResult(admitted, authority, options, scope);
3760
- if (settled === undefined) {
3761
- throw new Error(
3762
- "Reviewer Role run completed without a lawful typed terminal result",
3763
- );
3764
- }
3765
- return settled;
3766
- }
3767
-
3768
- /** Try to settle a lawful Reviewer Terminal; undefined only for genuine absence. */
3769
- export async function trySettleReviewerTerminalResult(
3770
- admitted: AdmittedReviewerInvocation,
3771
- authority: DurablePrincipalAuthority,
3772
- options: {
3773
- readonly methodProvenance: PackagedMethodSkillProvenance;
3774
- readonly methodSkillPath: string;
3775
- readonly methodSkillConfiguredPath: string;
3776
- },
3777
- scope?: SettlementCourtScope,
3778
- ): Promise<TerminalResult | undefined> {
3779
- return settleLawfulReviewerTerminalResult(admitted, authority, options, scope);
3780
- }
3781
-
3782
- /**
3783
- * Observe forced Merger resolving-merge-conflicts Skill expansions from the session.
3784
- * Expansion evidence is package-path only; ambient home locations never count.
3785
- */
3786
- export function extractMergerMethodInvocations(
3787
- entries: readonly SessionEntry[],
3788
- options: {
3789
- readonly allowedLocations: readonly string[];
3790
- },
3791
- ): readonly ObservedPackagedMethodSkillInvocation[] {
3792
- const observed: ObservedPackagedMethodSkillInvocation[] = [];
3793
- for (const entry of entries) {
3794
- if (entry?.type !== "message") continue;
3795
- const message = entry.message;
3796
- if (message?.role !== "user") continue;
3797
- const text = sessionMessageText(message);
3798
- if (text.length === 0) continue;
3799
- const hit = observePackagedMethodSkillInvocation(text, {
3800
- name: "resolving-merge-conflicts",
3801
- allowedLocations: options.allowedLocations,
3802
- });
3803
- if (hit !== undefined) observed.push(hit);
3804
- }
3805
- return Object.freeze(observed);
3806
- }
3807
-
3808
- /**
3809
- * Publish lawful Merger success Artifacts on the shared #106 success interface.
3810
- * Evidence records package method provenance, forced expansion observation, and
3811
- * adapter-derived mechanical envelope facts without ambient home Skill paths.
3812
- */
3813
- export async function publishMergerArtifacts(
3814
- admitted: AdmittedMergerInvocation,
3815
- roleOutcome: TerminalRoleOutcome,
3816
- coordinates: DurablePrincipalCoordinates,
3817
- options: {
3818
- readonly methodProvenance: PackagedMethodSkillProvenance;
3819
- readonly methodInvocations?: readonly ObservedPackagedMethodSkillInvocation[];
3820
- },
3821
- ): Promise<TerminalArtifactRef[]> {
3822
- return publishAcceptedTerminalArtifacts(admitted, roleOutcome, coordinates, {
3823
- report: {
3824
- role: "merger",
3825
- runId: admitted.runId,
3826
- outcome: roleOutcome,
3827
- },
3828
- evidence: {
3829
- runId: admitted.runId,
3830
- role: "merger",
3831
- sessionDirectory: coordinates.sessionDirectory,
3832
- sessionFile: coordinates.sessionFile,
3833
- admittedRequestPath: admitted.admittedRequestPath,
3834
- mergerInputPath: admitted.mergerInputPath,
3835
- derived: admitted.derived,
3836
- attachments: acceptedArtifactAttachmentRefs(admitted.attachments),
3837
- methodProvenance: options.methodProvenance,
3838
- methodInvocationObserved: (options.methodInvocations ?? []).length > 0,
3839
- methodInvocations: options.methodInvocations ?? [],
3840
- },
3841
- });
3842
- }
3843
-
3844
- /** Lawful Merger accepted outcome extracted from session (shared success interface). */
3845
- export type LawfulMergerRoleOutcome = Extract<TerminalRoleOutcome, { kind: "accepted" }>;
3846
- async function settleLawfulMergerTerminalResult(
3847
- admitted: AdmittedMergerInvocation,
3848
- authority: DurablePrincipalAuthority,
3849
- options: {
3850
- readonly methodProvenance: PackagedMethodSkillProvenance;
3851
- readonly methodSkillPath: string;
3852
- readonly methodSkillConfiguredPath: string;
3853
- },
3854
- scope?: SettlementCourtScope,
3855
- ): Promise<TerminalResult | undefined> {
3856
- const coordinates = coordinatesFromAdmitted(authority, admitted);
3857
- const { sessionDirectory, sessionFile } = coordinates;
3858
- const entries = await readLawfulSettlementEntries(sessionFile) ?? [];
3859
- const roleOutcome = await sealedLedgerOutcome(admitted, "merger", scope);
3860
- if (roleOutcome?.role !== "merger" || roleOutcome.kind !== "accepted") {
3861
- // #836: non-sole-round barrier deleted; residual path is host/session only.
3862
- for (let index = entries.length - 1; index >= 0; index -= 1) {
3863
- const message = entries[index]?.message;
3864
- if (message?.role !== "toolResult") continue;
3865
- const residual = boundErroredToolCandidate(entries, index, message, MERGER_OUTPUT_TOOL_NAME);
3866
- if (residual === undefined) continue;
3867
- const candidate = residual.candidate;
3868
- const details = isRecord(candidate) ? candidate : { candidate };
3869
- const failed = await settleFailureTerminalResult(
3870
- admitted,
3871
- {
3872
- cause: "output",
3873
- diagnostic: residual.diagnostic,
3874
- details,
3875
- },
3876
- authority,
3877
- scope ?? {},
3878
- );
3879
- return attachRecordedSubmissions(admitted, failed, scope);
3880
- }
3881
- return undefined;
3882
- }
3883
- const accepted: LawfulMergerRoleOutcome = roleOutcome;
3884
- const navigator = extractNavigatorFact(entries);
3885
- const methodInvocations = extractMergerMethodInvocations(entries, {
3886
- allowedLocations: [options.methodSkillPath, options.methodSkillConfiguredPath],
3887
- });
3888
- const artifacts = await publishMergerArtifacts(
3889
- admitted,
3890
- accepted,
3891
- coordinates,
3892
- {
3893
- methodProvenance: options.methodProvenance,
3894
- methodInvocations,
3895
- },
3896
- );
3897
- return attachRecordedSubmissions(
3898
- admitted,
3899
- await withOptionalGateProjection(
3900
- {
3901
- roleOutcome: accepted,
3902
- navigator,
3903
- artifacts,
3904
- runId: admitted.runId,
3905
- },
3906
- sessionDirectory,
3907
- detourGateContext(admitted, scope),
3908
- ),
3909
- scope,
3910
- );
3911
- }
3912
-
3913
- /** Settle a lawful Merger Terminal from the admitted session (shared #106 success interface). */
3914
- export async function settleMergerTerminalResult(
3915
- admitted: AdmittedMergerInvocation,
3916
- authority: DurablePrincipalAuthority,
3917
- options: {
3918
- readonly methodProvenance: PackagedMethodSkillProvenance;
3919
- readonly methodSkillPath: string;
3920
- readonly methodSkillConfiguredPath: string;
3921
- },
3922
- scope?: SettlementCourtScope,
3923
- ): Promise<TerminalResult> {
3924
- const settled = await settleLawfulMergerTerminalResult(admitted, authority, options, scope);
3925
- if (settled === undefined) {
3926
- throw new Error(
3927
- "Merger Role run completed without a lawful typed terminal result",
3928
- );
3929
- }
3930
- return settled;
3931
- }
3932
-
3933
- /** Try to settle a lawful Merger Terminal; undefined only for genuine absence. */
3934
- export async function trySettleMergerTerminalResult(
3935
- admitted: AdmittedMergerInvocation,
3936
- authority: DurablePrincipalAuthority,
3937
- options: {
3938
- readonly methodProvenance: PackagedMethodSkillProvenance;
3939
- readonly methodSkillPath: string;
3940
- readonly methodSkillConfiguredPath: string;
3941
- },
3942
- scope?: SettlementCourtScope,
3943
- ): Promise<TerminalResult | undefined> {
3944
- return settleLawfulMergerTerminalResult(admitted, authority, options, scope);
3945
- }
3946
-
3947
3161
  /** One failed attempt to place a durable failure artifact (path is private layout). */
3948
3162
  type PublicationAttempt = {
3949
3163
  readonly path: string;
@@ -4220,7 +3434,7 @@ export async function settleFailureTerminalResult(
4220
3434
  ) {
4221
3435
  let decisiveFacts: NoReceiptLifecycleFacts & Record<string, unknown> = facts;
4222
3436
  // #676 D1/J3: project durable bind-target rejection onto public no_receipt facts.
4223
- if (admitted.role === "collector") {
3437
+ if (packagedProjectsTargetBindRejection(admitted.role)) {
4224
3438
  const bindRejection = extractCollectorTargetBindRejection(entries.slice(attemptStart));
4225
3439
  if (bindRejection !== undefined) {
4226
3440
  decisiveFacts = {