cool-workflow 0.1.98 → 0.2.1

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 (315) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/README.md +18 -7
  4. package/apps/architecture-review/app.json +2 -2
  5. package/apps/architecture-review/workflow.js +12 -12
  6. package/apps/architecture-review-fast/app.json +1 -1
  7. package/apps/end-to-end-golden-path/app.json +1 -1
  8. package/apps/pr-review-fix-ci/app.json +1 -1
  9. package/apps/release-cut/app.json +1 -1
  10. package/apps/research-synthesis/app.json +1 -1
  11. package/dist/cli/dispatch.js +236 -0
  12. package/dist/cli/entry.js +120 -0
  13. package/dist/cli/io.js +21 -4
  14. package/dist/cli/parseargv.js +157 -0
  15. package/dist/cli.js +6 -33
  16. package/dist/core/capability-table.js +3518 -0
  17. package/dist/core/format/help.js +314 -0
  18. package/dist/{state-explosion/format.js → core/format/state-explosion-text.js} +10 -1
  19. package/dist/core/hash.js +137 -0
  20. package/dist/core/multi-agent/candidate-scoring.js +219 -0
  21. package/dist/core/multi-agent/collaboration.js +481 -0
  22. package/dist/core/multi-agent/coordinator.js +515 -0
  23. package/dist/core/multi-agent/eval-replay.js +306 -0
  24. package/dist/core/multi-agent/runtime.js +929 -0
  25. package/dist/core/multi-agent/topology.js +298 -0
  26. package/dist/core/multi-agent/trust-policy.js +197 -0
  27. package/dist/core/pipeline/commit-gate.js +320 -0
  28. package/dist/{pipeline-contract.js → core/pipeline/contract.js} +24 -37
  29. package/dist/core/pipeline/dispatch.js +103 -0
  30. package/dist/core/pipeline/drive-decide.js +227 -0
  31. package/dist/core/pipeline/error-feedback.js +161 -0
  32. package/dist/core/pipeline/loop-expansion.js +124 -0
  33. package/dist/{result-normalize.js → core/pipeline/result-normalize.js} +14 -37
  34. package/dist/core/pipeline/runner.js +230 -0
  35. package/dist/{contract-migration.js → core/state/contract-migration.js} +68 -92
  36. package/dist/{state-migrations.js → core/state/migrations.js} +103 -96
  37. package/dist/core/state/node-projection.js +95 -0
  38. package/dist/core/state/node-snapshot.js +230 -0
  39. package/dist/core/state/run-paths.js +93 -0
  40. package/dist/{schema-validate.js → core/state/schema-validate.js} +35 -28
  41. package/dist/core/state/schema.js +50 -0
  42. package/dist/core/state/state-explosion/digest.js +243 -0
  43. package/dist/core/state/state-explosion/graph.js +527 -0
  44. package/dist/{state-explosion → core/state/state-explosion}/helpers.js +34 -3
  45. package/dist/core/state/state-explosion/report.js +187 -0
  46. package/dist/{state-explosion → core/state/state-explosion}/size.js +25 -7
  47. package/dist/{state-node.js → core/state/state-node.js} +90 -113
  48. package/dist/core/state/types.js +19 -0
  49. package/dist/{validation.js → core/state/validation.js} +64 -131
  50. package/dist/core/trust/evidence-grounding.js +134 -0
  51. package/dist/core/trust/ledger.js +199 -0
  52. package/dist/{telemetry-attestation.js → core/trust/telemetry-attestation.js} +94 -68
  53. package/dist/core/trust/telemetry-ledger.js +127 -0
  54. package/dist/core/types.js +20 -0
  55. package/dist/core/version.js +16 -0
  56. package/dist/{workflow-app-framework.js → core/workflow-apps/app-schema.js} +337 -426
  57. package/dist/mcp/dispatch.js +104 -0
  58. package/dist/mcp/server.js +144 -0
  59. package/dist/mcp-server.js +6 -84
  60. package/dist/{agent-config.js → shell/agent-config.js} +118 -71
  61. package/dist/shell/app-run-cli.js +88 -0
  62. package/dist/shell/audit-cli.js +259 -0
  63. package/dist/shell/audit-provenance.js +83 -0
  64. package/dist/shell/candidate-scoring-io.js +459 -0
  65. package/dist/shell/collaboration-io.js +264 -0
  66. package/dist/shell/commit-summary.js +104 -0
  67. package/dist/shell/commit.js +290 -0
  68. package/dist/shell/coordinator-io.js +476 -0
  69. package/dist/shell/demo-cli.js +19 -0
  70. package/dist/shell/dispatch.js +162 -0
  71. package/dist/shell/doctor.js +292 -0
  72. package/dist/shell/drive.js +885 -0
  73. package/dist/shell/error-feedback-io.js +305 -0
  74. package/dist/shell/eval-io.js +473 -0
  75. package/dist/{multi-agent-eval/format.js → shell/eval-text.js} +78 -49
  76. package/dist/{evidence-reasoning.js → shell/evidence-reasoning.js} +124 -255
  77. package/dist/shell/exec-backend-cli.js +88 -0
  78. package/dist/shell/execution-backend/agent.js +507 -0
  79. package/dist/shell/execution-backend/ci.js +15 -0
  80. package/dist/shell/execution-backend/container.js +69 -0
  81. package/dist/shell/execution-backend/envelopes.js +55 -0
  82. package/dist/shell/execution-backend/local.js +113 -0
  83. package/dist/shell/execution-backend/probes.js +175 -0
  84. package/dist/shell/execution-backend/registry.js +402 -0
  85. package/dist/shell/execution-backend/remote.js +128 -0
  86. package/dist/shell/execution-backend/types.js +11 -0
  87. package/dist/shell/feedback-cli.js +81 -0
  88. package/dist/shell/feedback-operations.js +48 -0
  89. package/dist/shell/fs-atomic.js +276 -0
  90. package/dist/shell/harness.js +98 -0
  91. package/dist/shell/ledger-cli.js +212 -0
  92. package/dist/shell/ledger-io.js +169 -0
  93. package/dist/shell/man-cli.js +89 -0
  94. package/dist/shell/metrics-cli.js +100 -0
  95. package/dist/shell/multi-agent-cli.js +1002 -0
  96. package/dist/shell/multi-agent-host.js +563 -0
  97. package/dist/shell/multi-agent-io.js +387 -0
  98. package/dist/{multi-agent-operator-ux.js → shell/multi-agent-operator-ux.js} +234 -191
  99. package/dist/shell/node-store.js +124 -0
  100. package/dist/{observability/format.js → shell/observability-format.js} +7 -1
  101. package/dist/{observability/intake.js → shell/observability-intake.js} +79 -56
  102. package/dist/{observability.js → shell/observability.js} +159 -332
  103. package/dist/{onramp.js → shell/onramp.js} +11 -0
  104. package/dist/{operator-ux/format.js → shell/operator-ux-text.js} +250 -239
  105. package/dist/shell/operator-ux.js +431 -0
  106. package/dist/shell/orchestrator.js +231 -0
  107. package/dist/shell/pipeline-cli.js +667 -0
  108. package/dist/shell/pipeline.js +217 -0
  109. package/dist/shell/reclamation-io.js +1366 -0
  110. package/dist/shell/registry-cli.js +344 -0
  111. package/dist/{remote-source.js → shell/remote-source.js} +2 -2
  112. package/dist/shell/report-cli.js +101 -0
  113. package/dist/shell/report-view-cli.js +117 -0
  114. package/dist/{orchestrator → shell}/report.js +289 -282
  115. package/dist/shell/reporter.js +62 -0
  116. package/dist/shell/run-export-cli.js +106 -0
  117. package/dist/shell/run-export.js +680 -0
  118. package/dist/shell/run-registry-io.js +1014 -0
  119. package/dist/shell/run-store.js +164 -0
  120. package/dist/{sandbox-profile.js → shell/sandbox-profile.js} +134 -95
  121. package/dist/{scheduler.js → shell/scheduler-io.js} +257 -48
  122. package/dist/shell/scheduling-io.js +321 -0
  123. package/dist/shell/state-cli.js +181 -0
  124. package/dist/shell/state-explosion-cli.js +197 -0
  125. package/dist/shell/telemetry-cli.js +85 -0
  126. package/dist/{telemetry-demo.js → shell/telemetry-demo.js} +149 -119
  127. package/dist/shell/telemetry-ledger-io.js +132 -0
  128. package/dist/{term.js → shell/term.js} +36 -46
  129. package/dist/shell/topology-io.js +361 -0
  130. package/dist/shell/trust-audit.js +472 -0
  131. package/dist/{multi-agent-trust.js → shell/trust-policy-io.js} +67 -205
  132. package/dist/shell/verifier.js +48 -0
  133. package/dist/shell/workbench-host.js +258 -0
  134. package/dist/shell/workbench-text.js +18 -0
  135. package/dist/shell/workbench.js +170 -0
  136. package/dist/shell/worker-cli.js +124 -0
  137. package/dist/shell/worker-isolation.js +852 -0
  138. package/dist/shell/workflow-app-loader.js +650 -0
  139. package/docs/agent-delegation-drive.7.md +4 -0
  140. package/docs/cli-mcp-parity.7.md +282 -219
  141. package/docs/contract-migration-tooling.7.md +4 -0
  142. package/docs/control-plane-scheduling.7.md +4 -0
  143. package/docs/durable-state-and-locking.7.md +4 -0
  144. package/docs/evidence-adoption-reasoning-chain.7.md +4 -0
  145. package/docs/execution-backends.7.md +4 -0
  146. package/docs/multi-agent-cli-mcp-surface.7.md +4 -0
  147. package/docs/multi-agent-eval-replay-harness.7.md +4 -0
  148. package/docs/multi-agent-operator-ux.7.md +4 -0
  149. package/docs/node-snapshot-diff-replay.7.md +4 -0
  150. package/docs/observability-cost-accounting.7.md +6 -1
  151. package/docs/project-index.md +137 -71
  152. package/docs/real-execution-backends.7.md +4 -0
  153. package/docs/release-and-migration.7.md +4 -0
  154. package/docs/release-tooling.7.md +4 -0
  155. package/docs/remote-source-review.7.md +4 -4
  156. package/docs/report-verifiable-bundle.7.md +1 -1
  157. package/docs/run-registry-control-plane.7.md +4 -0
  158. package/docs/run-retention-reclamation.7.md +25 -0
  159. package/docs/security-trust-hardening.7.md +3 -1
  160. package/docs/state-explosion-management.7.md +4 -0
  161. package/docs/team-collaboration.7.md +4 -0
  162. package/docs/web-desktop-workbench.7.md +15 -1
  163. package/manifest/plugin.manifest.json +1 -1
  164. package/manifest/source-context-profiles.json +9 -13
  165. package/package.json +1 -1
  166. package/scripts/agents/agent-adapter-core.js +22 -2
  167. package/scripts/agents/claude-p-agent.js +8 -3
  168. package/scripts/agents/cw-attest-wrap.js +2 -2
  169. package/scripts/agents/gemini-agent.js +5 -1
  170. package/scripts/agents/opencode-agent.js +5 -1
  171. package/scripts/bump-version.js +4 -3
  172. package/scripts/canonical-apps.js +4 -4
  173. package/scripts/dogfood-architecture-review.js +2 -3
  174. package/scripts/dogfood-release.js +3 -3
  175. package/scripts/gen-parity-doc.js +15 -2
  176. package/scripts/golden-path.js +4 -4
  177. package/scripts/onramp-check.js +1 -1
  178. package/scripts/parity-check.js +38 -21
  179. package/scripts/release-flow.js +2 -2
  180. package/scripts/sync-project-index.js +51 -27
  181. package/scripts/validate-run-state-schema.js +2 -2
  182. package/scripts/version-sync-check.js +30 -30
  183. package/dist/candidate-scoring.js +0 -729
  184. package/dist/capability-core.js +0 -1189
  185. package/dist/capability-registry.js +0 -885
  186. package/dist/cli/command-surface.js +0 -494
  187. package/dist/cli/format.js +0 -56
  188. package/dist/cli/handlers/audit.js +0 -82
  189. package/dist/cli/handlers/blackboard.js +0 -81
  190. package/dist/cli/handlers/candidate.js +0 -40
  191. package/dist/cli/handlers/clones.js +0 -34
  192. package/dist/cli/handlers/collaboration.js +0 -61
  193. package/dist/cli/handlers/eval.js +0 -40
  194. package/dist/cli/handlers/ledger.js +0 -169
  195. package/dist/cli/handlers/maintenance.js +0 -107
  196. package/dist/cli/handlers/multi-agent.js +0 -165
  197. package/dist/cli/handlers/node.js +0 -41
  198. package/dist/cli/handlers/operational.js +0 -155
  199. package/dist/cli/handlers/operator.js +0 -146
  200. package/dist/cli/handlers/registry.js +0 -68
  201. package/dist/cli/handlers/run.js +0 -153
  202. package/dist/cli/handlers/scheduling.js +0 -132
  203. package/dist/cli/handlers/workbench.js +0 -41
  204. package/dist/cli/handlers/worker.js +0 -45
  205. package/dist/cli/run-summary.js +0 -45
  206. package/dist/clones.js +0 -162
  207. package/dist/collaboration.js +0 -726
  208. package/dist/commit.js +0 -592
  209. package/dist/compare.js +0 -18
  210. package/dist/coordinator/classify.js +0 -45
  211. package/dist/coordinator/paths.js +0 -42
  212. package/dist/coordinator/util.js +0 -126
  213. package/dist/coordinator.js +0 -990
  214. package/dist/daemon.js +0 -44
  215. package/dist/dispatch.js +0 -250
  216. package/dist/doctor.js +0 -212
  217. package/dist/drive.js +0 -864
  218. package/dist/error-feedback.js +0 -419
  219. package/dist/evidence-grounding.js +0 -184
  220. package/dist/execution-backend/agent.js +0 -354
  221. package/dist/execution-backend/probes.js +0 -112
  222. package/dist/execution-backend/util.js +0 -47
  223. package/dist/execution-backend.js +0 -961
  224. package/dist/gates.js +0 -48
  225. package/dist/harness.js +0 -61
  226. package/dist/ledger.js +0 -313
  227. package/dist/loop-expansion.js +0 -60
  228. package/dist/mcp/tool-call.js +0 -470
  229. package/dist/mcp/tool-definitions.js +0 -1066
  230. package/dist/mcp-surface.js +0 -30
  231. package/dist/multi-agent/graph.js +0 -84
  232. package/dist/multi-agent/helpers.js +0 -141
  233. package/dist/multi-agent/ids.js +0 -20
  234. package/dist/multi-agent/paths.js +0 -22
  235. package/dist/multi-agent-eval/normalize.js +0 -51
  236. package/dist/multi-agent-eval.js +0 -678
  237. package/dist/multi-agent-host.js +0 -777
  238. package/dist/multi-agent.js +0 -984
  239. package/dist/node-projection.js +0 -59
  240. package/dist/node-snapshot.js +0 -260
  241. package/dist/operator-ux.js +0 -631
  242. package/dist/orchestrator/app-operations.js +0 -211
  243. package/dist/orchestrator/audit-operations.js +0 -182
  244. package/dist/orchestrator/candidate-operations.js +0 -117
  245. package/dist/orchestrator/cli-options.js +0 -294
  246. package/dist/orchestrator/collaboration-operations.js +0 -86
  247. package/dist/orchestrator/feedback-operations.js +0 -81
  248. package/dist/orchestrator/host-operations.js +0 -78
  249. package/dist/orchestrator/lifecycle-operations.js +0 -650
  250. package/dist/orchestrator/migration-operations.js +0 -44
  251. package/dist/orchestrator/multi-agent-operations.js +0 -362
  252. package/dist/orchestrator/topology-operations.js +0 -84
  253. package/dist/orchestrator.js +0 -925
  254. package/dist/pipeline-runner.js +0 -285
  255. package/dist/reclamation/hash.js +0 -72
  256. package/dist/reclamation.js +0 -812
  257. package/dist/reporter.js +0 -67
  258. package/dist/run-export.js +0 -815
  259. package/dist/run-registry/derive.js +0 -175
  260. package/dist/run-registry/format.js +0 -124
  261. package/dist/run-registry/gc.js +0 -251
  262. package/dist/run-registry/policy.js +0 -16
  263. package/dist/run-registry/queue.js +0 -115
  264. package/dist/run-registry.js +0 -850
  265. package/dist/run-state-schema.js +0 -68
  266. package/dist/scheduling.js +0 -184
  267. package/dist/state-explosion.js +0 -1014
  268. package/dist/state.js +0 -367
  269. package/dist/telemetry-ledger.js +0 -196
  270. package/dist/topology.js +0 -565
  271. package/dist/triggers.js +0 -184
  272. package/dist/trust-audit.js +0 -644
  273. package/dist/types/blackboard.js +0 -2
  274. package/dist/types/candidate.js +0 -2
  275. package/dist/types/collaboration.js +0 -2
  276. package/dist/types/core.js +0 -2
  277. package/dist/types/drive.js +0 -10
  278. package/dist/types/error-feedback.js +0 -2
  279. package/dist/types/evidence-reasoning.js +0 -2
  280. package/dist/types/execution-backend.js +0 -2
  281. package/dist/types/multi-agent.js +0 -2
  282. package/dist/types/observability.js +0 -2
  283. package/dist/types/pipeline.js +0 -2
  284. package/dist/types/reclamation.js +0 -8
  285. package/dist/types/report-bundle.js +0 -6
  286. package/dist/types/result.js +0 -2
  287. package/dist/types/run-registry.js +0 -2
  288. package/dist/types/run.js +0 -2
  289. package/dist/types/sandbox.js +0 -2
  290. package/dist/types/schedule.js +0 -2
  291. package/dist/types/state-node.js +0 -2
  292. package/dist/types/topology.js +0 -2
  293. package/dist/types/trust.js +0 -2
  294. package/dist/types/workbench.js +0 -2
  295. package/dist/types/worker.js +0 -2
  296. package/dist/types/workflow-app.js +0 -2
  297. package/dist/types.js +0 -44
  298. package/dist/util/fingerprint.js +0 -19
  299. package/dist/util/fingerprint.test.js +0 -27
  300. package/dist/verifier.js +0 -78
  301. package/dist/version.js +0 -8
  302. package/dist/workbench-host.js +0 -192
  303. package/dist/workbench.js +0 -192
  304. package/dist/worker-accept/acceptance.js +0 -114
  305. package/dist/worker-accept/blackboard-fanout.js +0 -80
  306. package/dist/worker-accept/blackboard-linkage.js +0 -19
  307. package/dist/worker-accept/context.js +0 -2
  308. package/dist/worker-accept/telemetry-ledger.js +0 -126
  309. package/dist/worker-accept/validation.js +0 -77
  310. package/dist/worker-accept/verifier-completion.js +0 -73
  311. package/dist/worker-isolation/helpers.js +0 -51
  312. package/dist/worker-isolation/paths.js +0 -46
  313. package/dist/worker-isolation.js +0 -656
  314. package/dist/workflow-api.js +0 -131
  315. /package/dist/{types → core/types}/boundary.js +0 -0
@@ -1,4 +1,13 @@
1
1
  "use strict";
2
+ // shell/evidence-reasoning.ts — the Evidence Adoption Reasoning Chain
3
+ // (v0.1.26). Faithful port of the old flat build's src/evidence-reasoning.ts
4
+ // (+ its src/types/evidence-reasoning.ts types), adapted to v2's core/shell
5
+ // split. DERIVES the "why" behind each evidence adoption decision from
6
+ // existing run state; never mutates source records, never fabricates a
7
+ // rationale (an untraceable adoption renders `unexplained`).
8
+ //
9
+ // Evidence: SPEC/multi-agent.md "Evidence adoption reasoning";
10
+ // plugins/cool-workflow/src/evidence-reasoning.ts (byte-behavior source).
2
11
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
13
  };
@@ -15,30 +24,31 @@ exports.formatEvidenceReasoningReport = formatEvidenceReasoningReport;
15
24
  const node_crypto_1 = __importDefault(require("node:crypto"));
16
25
  const node_fs_1 = __importDefault(require("node:fs"));
17
26
  const node_path_1 = __importDefault(require("node:path"));
18
- const state_1 = require("./state");
27
+ const validation_1 = require("../core/state/validation");
28
+ const fs_atomic_1 = require("./fs-atomic");
19
29
  const multi_agent_operator_ux_1 = require("./multi-agent-operator-ux");
20
30
  const trust_audit_1 = require("./trust-audit");
21
- const multi_agent_trust_1 = require("./multi-agent-trust");
22
- const validation_1 = require("./validation");
23
- // ---------------------------------------------------------------------------
24
- // Evidence Adoption Reasoning Chain (v0.1.26)
25
- //
26
- // This module DERIVES the "why" behind each evidence adoption decision from
27
- // existing run state. It is the mechanism half of the FreeBSD mechanism/policy
28
- // split: it captures, fingerprints and renders the recorded rationale; it never
29
- // decides whether a rationale is *sufficient* (that stays with the verifier /
30
- // role policy). It never mutates source-of-truth records and never fabricates a
31
- // rationale — an adoption whose reason cannot be traced renders `unexplained`.
32
- //
33
- // The persisted view mirrors the v0.1.25 state-explosion summaries: a derived,
34
- // versioned, provenance-backed index under .cw/runs/<id>/reasoning/ with a
35
- // sourceFingerprint and valid|stale|absent freshness, refreshable, never
36
- // authoritative over raw state.
37
- // ---------------------------------------------------------------------------
31
+ const trust_policy_1 = require("../core/multi-agent/trust-policy");
38
32
  exports.EVIDENCE_REASONING_SCHEMA_VERSION = 1;
39
- // ---------------------------------------------------------------------------
40
- // Derivation
41
- // ---------------------------------------------------------------------------
33
+ function candidatesOf(run) {
34
+ return (run.candidates || []);
35
+ }
36
+ function selectionsOf(run) {
37
+ return (run.candidateSelections || []);
38
+ }
39
+ function commitsOf(run) {
40
+ return (run.commits || []);
41
+ }
42
+ function decisionsOf(run) {
43
+ return (run.blackboard?.decisions || []);
44
+ }
45
+ function faninsOf(run) {
46
+ return (run.multiAgent?.fanins || []);
47
+ }
48
+ function rolesOf(run) {
49
+ return (run.multiAgent?.roles || []);
50
+ }
51
+ // ---- Derivation ------------------------------------------------------------
42
52
  function buildEvidenceReasoningReport(run, options = {}) {
43
53
  const operator = (0, multi_agent_operator_ux_1.summarizeMultiAgentOperator)(run);
44
54
  const scores = readAllScores(run);
@@ -62,27 +72,18 @@ function buildEvidenceReasoningReport(run, options = {}) {
62
72
  schemaVersion: exports.EVIDENCE_REASONING_SCHEMA_VERSION,
63
73
  runId: run.id,
64
74
  generatedAt: new Date().toISOString(),
65
- freshness: {
66
- status,
67
- persistedFingerprint: persisted?.sourceFingerprint,
68
- currentFingerprint
69
- },
75
+ freshness: { status, persistedFingerprint: persisted?.sourceFingerprint, currentFingerprint },
70
76
  sourceFingerprint: currentFingerprint,
71
77
  totals,
72
78
  chains,
73
- nextAction
79
+ nextAction,
74
80
  };
75
81
  }
76
82
  function buildChain(run, evidence, context) {
77
83
  const steps = [];
78
84
  const sourceRecordIds = new Set();
79
- const note = (id) => {
80
- if (id)
81
- sourceRecordIds.add(id);
82
- };
83
- // Walk the adopters/rejecters recorded on the evidence row and classify each
84
- // by the gate it represents. We never invent gates: a step exists only when a
85
- // real adopter/decision record references this evidence.
85
+ const note = (id) => { if (id)
86
+ sourceRecordIds.add(id); };
86
87
  const adopters = [...evidence.adoptedBy, ...evidence.rejectedBy];
87
88
  for (const scoreId of evidence.scoreIds) {
88
89
  const score = context.scores.get(scoreId);
@@ -90,22 +91,20 @@ function buildChain(run, evidence, context) {
90
91
  steps.push(buildScoreStep(run, evidence, score, scoreId, context));
91
92
  }
92
93
  for (const selectionId of evidence.selectionIds) {
93
- const selection = (run.candidateSelections || []).find((entry) => entry.id === selectionId);
94
+ const selection = selectionsOf(run).find((entry) => entry.id === selectionId);
94
95
  note(selectionId);
95
96
  steps.push(buildSelectionStep(run, evidence, selection, selectionId, context));
96
97
  }
97
98
  for (const commitId of evidence.commitIds) {
98
- const commit = (run.commits || []).find((entry) => entry.id === commitId);
99
+ const commit = commitsOf(run).find((entry) => entry.id === commitId);
99
100
  note(commitId);
100
- const commitStep = buildCommitStep(run, evidence, commit, commitId);
101
- steps.push(commitStep);
102
- const verifierStep = buildVerifierStep(run, evidence, commit, commitId);
101
+ steps.push(buildCommitStep(run, evidence, commit, commitId));
102
+ const verifierStep = buildVerifierStep(evidence, commit, commitId);
103
103
  if (verifierStep)
104
104
  steps.push(verifierStep);
105
105
  }
106
- // Fanin and coordinator-decision adopters (blackboard -> fanin consolidation).
107
- const faninIds = new Set((run.multiAgent?.fanins || []).map((entry) => entry.id));
108
- const decisions = run.blackboard?.decisions || [];
106
+ const faninIds = new Set(faninsOf(run).map((entry) => entry.id));
107
+ const decisions = decisionsOf(run);
109
108
  for (const adopter of unique(adopters)) {
110
109
  if (faninIds.has(adopter)) {
111
110
  note(adopter);
@@ -115,13 +114,10 @@ function buildChain(run, evidence, context) {
115
114
  const decision = decisions.find((entry) => entry.id === adopter);
116
115
  if (decision) {
117
116
  note(decision.id);
118
- steps.push(buildDecisionStep(run, evidence, decision));
117
+ steps.push(buildDecisionStep(evidence, decision));
119
118
  }
120
119
  }
121
120
  }
122
- // An adopted/rejected item that produced no decision-gate step has a known
123
- // WHAT but no recorded WHY: fail closed with an explicit unexplained step so
124
- // the gap is visible rather than silently treated as adopted.
125
121
  if (!steps.length && isDecisionStatus(evidence.status)) {
126
122
  steps.push(buildUnexplainedStep(evidence));
127
123
  }
@@ -142,17 +138,12 @@ function buildChain(run, evidence, context) {
142
138
  sourceId: evidence.sourceId,
143
139
  steps,
144
140
  sourceRecordIds: [...sourceRecordIds].filter(Boolean).sort(),
145
- unexplainedReasons
141
+ unexplainedReasons,
146
142
  };
147
143
  }
148
- // ---------------------------------------------------------------------------
149
- // Per-gate steps
150
- // ---------------------------------------------------------------------------
151
144
  function buildScoreStep(run, evidence, score, scoreId, context) {
152
145
  const decision = score?.verdict === "fail" ? "rejected" : "adopted";
153
- const judge = context.auditEvents.find((event) => event.kind === "judge.rationale" &&
154
- event.decision === "accepted" &&
155
- (event.scoreId === scoreId || (!event.scoreId && event.candidateId && evidence.candidateIds.includes(event.candidateId))));
146
+ const judge = context.auditEvents.find((event) => event.kind === "judge.rationale" && event.decision === "accepted" && (event.scoreId === scoreId || (!event.scoreId && event.candidateId && evidence.candidateIds.includes(event.candidateId))));
156
147
  const rationaleText = score?.notes || judgeRationaleText(judge);
157
148
  const rationale = rationaleText
158
149
  ? {
@@ -161,13 +152,9 @@ function buildScoreStep(run, evidence, score, scoreId, context) {
161
152
  sourceKind: score?.notes ? "score-notes" : "judge-rationale",
162
153
  sourceId: score?.notes ? scoreId : judge?.id,
163
154
  scoreCriteria: score?.criteria,
164
- scoreDelta: context.counterfactuals.bestRejectedNormalized !== undefined && score
165
- ? round(score.normalized - context.counterfactuals.bestRejectedNormalized)
166
- : undefined
155
+ scoreDelta: context.counterfactuals.bestRejectedNormalized !== undefined && score ? round(score.normalized - context.counterfactuals.bestRejectedNormalized) : undefined,
167
156
  }
168
157
  : unexplainedRationale();
169
- // AUTHORITY: the judge role that authored the rationale, when recorded; the
170
- // host that mechanically wrote the score is the fallback actor.
171
158
  const auditIds = unique([...collectAuditIds(score), ...(judge ? [judge.id] : [])]);
172
159
  return {
173
160
  gate: "candidate-score",
@@ -175,77 +162,44 @@ function buildScoreStep(run, evidence, score, scoreId, context) {
175
162
  basis: basisFor(evidence, { auditEventIds: auditIds, evidenceRefs: scoreEvidenceRefs(score) }),
176
163
  authority: roleAuthority(run, judge?.agentRoleId || score?.scorer, judge ? judge.decision === "accepted" : undefined),
177
164
  rationale,
178
- counterfactuals: decision === "adopted" ? context.counterfactuals.forScoreGate : []
165
+ counterfactuals: decision === "adopted" ? context.counterfactuals.forScoreGate : [],
179
166
  };
180
167
  }
181
168
  function buildSelectionStep(run, evidence, selection, selectionId, context) {
182
169
  const rationaleText = selection?.reason;
183
170
  const acceptance = selection?.acceptanceRationale;
184
171
  const rationale = rationaleText
185
- ? {
186
- status: "explained",
187
- text: truncate(rationaleText),
188
- sourceKind: "selection-reason",
189
- sourceId: selectionId,
190
- scoreCriteria: acceptance?.scoreCriteria,
191
- judgeRationaleIds: acceptance?.judgeRationaleIds,
192
- panelDecisionId: acceptance?.panelDecisionId
193
- }
172
+ ? { status: "explained", text: truncate(rationaleText), sourceKind: "selection-reason", sourceId: selectionId, scoreCriteria: acceptance?.scoreCriteria, judgeRationaleIds: acceptance?.judgeRationaleIds, panelDecisionId: acceptance?.panelDecisionId }
194
173
  : acceptance
195
- ? {
196
- status: "explained",
197
- text: `commit gate ${acceptance.commitGateResult || "recorded"} with ${acceptance.evidenceCount} evidence ref(s)`,
198
- sourceKind: "acceptance-rationale",
199
- sourceId: selectionId,
200
- scoreCriteria: acceptance.scoreCriteria,
201
- judgeRationaleIds: acceptance.judgeRationaleIds,
202
- panelDecisionId: acceptance.panelDecisionId
203
- }
174
+ ? { status: "explained", text: `commit gate ${acceptance.commitGateResult || "recorded"} with ${acceptance.evidenceCount} evidence ref(s)`, sourceKind: "acceptance-rationale", sourceId: selectionId, scoreCriteria: acceptance.scoreCriteria, judgeRationaleIds: acceptance.judgeRationaleIds, panelDecisionId: acceptance.panelDecisionId }
204
175
  : unexplainedRationale();
205
- // AUTHORITY: the chair role recorded as the author of the candidate-synthesis
206
- // coordinator decision for this selection; the host is the fallback actor.
207
- const synthesis = (run.blackboard?.decisions || []).find((entry) => entry.kind === "candidate-synthesis" && entry.subjectIds.includes(selectionId) && entry.author?.kind === "role");
176
+ const synthesis = decisionsOf(run).find((entry) => entry.kind === "candidate-synthesis" && (entry.subjectIds || []).includes(selectionId) && entry.author?.kind === "role");
208
177
  return {
209
178
  gate: "selection",
210
179
  decision: "adopted",
211
- basis: basisFor(evidence, {
212
- auditEventIds: acceptance?.auditEventIds || [],
213
- evidenceRefs: (selection?.evidence || []).map(evidenceRef).filter(Boolean)
214
- }),
180
+ basis: basisFor(evidence, { auditEventIds: acceptance?.auditEventIds || [], evidenceRefs: (selection?.evidence || []).map(evidenceRef).filter(Boolean) }),
215
181
  authority: roleAuthority(run, synthesis?.author?.id || selection?.selectedBy, true),
216
182
  rationale,
217
- counterfactuals: context.counterfactuals.forSelectionGate
183
+ counterfactuals: context.counterfactuals.forSelectionGate,
218
184
  };
219
185
  }
220
- function buildCommitStep(run, evidence, commit, commitId) {
186
+ function buildCommitStep(_run, evidence, commit, commitId) {
221
187
  const decision = commit?.verifierGated ? "adopted" : "pending";
222
188
  const rationale = commit?.reason
223
- ? {
224
- status: "explained",
225
- text: truncate(commit.reason),
226
- sourceKind: "commit-reason",
227
- sourceId: commitId
228
- }
189
+ ? { status: "explained", text: truncate(commit.reason), sourceKind: "commit-reason", sourceId: commitId }
229
190
  : decision === "adopted"
230
191
  ? unexplainedRationale()
231
192
  : { status: "not-applicable" };
232
193
  return {
233
194
  gate: "commit",
234
195
  decision,
235
- basis: basisFor(evidence, {
236
- auditEventIds: commit?.acceptanceRationale?.auditEventIds || [],
237
- evidenceRefs: (commit?.evidence || []).map(evidenceRef).filter(Boolean)
238
- }),
239
- authority: {
240
- actor: commitId,
241
- actorKind: "runtime",
242
- allowed: commit?.verifierGated
243
- },
196
+ basis: basisFor(evidence, { auditEventIds: commit?.acceptanceRationale?.auditEventIds || [], evidenceRefs: (commit?.evidence || []).map(evidenceRef).filter(Boolean) }),
197
+ authority: { actor: commitId, actorKind: "runtime", allowed: commit?.verifierGated },
244
198
  rationale,
245
- counterfactuals: []
199
+ counterfactuals: [],
246
200
  };
247
201
  }
248
- function buildVerifierStep(run, evidence, commit, commitId) {
202
+ function buildVerifierStep(evidence, commit, commitId) {
249
203
  const verifierNodeId = commit?.verifierNodeId;
250
204
  if (!verifierNodeId)
251
205
  return undefined;
@@ -260,27 +214,22 @@ function buildVerifierStep(run, evidence, commit, commitId) {
260
214
  : commit?.verifierGated
261
215
  ? { status: "explained", text: "verifier-gated commit recorded", sourceKind: "commit-reason", sourceId: commitId }
262
216
  : { status: "not-applicable" },
263
- counterfactuals: []
217
+ counterfactuals: [],
264
218
  };
265
219
  }
266
220
  function buildFaninStep(run, evidence, faninId, decisions) {
267
- const fanin = (run.multiAgent?.fanins || []).find((entry) => entry.id === faninId);
268
- const readiness = decisions.find((entry) => entry.kind === "fanin-readiness" && entry.subjectIds.includes(faninId));
221
+ const fanin = faninsOf(run).find((entry) => entry.id === faninId);
222
+ const readiness = decisions.find((entry) => entry.kind === "fanin-readiness" && (entry.subjectIds || []).includes(faninId));
269
223
  const adopted = evidence.adoptedBy.includes(faninId);
270
224
  const decision = adopted ? "adopted" : "pending";
271
225
  let rationale;
272
226
  if (readiness?.reason) {
273
227
  rationale = { status: "explained", text: truncate(readiness.reason), sourceKind: "coordinator-decision", sourceId: readiness.id };
274
228
  }
275
- else if (fanin && fanin.verifierReady && coverageComplete(fanin, evidence)) {
276
- rationale = {
277
- status: "explained",
278
- text: `fanin ${faninId} ready: required evidence covered under "${fanin.strategy}" strategy`,
279
- sourceKind: "coordinator-decision",
280
- sourceId: faninId
281
- };
229
+ else if (fanin && fanin.verifierReady && coverageComplete(fanin)) {
230
+ rationale = { status: "explained", text: `fanin ${faninId} ready: required evidence covered under "${fanin.strategy}" strategy`, sourceKind: "coordinator-decision", sourceId: faninId };
282
231
  }
283
- else if (fanin && fanin.blockedReasons.length) {
232
+ else if (fanin && (fanin.blockedReasons || []).length) {
284
233
  rationale = { status: "explained", text: truncate(fanin.blockedReasons[0]), sourceKind: "coordinator-decision", sourceId: faninId };
285
234
  }
286
235
  else {
@@ -292,109 +241,66 @@ function buildFaninStep(run, evidence, faninId, decisions) {
292
241
  basis: basisFor(evidence, { auditEventIds: [], evidenceRefs: [] }),
293
242
  authority: { actor: faninId, actorKind: "coordinator", allowed: adopted },
294
243
  rationale,
295
- counterfactuals: []
244
+ counterfactuals: [],
296
245
  };
297
246
  }
298
- function buildDecisionStep(run, evidence, decision) {
299
- const status = mapDecisionOutcome(decision.outcome);
247
+ function buildDecisionStep(evidence, decision) {
248
+ const status = mapDecisionOutcome(decision.outcome || "");
300
249
  return {
301
250
  gate: "fanin",
302
251
  decision: status,
303
252
  basis: basisFor(evidence, { auditEventIds: [], evidenceRefs: decision.evidenceRefs || [] }),
304
- authority: {
305
- actor: decision.author?.id || decision.id,
306
- actorKind: authorKind(decision.author?.kind),
307
- allowed: decision.outcome === "accepted" || decision.outcome === "ready"
308
- },
253
+ authority: { actor: decision.author?.id || decision.id, actorKind: authorKind(decision.author?.kind), allowed: decision.outcome === "accepted" || decision.outcome === "ready" },
309
254
  rationale: decision.reason
310
255
  ? { status: "explained", text: truncate(decision.reason), sourceKind: "coordinator-decision", sourceId: decision.id }
311
256
  : isDecisionStatus(status)
312
257
  ? unexplainedRationale()
313
258
  : { status: "not-applicable" },
314
- counterfactuals: []
259
+ counterfactuals: [],
315
260
  };
316
261
  }
317
262
  function buildUnexplainedStep(evidence) {
318
- // The item is marked adopted/rejected but no decision record carries a reason.
319
- // Render the gap explicitly (fail closed) rather than inferring a rationale.
320
263
  return {
321
264
  gate: "fanin",
322
265
  decision: mapStatus(evidence.status),
323
266
  basis: basisFor(evidence, { auditEventIds: [], evidenceRefs: [] }),
324
- authority: {
325
- actor: evidence.adoptedBy[0] || evidence.rejectedBy[0] || evidence.sourceId,
326
- actorKind: actorKindForSource(evidence.sourceKind),
327
- allowed: evidence.status === "adopted"
328
- },
329
- rationale: evidence.reason
330
- ? { status: "explained", text: truncate(evidence.reason), sourceKind: "coordinator-decision", sourceId: evidence.sourceId }
331
- : unexplainedRationale(),
332
- counterfactuals: []
267
+ authority: { actor: evidence.adoptedBy[0] || evidence.rejectedBy[0] || evidence.sourceId, actorKind: actorKindForSource(evidence.sourceKind), allowed: evidence.status === "adopted" },
268
+ rationale: evidence.reason ? { status: "explained", text: truncate(evidence.reason), sourceKind: "coordinator-decision", sourceId: evidence.sourceId } : unexplainedRationale(),
269
+ counterfactuals: [],
333
270
  };
334
271
  }
335
- // ---------------------------------------------------------------------------
336
- // Counterfactuals
337
- // ---------------------------------------------------------------------------
338
272
  function deriveCounterfactuals(run, scores) {
339
273
  const forScoreGate = [];
340
274
  const forSelectionGate = [];
341
275
  let bestRejectedNormalized;
342
- for (const candidate of run.candidates || []) {
276
+ for (const candidate of candidatesOf(run)) {
343
277
  if (candidate.status === "rejected" || candidate.status === "failed") {
344
- forSelectionGate.push({
345
- ref: candidate.id,
346
- kind: "candidate",
347
- status: "rejected",
348
- reason: candidate.feedbackIds[0] ? `see feedback ${candidate.feedbackIds[0]}` : `candidate ${candidate.id} ${candidate.status}`
349
- });
278
+ forSelectionGate.push({ ref: candidate.id, kind: "candidate", status: "rejected", reason: (candidate.feedbackIds || [])[0] ? `see feedback ${candidate.feedbackIds[0]}` : `candidate ${candidate.id} ${candidate.status}` });
350
279
  for (const scoreId of candidate.scores || []) {
351
280
  const score = scores.get(scoreId);
352
- if (score && (bestRejectedNormalized === undefined || score.normalized > bestRejectedNormalized)) {
281
+ if (score && (bestRejectedNormalized === undefined || score.normalized > bestRejectedNormalized))
353
282
  bestRejectedNormalized = score.normalized;
354
- }
355
283
  }
356
284
  }
357
285
  }
358
286
  for (const [scoreId, score] of scores) {
359
287
  if (score.verdict === "fail") {
360
- forScoreGate.push({
361
- ref: scoreId,
362
- kind: "score",
363
- status: "rejected",
364
- reason: score.notes ? truncate(score.notes) : `score ${scoreId} verdict=fail (normalized ${round(score.normalized)})`
365
- });
288
+ forScoreGate.push({ ref: scoreId, kind: "score", status: "rejected", reason: score.notes ? truncate(score.notes) : `score ${scoreId} verdict=fail (normalized ${round(score.normalized)})` });
366
289
  }
367
290
  }
368
- for (const decision of run.blackboard?.decisions || []) {
291
+ for (const decision of decisionsOf(run)) {
369
292
  if (decision.outcome === "rejected" || decision.outcome === "superseded" || decision.outcome === "conflicting") {
370
- forSelectionGate.push({
371
- ref: decision.id,
372
- kind: "decision",
373
- status: mapDecisionOutcome(decision.outcome),
374
- reason: decision.reason ? truncate(decision.reason) : `decision ${decision.id} ${decision.outcome}`
375
- });
293
+ forSelectionGate.push({ ref: decision.id, kind: "decision", status: mapDecisionOutcome(decision.outcome), reason: decision.reason ? truncate(decision.reason) : `decision ${decision.id} ${decision.outcome}` });
376
294
  }
377
295
  }
378
- return {
379
- forScoreGate: forScoreGate.sort(byRef),
380
- forSelectionGate: forSelectionGate.sort(byRef),
381
- bestRejectedNormalized
382
- };
296
+ return { forScoreGate: forScoreGate.sort(byRef), forSelectionGate: forSelectionGate.sort(byRef), bestRejectedNormalized };
383
297
  }
384
- // ---------------------------------------------------------------------------
385
- // Compaction exemption
386
- //
387
- // FreeBSD tenet — ORTHOGONALITY & COMPOSABILITY: the reasoning chain composes
388
- // with the existing graph views and must survive compaction. A reasoning step is
389
- // on the critical path and must NEVER be collapsed into a synthetic summary
390
- // node. This returns the operator-graph node ids backing every decision-bearing
391
- // reasoning step of an adopted chain, so state-explosion can protect them.
392
- // ---------------------------------------------------------------------------
393
- function reasoningCriticalNodeIds(run, operator = (0, multi_agent_operator_ux_1.summarizeMultiAgentOperator)(run)) {
298
+ /** Critical-path node ids that state-explosion compaction must never collapse. */
299
+ function reasoningCriticalNodeIds(run) {
394
300
  const ids = new Set();
395
- const faninIds = new Set((run.multiAgent?.fanins || []).map((entry) => entry.id));
396
- const commitById = new Map((run.commits || []).map((commit) => [commit.id, commit]));
397
- for (const evidence of operator.evidence) {
301
+ const faninIds = new Set(faninsOf(run).map((entry) => entry.id));
302
+ const commitById = new Map(commitsOf(run).map((commit) => [commit.id, commit]));
303
+ for (const evidence of (0, multi_agent_operator_ux_1.summarizeMultiAgentOperator)(run).evidence) {
398
304
  if (evidence.status !== "adopted")
399
305
  continue;
400
306
  for (const id of evidence.candidateIds)
@@ -403,20 +309,15 @@ function reasoningCriticalNodeIds(run, operator = (0, multi_agent_operator_ux_1.
403
309
  ids.add(`${run.id}:score:${id}`);
404
310
  for (const id of evidence.selectionIds)
405
311
  ids.add(`${run.id}:selection:${id}`);
406
- for (const id of evidence.commitIds) {
407
- const commit = commitById.get(id);
408
- ids.add(commit?.stateNodeId || `${run.id}:commit:${id}`);
409
- }
410
- for (const adopter of evidence.adoptedBy) {
312
+ for (const id of evidence.commitIds)
313
+ ids.add(commitById.get(id)?.stateNodeId || `${run.id}:commit:${id}`);
314
+ for (const adopter of evidence.adoptedBy)
411
315
  if (faninIds.has(adopter))
412
316
  ids.add(`${run.id}:multi-agent:fanin:${adopter}`);
413
- }
414
317
  }
415
318
  return [...ids].sort();
416
319
  }
417
- // ---------------------------------------------------------------------------
418
- // Persistence + refresh (mirrors state-explosion summaries discipline)
419
- // ---------------------------------------------------------------------------
320
+ // ---- Persistence + refresh -------------------------------------------------
420
321
  function reasoningDir(run) {
421
322
  return node_path_1.default.join(run.paths.runDir, "reasoning");
422
323
  }
@@ -426,15 +327,9 @@ function refreshEvidenceReasoning(run) {
426
327
  node_fs_1.default.mkdirSync(dir, { recursive: true });
427
328
  const entries = [];
428
329
  for (const chain of report.chains) {
429
- const file = node_path_1.default.join(dir, `chain-${(0, state_1.safeFileName)(chain.id)}.json`);
430
- (0, state_1.writeJson)(file, chain);
431
- entries.push({
432
- id: chain.id,
433
- path: file,
434
- evidenceStatus: chain.evidenceStatus,
435
- rationaleStatus: chain.rationaleStatus,
436
- sourceFingerprint: fingerprintChains([chain])
437
- });
330
+ const file = node_path_1.default.join(dir, `chain-${(0, fs_atomic_1.safeFileName)(chain.id)}.json`);
331
+ (0, fs_atomic_1.writeJson)(file, chain);
332
+ entries.push({ id: chain.id, path: file, evidenceStatus: chain.evidenceStatus, rationaleStatus: chain.rationaleStatus, sourceFingerprint: fingerprintChains([chain]) });
438
333
  }
439
334
  const indexPath = node_path_1.default.join(dir, "index.json");
440
335
  const reportPath = node_path_1.default.join(dir, "report.json");
@@ -447,10 +342,10 @@ function refreshEvidenceReasoning(run) {
447
342
  totals: report.totals,
448
343
  entries: entries.sort((a, b) => a.id.localeCompare(b.id)),
449
344
  paths: { reasoningDir: dir, indexPath, reportPath },
450
- nextAction: `node scripts/cw.js multi-agent reasoning ${run.id}`
345
+ nextAction: `node scripts/cw.js multi-agent reasoning ${run.id}`,
451
346
  };
452
- (0, state_1.writeJson)(indexPath, index);
453
- (0, state_1.writeJson)(reportPath, { ...report, freshness: { ...report.freshness, status: "valid", persistedFingerprint: report.sourceFingerprint } });
347
+ (0, fs_atomic_1.writeJson)(indexPath, index);
348
+ (0, fs_atomic_1.writeJson)(reportPath, { ...report, freshness: { ...report.freshness, status: "valid", persistedFingerprint: report.sourceFingerprint } });
454
349
  return index;
455
350
  }
456
351
  function loadEvidenceReasoningIndex(run) {
@@ -475,10 +370,9 @@ function showEvidenceReasoning(run, options = {}) {
475
370
  const chains = report.chains.filter((chain) => chain.id === options.evidenceId || chain.ref === options.evidenceId);
476
371
  return { ...report, chains, totals: summarizeTotals(chains) };
477
372
  }
373
+ /** Derive the reasoning eval sections WITHOUT the persisted index (a replay
374
+ * run has no reasoning/index.json). Port of normalizeEvidenceReasoningForEval. */
478
375
  function normalizeEvidenceReasoningForEval(run) {
479
- // Derive without the persisted index: a replay run has no reasoning/index.json,
480
- // so persistence-derived freshness status would drift. Parity is asserted over
481
- // the derived content (the CLI smoke test covers valid|stale|absent behavior).
482
376
  const report = buildEvidenceReasoningReport(run);
483
377
  return {
484
378
  reasoningFreshness: [
@@ -489,8 +383,8 @@ function normalizeEvidenceReasoningForEval(run) {
489
383
  unexplained: report.totals.unexplained,
490
384
  notApplicable: report.totals.notApplicable,
491
385
  adopted: report.totals.adopted,
492
- rejected: report.totals.rejected
493
- })
386
+ rejected: report.totals.rejected,
387
+ }),
494
388
  ],
495
389
  reasoningChains: report.chains
496
390
  .map((chain) => JSON.stringify({
@@ -498,21 +392,16 @@ function normalizeEvidenceReasoningForEval(run) {
498
392
  evidenceStatus: chain.evidenceStatus,
499
393
  rationaleStatus: chain.rationaleStatus,
500
394
  gates: chain.steps.map((step) => `${step.gate}:${step.decision}:${step.rationale.status}`),
501
- counterfactuals: chain.steps.reduce((total, step) => total + step.counterfactuals.length, 0)
395
+ counterfactuals: chain.steps.reduce((total, step) => total + step.counterfactuals.length, 0),
502
396
  }))
503
397
  .sort(),
504
- reasoningUnexplained: report.chains
505
- .filter((chain) => chain.rationaleStatus === "unexplained")
506
- .map((chain) => stripRunId(run, chain.id))
507
- .sort()
398
+ reasoningUnexplained: report.chains.filter((chain) => chain.rationaleStatus === "unexplained").map((chain) => stripRunId(run, chain.id)).sort(),
508
399
  };
509
400
  }
510
401
  function stripRunId(run, id) {
511
402
  return id.startsWith(`${run.id}:`) ? id.slice(run.id.length + 1) : id;
512
403
  }
513
- // ---------------------------------------------------------------------------
514
- // Human formatting (stable, six-panel-compatible idiom)
515
- // ---------------------------------------------------------------------------
404
+ // ---- Human formatting ------------------------------------------------------
516
405
  function formatEvidenceReasoningReport(report) {
517
406
  const lines = [];
518
407
  lines.push(`Evidence Adoption Reasoning: ${report.runId}`);
@@ -521,9 +410,8 @@ function formatEvidenceReasoningReport(report) {
521
410
  lines.push("Adoption Rationale");
522
411
  lines.push(` chains=${report.totals.chains}; explained=${report.totals.explained}; unexplained=${report.totals.unexplained}; n/a=${report.totals.notApplicable}; adopted=${report.totals.adopted}; rejected=${report.totals.rejected}`);
523
412
  lines.push("");
524
- if (!report.chains.length) {
413
+ if (!report.chains.length)
525
414
  lines.push(" none");
526
- }
527
415
  for (const chain of report.chains.slice(0, 60)) {
528
416
  lines.push(` [${chain.evidenceStatus}/${chain.rationaleStatus}] ${chain.id} (${chain.ref || chain.sourceKind})`);
529
417
  for (const step of chain.steps) {
@@ -531,9 +419,8 @@ function formatEvidenceReasoningReport(report) {
531
419
  const why = step.rationale.status === "explained" ? step.rationale.text : `(${step.rationale.status})`;
532
420
  const policy = step.authority.policyRef ? ` policy=${step.authority.policyRef}` : "";
533
421
  lines.push(` - ${step.gate} [${step.decision}] by ${actor}${policy}: ${why}`);
534
- for (const cf of step.counterfactuals.slice(0, 4)) {
422
+ for (const cf of step.counterfactuals.slice(0, 4))
535
423
  lines.push(` x ${cf.kind} ${cf.ref} [${cf.status}]: ${cf.reason}`);
536
- }
537
424
  }
538
425
  for (const reason of chain.unexplainedReasons)
539
426
  lines.push(` ! ${reason}`);
@@ -545,41 +432,30 @@ function formatEvidenceReasoningReport(report) {
545
432
  lines.push(` ${report.nextAction}`);
546
433
  return lines.join("\n");
547
434
  }
548
- // ---------------------------------------------------------------------------
549
- // Helpers
550
- // ---------------------------------------------------------------------------
435
+ // ---- Helpers ---------------------------------------------------------------
551
436
  function basisFor(evidence, extra) {
552
437
  return {
553
438
  evidenceRefs: unique([evidence.locator || evidence.path || evidence.ref || evidence.id, ...extra.evidenceRefs].filter(Boolean)),
554
439
  provenanceSource: provenanceSourceFor(evidence),
555
440
  parentEvidenceIds: [],
556
- auditEventIds: unique(extra.auditEventIds.filter(Boolean))
441
+ auditEventIds: unique(extra.auditEventIds.filter(Boolean)),
557
442
  };
558
443
  }
559
444
  function provenanceSourceFor(evidence) {
560
445
  const value = evidence.provenanceSource;
561
- if (value === "cw-validated" || value === "host-attested" || value === "operator-recorded" || value === "runtime-derived") {
446
+ if (value === "cw-validated" || value === "host-attested" || value === "operator-recorded" || value === "runtime-derived")
562
447
  return value;
563
- }
564
448
  return undefined;
565
449
  }
566
- // AUTHORITY resolver: links the actor to its role policy when the actor is a
567
- // role; never fabricates a policy. allowed comes from the recorded decision.
568
450
  function roleAuthority(run, actor, allowed) {
569
- const role = (run.multiAgent?.roles || []).find((entry) => entry.id === actor);
570
- const policyRef = role ? (role.policy || (0, multi_agent_trust_1.policyForRole)(role)).policyRef : undefined;
571
- return {
572
- actor,
573
- actorKind: role ? "role" : actor === "multi-agent-host" ? "operator" : actorKindForActor(actor),
574
- policyRef,
575
- allowed
576
- };
451
+ const role = rolesOf(run).find((entry) => entry.id === actor);
452
+ const policyRef = role ? (role.policy || (0, trust_policy_1.policyForRole)(role)).policyRef : undefined;
453
+ return { actor, actorKind: role ? "role" : actor === "multi-agent-host" ? "operator" : actorKindForActor(actor), policyRef, allowed };
577
454
  }
578
455
  function rollupRationale(steps, evidenceStatus) {
579
456
  const decisionSteps = steps.filter((step) => isDecisionStatus(step.decision));
580
457
  if (!decisionSteps.length)
581
458
  return "not-applicable";
582
- // Explained only when EVERY decision-bearing step is explained — fail closed.
583
459
  if (decisionSteps.some((step) => step.rationale.status === "unexplained"))
584
460
  return "unexplained";
585
461
  if (decisionSteps.every((step) => step.rationale.status === "explained"))
@@ -588,11 +464,7 @@ function rollupRationale(steps, evidenceStatus) {
588
464
  }
589
465
  function summarizeTotals(chains) {
590
466
  const byStatus = {};
591
- let explained = 0;
592
- let unexplained = 0;
593
- let notApplicable = 0;
594
- let adopted = 0;
595
- let rejected = 0;
467
+ let explained = 0, unexplained = 0, notApplicable = 0, adopted = 0, rejected = 0;
596
468
  for (const chain of chains) {
597
469
  byStatus[chain.evidenceStatus] = (byStatus[chain.evidenceStatus] || 0) + 1;
598
470
  if (chain.rationaleStatus === "explained")
@@ -611,31 +483,28 @@ function summarizeTotals(chains) {
611
483
  function readAllScores(run) {
612
484
  const scores = new Map();
613
485
  const candidatesDir = run.paths.candidatesDir || node_path_1.default.join(run.paths.runDir, "candidates");
614
- for (const candidate of run.candidates || []) {
615
- const dir = node_path_1.default.join(candidatesDir, (0, state_1.safeFileName)(candidate.id), "scores");
486
+ for (const candidate of candidatesOf(run)) {
487
+ const dir = node_path_1.default.join(candidatesDir, (0, fs_atomic_1.safeFileName)(candidate.id), "scores");
616
488
  if (!node_fs_1.default.existsSync(dir))
617
489
  continue;
618
490
  for (const file of node_fs_1.default.readdirSync(dir).filter((entry) => entry.endsWith(".json")).sort()) {
619
491
  try {
620
- const score = (0, validation_1.tryValidateCandidateScore)(JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.join(dir, file), "utf8")));
621
- if (!score)
622
- continue; // Malformed/forged score shape: skip; the score gate fails closed.
623
- scores.set(score.id, score);
492
+ // Fail closed on a malformed/forged score shape via the shared core
493
+ // guard (full field-shape check, not just id/normalized).
494
+ const parsed = (0, validation_1.tryValidateCandidateScore)(JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.join(dir, file), "utf8")));
495
+ if (!parsed)
496
+ continue;
497
+ scores.set(parsed.id, parsed);
624
498
  }
625
499
  catch {
626
- // Unreadable score record: skip; the score gate will fail closed.
500
+ // Unreadable score record: skip; the score gate fails closed.
627
501
  }
628
502
  }
629
503
  }
630
504
  return scores;
631
505
  }
632
506
  function fingerprintChains(chains) {
633
- const lines = chains.map((chain) => JSON.stringify([
634
- chain.id,
635
- chain.evidenceStatus,
636
- chain.rationaleStatus,
637
- chain.steps.map((step) => [step.gate, step.decision, step.rationale.status, step.rationale.sourceId || ""])
638
- ]));
507
+ const lines = chains.map((chain) => JSON.stringify([chain.id, chain.evidenceStatus, chain.rationaleStatus, chain.steps.map((step) => [step.gate, step.decision, step.rationale.status, step.rationale.sourceId || ""])]));
639
508
  const hash = node_crypto_1.default.createHash("sha256");
640
509
  hash.update(JSON.stringify([...lines].sort()));
641
510
  return `sha256:${hash.digest("hex").slice(0, 32)}`;
@@ -649,10 +518,9 @@ function judgeRationaleText(event) {
649
518
  }
650
519
  function collectAuditIds(score) {
651
520
  const ids = [];
652
- for (const item of score?.evidence || []) {
521
+ for (const item of score?.evidence || [])
653
522
  for (const id of item.provenance?.auditEventIds || [])
654
523
  ids.push(id);
655
- }
656
524
  return ids;
657
525
  }
658
526
  function scoreEvidenceRefs(score) {
@@ -661,8 +529,9 @@ function scoreEvidenceRefs(score) {
661
529
  function evidenceRef(item) {
662
530
  return item.locator || item.path || item.summary || item.id || "";
663
531
  }
664
- function coverageComplete(fanin, _evidence) {
665
- return fanin.evidenceCoverage.length > 0 && fanin.evidenceCoverage.every((entry) => entry.complete);
532
+ function coverageComplete(fanin) {
533
+ const coverage = fanin.evidenceCoverage || [];
534
+ return coverage.length > 0 && coverage.every((entry) => entry.complete);
666
535
  }
667
536
  function mapStatus(status) {
668
537
  return status;
@@ -719,7 +588,7 @@ function actorKindForSource(sourceKind) {
719
588
  return "runtime";
720
589
  }
721
590
  function statusRank(status) {
722
- return { adopted: 0, pending: 1, missing: 2, conflicting: 3, rejected: 4, superseded: 5, unexplained: 6 }[status];
591
+ return { adopted: 0, pending: 1, missing: 2, conflicting: 3, rejected: 4, superseded: 5, unexplained: 6 }[status] ?? 9;
723
592
  }
724
593
  function truncate(value) {
725
594
  const single = value.replace(/\s+/g, " ").trim();