@tangle-network/agent-eval 0.135.4 → 0.137.0

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 (211) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +4 -2
  3. package/dist/{agent-profile-cell-OhuTee9n.js → agent-profile-cell-CbfBm2g6.js} +2 -2
  4. package/dist/{agent-profile-cell-OhuTee9n.js.map → agent-profile-cell-CbfBm2g6.js.map} +1 -1
  5. package/dist/{agent-profile-cell-CCm3l2v2.d.ts → agent-profile-cell-Cw0PVwDr.d.ts} +2 -2
  6. package/dist/{agent-profile-cell-CCm3l2v2.d.ts.map → agent-profile-cell-Cw0PVwDr.d.ts.map} +1 -1
  7. package/dist/analyst/index.d.ts +139 -17
  8. package/dist/analyst/index.d.ts.map +1 -1
  9. package/dist/analyst/index.js +606 -4
  10. package/dist/analyst/index.js.map +1 -1
  11. package/dist/{analyze-runs-jjCmF8pU.js → analyze-runs-BScZvqMV.js} +6 -6
  12. package/dist/{analyze-runs-jjCmF8pU.js.map → analyze-runs-BScZvqMV.js.map} +1 -1
  13. package/dist/{analyze-runs-Cda5Xkj1.d.ts → analyze-runs-PVtnfjvA.d.ts} +6 -6
  14. package/dist/{analyze-runs-Cda5Xkj1.d.ts.map → analyze-runs-PVtnfjvA.d.ts.map} +1 -1
  15. package/dist/{baseline-BUeFcgrn.js → baseline-C-GocmIW.js} +2 -2
  16. package/dist/{baseline-BUeFcgrn.js.map → baseline-C-GocmIW.js.map} +1 -1
  17. package/dist/benchmark-CHX4orG7.d.ts +184 -0
  18. package/dist/benchmark-CHX4orG7.d.ts.map +1 -0
  19. package/dist/benchmark-YDrpumqB.js +414 -0
  20. package/dist/benchmark-YDrpumqB.js.map +1 -0
  21. package/dist/benchmarks/index.d.ts +1 -1
  22. package/dist/benchmarks/index.js +1 -1
  23. package/dist/{benchmarks-Dfgm9ts5.js → benchmarks-DCLkQOmc.js} +3 -3
  24. package/dist/{benchmarks-Dfgm9ts5.js.map → benchmarks-DCLkQOmc.js.map} +1 -1
  25. package/dist/builder-eval/index.js +2 -2
  26. package/dist/campaign/index.d.ts +6 -6
  27. package/dist/campaign/index.js +3 -3
  28. package/dist/{campaign-Dz8uQnhC.js → campaign-lgObcHFC.js} +212 -78
  29. package/dist/campaign-lgObcHFC.js.map +1 -0
  30. package/dist/cli.js +1 -1
  31. package/dist/client-C8L6h6Wf.d.ts +202 -0
  32. package/dist/client-C8L6h6Wf.d.ts.map +1 -0
  33. package/dist/completion-verifier-DSyRNVzU.d.ts +240 -0
  34. package/dist/completion-verifier-DSyRNVzU.d.ts.map +1 -0
  35. package/dist/contract/index.d.ts +10 -9
  36. package/dist/contract/index.d.ts.map +1 -1
  37. package/dist/contract/index.js +10 -10
  38. package/dist/control.d.ts +2 -2
  39. package/dist/control.js +1 -1
  40. package/dist/{cost-ledger-DHAjwNj7.js → cost-ledger-D-5_-dhi.js} +2 -2
  41. package/dist/{cost-ledger-DHAjwNj7.js.map → cost-ledger-D-5_-dhi.js.map} +1 -1
  42. package/dist/{cost-ledger-fGS_u_O1.d.ts → cost-ledger-D2o6JOrL.d.ts} +2 -2
  43. package/dist/{cost-ledger-fGS_u_O1.d.ts.map → cost-ledger-D2o6JOrL.d.ts.map} +1 -1
  44. package/dist/{dataset-BvtnC8Dc.d.ts → dataset-v_Y5902-.d.ts} +2 -2
  45. package/dist/{dataset-BvtnC8Dc.d.ts.map → dataset-v_Y5902-.d.ts.map} +1 -1
  46. package/dist/{default-registry-CHmdy2An.js → default-registry-CLXbRt0f.js} +119 -38
  47. package/dist/default-registry-CLXbRt0f.js.map +1 -0
  48. package/dist/{default-registry-Brxr728w.d.ts → default-registry-Dc5D_Loc.d.ts} +63 -137
  49. package/dist/default-registry-Dc5D_Loc.d.ts.map +1 -0
  50. package/dist/{errors-8YnH8WlF.js → errors-D-LKuDhb.js} +8 -2
  51. package/dist/errors-D-LKuDhb.js.map +1 -0
  52. package/dist/{errors-CEk209JS.d.ts → errors-DkfjIDvD.d.ts} +9 -3
  53. package/dist/errors-DkfjIDvD.d.ts.map +1 -0
  54. package/dist/{eval-campaign-Cc8WZJ6b.js → eval-campaign-CHqfLnff.js} +6 -6
  55. package/dist/{eval-campaign-Cc8WZJ6b.js.map → eval-campaign-CHqfLnff.js.map} +1 -1
  56. package/dist/{extract-usage-DIQpN-ww.js → extract-usage-p-56bh8q.js} +3 -3
  57. package/dist/{extract-usage-DIQpN-ww.js.map → extract-usage-p-56bh8q.js.map} +1 -1
  58. package/dist/{feedback-trajectory-CVaeREXV.d.ts → feedback-trajectory-N_F0PwHz.d.ts} +90 -3
  59. package/dist/feedback-trajectory-N_F0PwHz.d.ts.map +1 -0
  60. package/dist/fuzz.d.ts +1 -1
  61. package/dist/fuzz.js +2 -2
  62. package/dist/hosted/index.d.ts +3 -2
  63. package/dist/hosted/index.d.ts.map +1 -1
  64. package/dist/index-BipJlj-C.d.ts +316 -0
  65. package/dist/index-BipJlj-C.d.ts.map +1 -0
  66. package/dist/{index-CQsJcqch.d.ts → index-BnP1QJUv.d.ts} +5 -5
  67. package/dist/{index-CQsJcqch.d.ts.map → index-BnP1QJUv.d.ts.map} +1 -1
  68. package/dist/{index-B4Fjfo5U.d.ts → index-C-Pr4OWg.d.ts} +88 -317
  69. package/dist/index-C-Pr4OWg.d.ts.map +1 -0
  70. package/dist/{index-DuhJaaiH.d.ts → index-DEb46kc6.d.ts} +2 -2
  71. package/dist/{index-DuhJaaiH.d.ts.map → index-DEb46kc6.d.ts.map} +1 -1
  72. package/dist/{index-C2fkZhv_.d.ts → index-DRNl6g_N.d.ts} +3 -3
  73. package/dist/{index-C2fkZhv_.d.ts.map → index-DRNl6g_N.d.ts.map} +1 -1
  74. package/dist/{index-AbhwHp0V.d.ts → index-U3RHOShi.d.ts} +2 -2
  75. package/dist/{index-AbhwHp0V.d.ts.map → index-U3RHOShi.d.ts.map} +1 -1
  76. package/dist/index.d.ts +29 -70
  77. package/dist/index.d.ts.map +1 -1
  78. package/dist/index.js +507 -33
  79. package/dist/index.js.map +1 -1
  80. package/dist/{client-DcvgkaZi.d.ts → insight-report-B9ooYH_g.d.ts} +5 -203
  81. package/dist/insight-report-B9ooYH_g.d.ts.map +1 -0
  82. package/dist/integrity-CCXTftiL.js +1360 -0
  83. package/dist/integrity-CCXTftiL.js.map +1 -0
  84. package/dist/{integrity-rmVhXWA7.d.ts → integrity-CKxosZ5Z.d.ts} +3 -3
  85. package/dist/{integrity-rmVhXWA7.d.ts.map → integrity-CKxosZ5Z.d.ts.map} +1 -1
  86. package/dist/{integrity-BzRbCHzi.js → integrity-fdt8XPAv.js} +2 -2
  87. package/dist/{integrity-BzRbCHzi.js.map → integrity-fdt8XPAv.js.map} +1 -1
  88. package/dist/ledger-core/index.d.ts +1 -1
  89. package/dist/ledger-core/index.js +1 -1
  90. package/dist/{ledger-core-DAKFKRzi.js → ledger-core-t6sItivm.js} +85 -85
  91. package/dist/{ledger-core-DAKFKRzi.js.map → ledger-core-t6sItivm.js.map} +1 -1
  92. package/dist/{llm-client-DHx8pzyJ.js → llm-client-DKB25jV8.js} +3 -3
  93. package/dist/{llm-client-DHx8pzyJ.js.map → llm-client-DKB25jV8.js.map} +1 -1
  94. package/dist/meta-eval/index.d.ts +2 -2
  95. package/dist/meta-eval/index.js +3 -3
  96. package/dist/{mint-DyRUc9k6.js → mint-Ctwk079K.js} +4 -4
  97. package/dist/{mint-DyRUc9k6.js.map → mint-Ctwk079K.js.map} +1 -1
  98. package/dist/multishot/index.d.ts +2 -2
  99. package/dist/openapi.json +1 -1
  100. package/dist/{paired-arms-BbFKrAU-.js → paired-arms-iZ08VFMN.js} +3 -3
  101. package/dist/{paired-arms-BbFKrAU-.js.map → paired-arms-iZ08VFMN.js.map} +1 -1
  102. package/dist/pipelines/index.js +2 -2
  103. package/dist/profile-cell.d.ts +1 -1
  104. package/dist/profile-cell.js +1 -1
  105. package/dist/{propose-review-control-SQ-n9-We.js → propose-review-control-DLXz4FCX.js} +2 -2
  106. package/dist/{propose-review-control-SQ-n9-We.js.map → propose-review-control-DLXz4FCX.js.map} +1 -1
  107. package/dist/registry-BdM7SuTr.d.ts +124 -0
  108. package/dist/registry-BdM7SuTr.d.ts.map +1 -0
  109. package/dist/{release-report-DooPguBc.js → release-report-B5XPBvAU.js} +4 -4
  110. package/dist/{release-report-DooPguBc.js.map → release-report-B5XPBvAU.js.map} +1 -1
  111. package/dist/{release-report-DpBxGGI1.d.ts → release-report-CofgVNZt.d.ts} +4 -4
  112. package/dist/{release-report-DpBxGGI1.d.ts.map → release-report-CofgVNZt.d.ts.map} +1 -1
  113. package/dist/{replay-C6wRg47C.js → replay-Bju0T8Ls.js} +248 -8
  114. package/dist/replay-Bju0T8Ls.js.map +1 -0
  115. package/dist/{replay-BRfMIs81.d.ts → replay-K8FaC0CB.d.ts} +227 -52
  116. package/dist/replay-K8FaC0CB.d.ts.map +1 -0
  117. package/dist/reporting.d.ts +4 -4
  118. package/dist/reporting.js +4 -4
  119. package/dist/{researcher-Doo95b50.d.ts → researcher-Da0Wj-bt.d.ts} +6 -7
  120. package/dist/researcher-Da0Wj-bt.d.ts.map +1 -0
  121. package/dist/{reward-hacking-D-QqXvg-.d.ts → reward-hacking-CQ3hTCO3.d.ts} +2 -2
  122. package/dist/{reward-hacking-D-QqXvg-.d.ts.map → reward-hacking-CQ3hTCO3.d.ts.map} +1 -1
  123. package/dist/{reward-hacking-a-kYs0-i.js → reward-hacking-GyN0kMd8.js} +3 -3
  124. package/dist/{reward-hacking-a-kYs0-i.js.map → reward-hacking-GyN0kMd8.js.map} +1 -1
  125. package/dist/rl.d.ts +6 -6
  126. package/dist/rl.js +9 -9
  127. package/dist/rollout/index.d.ts +1 -1
  128. package/dist/rollout/index.js +3 -3
  129. package/dist/{rollout-DLSUIWLu.js → rollout-DQFl0UXA.js} +2 -2
  130. package/dist/{rollout-DLSUIWLu.js.map → rollout-DQFl0UXA.js.map} +1 -1
  131. package/dist/{rubric-predictive-validity-BJf-8ejY.js → rubric-predictive-validity-BRR632r1.js} +2 -2
  132. package/dist/{rubric-predictive-validity-BJf-8ejY.js.map → rubric-predictive-validity-BRR632r1.js.map} +1 -1
  133. package/dist/{rubric-predictive-validity-C1dCLcvb.d.ts → rubric-predictive-validity-C4sztLR3.d.ts} +2 -2
  134. package/dist/{rubric-predictive-validity-C1dCLcvb.d.ts.map → rubric-predictive-validity-C4sztLR3.d.ts.map} +1 -1
  135. package/dist/{run-evidence-DokQtX0-.d.ts → run-evidence-BDIircdA.d.ts} +3 -3
  136. package/dist/{run-evidence-DokQtX0-.d.ts.map → run-evidence-BDIircdA.d.ts.map} +1 -1
  137. package/dist/{run-record-DcObtIGh.d.ts → run-record-BPCa2rQ8.d.ts} +4 -4
  138. package/dist/{run-record-DcObtIGh.d.ts.map → run-record-BPCa2rQ8.d.ts.map} +1 -1
  139. package/dist/{run-record-BIwU2wdV.js → run-record-vRgqWmJw.js} +3 -3
  140. package/dist/{run-record-BIwU2wdV.js.map → run-record-vRgqWmJw.js.map} +1 -1
  141. package/dist/{semantic-concept-judge-Btozx3Vc.js → semantic-concept-judge-Bz64IckK.js} +4 -4
  142. package/dist/{semantic-concept-judge-Btozx3Vc.js.map → semantic-concept-judge-Bz64IckK.js.map} +1 -1
  143. package/dist/{server-Bz3WQJs6.js → server-KjXZZUDX.js} +3 -3
  144. package/dist/{server-Bz3WQJs6.js.map → server-KjXZZUDX.js.map} +1 -1
  145. package/dist/{skill-usage-BDQVPIG1.d.ts → skill-usage-CFDLLlhF.d.ts} +25 -47
  146. package/dist/skill-usage-CFDLLlhF.d.ts.map +1 -0
  147. package/dist/{skillopt-optimization-method-CwSYkv35.d.ts → skillopt-optimization-method-BpbnlvAZ.d.ts} +11 -12
  148. package/dist/skillopt-optimization-method-BpbnlvAZ.d.ts.map +1 -0
  149. package/dist/{skillopt-optimization-method-0UmPD6aP.js → skillopt-optimization-method-f4o9sUT4.js} +7 -7
  150. package/dist/{skillopt-optimization-method-0UmPD6aP.js.map → skillopt-optimization-method-f4o9sUT4.js.map} +1 -1
  151. package/dist/{statistics-CnGCLLqc.js → statistics-ByxzSiOM.js} +2 -2
  152. package/dist/{statistics-CnGCLLqc.js.map → statistics-ByxzSiOM.js.map} +1 -1
  153. package/dist/{statistics-CKOqre5S.d.ts → statistics-_7P642CN.d.ts} +2 -2
  154. package/dist/{statistics-CKOqre5S.d.ts.map → statistics-_7P642CN.d.ts.map} +1 -1
  155. package/dist/{summary-report-BEk8OFLs.js → summary-report-9A5y7EsK.js} +4 -4
  156. package/dist/{summary-report-BEk8OFLs.js.map → summary-report-9A5y7EsK.js.map} +1 -1
  157. package/dist/{summary-report-CPMINBqs.d.ts → summary-report-DHipz9Kx.d.ts} +3 -3
  158. package/dist/{summary-report-CPMINBqs.d.ts.map → summary-report-DHipz9Kx.d.ts.map} +1 -1
  159. package/dist/supervisor-run/index.d.ts +3 -2
  160. package/dist/supervisor-run/index.js +3 -2
  161. package/dist/{supervisor-run-B7lUGoyZ.js → supervisor-run-B2EWUmQY.js} +221 -519
  162. package/dist/supervisor-run-B2EWUmQY.js.map +1 -0
  163. package/dist/{test-graded-scenario-BsqWLmPt.js → test-graded-scenario-JHcKQNpq.js} +2 -2
  164. package/dist/{test-graded-scenario-BsqWLmPt.js.map → test-graded-scenario-JHcKQNpq.js.map} +1 -1
  165. package/dist/tools-DZk2Jn64.js +1876 -0
  166. package/dist/tools-DZk2Jn64.js.map +1 -0
  167. package/dist/traces.d.ts +6 -7
  168. package/dist/traces.js +5 -6
  169. package/dist/{types-DVjczBM9.d.ts → types-CKswbJGO.d.ts} +260 -6
  170. package/dist/types-CKswbJGO.d.ts.map +1 -0
  171. package/dist/{types-DiWLru6Z.d.ts → types-CTGbIm57.d.ts} +5 -5
  172. package/dist/{types-DiWLru6Z.d.ts.map → types-CTGbIm57.d.ts.map} +1 -1
  173. package/dist/types-CTvKfr5F.d.ts +804 -0
  174. package/dist/types-CTvKfr5F.d.ts.map +1 -0
  175. package/dist/{index-C61Wi7yg.d.ts → types-Dea6tiVI.d.ts} +47 -235
  176. package/dist/types-Dea6tiVI.d.ts.map +1 -0
  177. package/dist/wire/index.d.ts +3 -3
  178. package/dist/wire/index.js +1 -1
  179. package/docs/feedback-trajectories.md +100 -1
  180. package/docs/trace-analysis.md +374 -58
  181. package/package.json +7 -3
  182. package/dist/analyst-BkTS3C58.d.ts +0 -89
  183. package/dist/analyst-BkTS3C58.d.ts.map +0 -1
  184. package/dist/analyst-j5je5J7c.js +0 -152
  185. package/dist/analyst-j5je5J7c.js.map +0 -1
  186. package/dist/campaign-Dz8uQnhC.js.map +0 -1
  187. package/dist/client-DcvgkaZi.d.ts.map +0 -1
  188. package/dist/default-registry-Brxr728w.d.ts.map +0 -1
  189. package/dist/default-registry-CHmdy2An.js.map +0 -1
  190. package/dist/errors-8YnH8WlF.js.map +0 -1
  191. package/dist/errors-CEk209JS.d.ts.map +0 -1
  192. package/dist/feedback-trajectory-CVaeREXV.d.ts.map +0 -1
  193. package/dist/index-B4Fjfo5U.d.ts.map +0 -1
  194. package/dist/index-C61Wi7yg.d.ts.map +0 -1
  195. package/dist/llm-client-BiK4HW0u.d.ts +0 -290
  196. package/dist/llm-client-BiK4HW0u.d.ts.map +0 -1
  197. package/dist/raw-provider-sink-BU29Sh8h.d.ts +0 -134
  198. package/dist/raw-provider-sink-BU29Sh8h.d.ts.map +0 -1
  199. package/dist/replay-BRfMIs81.d.ts.map +0 -1
  200. package/dist/replay-C6wRg47C.js.map +0 -1
  201. package/dist/researcher-Doo95b50.d.ts.map +0 -1
  202. package/dist/skill-usage-BDQVPIG1.d.ts.map +0 -1
  203. package/dist/skillopt-optimization-method-CwSYkv35.d.ts.map +0 -1
  204. package/dist/store-CxJry_cs.d.ts +0 -229
  205. package/dist/store-CxJry_cs.d.ts.map +0 -1
  206. package/dist/supervisor-run-B7lUGoyZ.js.map +0 -1
  207. package/dist/tools-D8yTtNSN.js +0 -1190
  208. package/dist/tools-D8yTtNSN.js.map +0 -1
  209. package/dist/types-Cc3qbqzj.d.ts +0 -387
  210. package/dist/types-Cc3qbqzj.d.ts.map +0 -1
  211. package/dist/types-DVjczBM9.d.ts.map +0 -1
@@ -0,0 +1,1360 @@
1
+ import { f as isRolloutLine, i as ROLLOUT_SCHEMA, m as validateRolloutLine } from "./schema-C6DW4ZHR.js";
2
+ import { c as unscreenedRewardFields } from "./reward-nw2xZGZG.js";
3
+ //#region src/supervisor-run/integrity-issues.ts
4
+ function evidence(path, value) {
5
+ return {
6
+ path,
7
+ value
8
+ };
9
+ }
10
+ function issue(init) {
11
+ return {
12
+ ...init,
13
+ evidence: init.evidence ?? []
14
+ };
15
+ }
16
+ //#endregion
17
+ //#region src/supervisor-run/integrity-steer.ts
18
+ function idListDetail(ids) {
19
+ const examples = ids.slice(0, 20).map((id) => JSON.stringify(id)).join(", ");
20
+ return ids.length <= 20 ? examples : `${examples}, and ${ids.length - 20} more`;
21
+ }
22
+ function steerIssues(joined, rootCompleted) {
23
+ const { facts } = joined;
24
+ const subject = facts.workerId ?? joined.spawn.id;
25
+ const prefix = `workers/${facts.sourceIndex}`;
26
+ const out = [];
27
+ if (!facts.inboxCaptured || !facts.eventsCaptured || facts.inboxInvalidRows > 0 || facts.eventsInvalidRows > 0) {
28
+ const missing = [...facts.inboxCaptured ? [] : ["inbox"], ...facts.eventsCaptured ? [] : ["events"]];
29
+ const malformed = [...facts.inboxInvalidRows > 0 ? [`inbox:${facts.inboxInvalidRows}`] : [], ...facts.eventsInvalidRows > 0 ? [`events:${facts.eventsInvalidRows}`] : []];
30
+ out.push(issue({
31
+ code: "worker-controls-unavailable",
32
+ area: "capture-integrity",
33
+ severity: "medium",
34
+ subject,
35
+ claim: "Steer delivery integrity is unavailable for a worker with missing control artifacts",
36
+ detail: missing.length > 0 ? `${JSON.stringify(subject)} is missing ${missing.join(" and ")}.` : `${JSON.stringify(subject)} has malformed control rows (${malformed.join(", ")}).`,
37
+ evidence: [
38
+ ...missing.map((artifact) => evidence(`${prefix}/${artifact}`, null)),
39
+ ...facts.inboxInvalidRows > 0 ? [evidence(`${prefix}/inbox/malformed-rows/count`, facts.inboxInvalidRows)] : [],
40
+ ...facts.eventsInvalidRows > 0 ? [evidence(`${prefix}/events/malformed-rows/count`, facts.eventsInvalidRows)] : []
41
+ ],
42
+ recommendedAction: "Retain both control artifacts and repair malformed rows before correlating requests.",
43
+ metadata: {
44
+ assessment: "unavailable",
45
+ missing_artifacts: missing,
46
+ malformed_rows: malformed
47
+ }
48
+ }));
49
+ return out;
50
+ }
51
+ const requestsById = /* @__PURE__ */ new Map();
52
+ const unidentifiedRequests = facts.steerRequests.filter((request) => request.requestId === null);
53
+ for (const request of facts.steerRequests) {
54
+ if (request.requestId === null) continue;
55
+ const rows = requestsById.get(request.requestId) ?? [];
56
+ rows.push(request);
57
+ requestsById.set(request.requestId, rows);
58
+ }
59
+ if (unidentifiedRequests.length > 0) out.push(issue({
60
+ code: "steer-request-id-unavailable",
61
+ area: "capture-integrity",
62
+ severity: "high",
63
+ subject,
64
+ claim: "Some queued steer requests cannot be correlated because their ids are missing",
65
+ detail: `${unidentifiedRequests.length} queued request(s) omit id.`,
66
+ evidence: unidentifiedRequests.slice(0, 20).map((request) => evidence(`${prefix}/inbox/${request.row}/id`, null)),
67
+ recommendedAction: "Assign one stable request id before appending a steer to the inbox.",
68
+ metadata: {
69
+ assessment: "unavailable",
70
+ unavailable_count: unidentifiedRequests.length
71
+ }
72
+ }));
73
+ const duplicateRequestIds = [...requestsById].filter(([, rows]) => rows.length > 1).map(([id]) => id);
74
+ if (duplicateRequestIds.length > 0) out.push(issue({
75
+ code: "duplicate-steer-request-id",
76
+ area: "control-integrity",
77
+ severity: "high",
78
+ subject,
79
+ claim: "A worker inbox contains duplicate steer request ids",
80
+ detail: `${duplicateRequestIds.length} duplicate id(s): ${idListDetail(duplicateRequestIds)}.`,
81
+ evidence: [evidence(`${prefix}/inbox/duplicate-request-ids`, duplicateRequestIds.slice(0, 20))],
82
+ recommendedAction: "Generate a unique request id for every queued steer."
83
+ }));
84
+ const acknowledgementsById = /* @__PURE__ */ new Map();
85
+ const unidentifiedAcks = facts.steerAcknowledgements.filter((ack) => ack.requestId === null);
86
+ for (const ack of facts.steerAcknowledgements) {
87
+ if (ack.requestId === null) continue;
88
+ const rows = acknowledgementsById.get(ack.requestId) ?? [];
89
+ rows.push(ack);
90
+ acknowledgementsById.set(ack.requestId, rows);
91
+ }
92
+ if (unidentifiedAcks.length > 0) out.push(issue({
93
+ code: "steer-ack-id-unavailable",
94
+ area: "capture-integrity",
95
+ severity: "high",
96
+ subject,
97
+ claim: "Some steer acknowledgements cannot be correlated because requestId is missing",
98
+ detail: `${unidentifiedAcks.length} down-leg event(s) omit requestId.`,
99
+ evidence: unidentifiedAcks.slice(0, 20).map((ack) => evidence(`${prefix}/events/${ack.row}/requestId`, null)),
100
+ recommendedAction: "Echo the queued request id on every worker acknowledgement.",
101
+ metadata: {
102
+ assessment: "unavailable",
103
+ unavailable_count: unidentifiedAcks.length
104
+ }
105
+ }));
106
+ const unknownAckIds = [...acknowledgementsById.keys()].filter((id) => !requestsById.has(id));
107
+ if (unknownAckIds.length > 0) out.push(issue({
108
+ code: "unknown-steer-ack",
109
+ area: "control-integrity",
110
+ severity: "high",
111
+ subject,
112
+ claim: "A worker acknowledged steer request ids absent from its captured inbox",
113
+ detail: `${unknownAckIds.length} unknown id(s): ${idListDetail(unknownAckIds)}.`,
114
+ evidence: [evidence(`${prefix}/events/unknown-request-ids`, unknownAckIds.slice(0, 20))],
115
+ recommendedAction: "Reject acknowledgements whose request id was not queued for this worker."
116
+ }));
117
+ const duplicateAckIds = [...acknowledgementsById].filter(([, rows]) => rows.length > 1).map(([id]) => id);
118
+ if (duplicateAckIds.length > 0) out.push(issue({
119
+ code: "duplicate-steer-ack",
120
+ area: "control-integrity",
121
+ severity: "high",
122
+ subject,
123
+ claim: "A worker emitted duplicate acknowledgements for steer request ids",
124
+ detail: `${duplicateAckIds.length} duplicated id(s): ${idListDetail(duplicateAckIds)}.`,
125
+ evidence: [evidence(`${prefix}/events/duplicate-request-ids`, duplicateAckIds.slice(0, 20))],
126
+ recommendedAction: "Make steer acknowledgement append idempotent by request id."
127
+ }));
128
+ const missingAckIds = [...requestsById.keys()].filter((id) => !acknowledgementsById.has(id));
129
+ if (rootCompleted && missingAckIds.length > 0) out.push(issue({
130
+ code: "missing-steer-ack",
131
+ area: "control-integrity",
132
+ severity: "high",
133
+ subject,
134
+ claim: "A completed supervisor run has queued steer requests without acknowledgements",
135
+ detail: `${missingAckIds.length}/${requestsById.size} request id(s) are unacknowledged: ${idListDetail(missingAckIds)}.`,
136
+ evidence: [evidence(`${prefix}/inbox/unacknowledged-request-ids`, missingAckIds.slice(0, 20))],
137
+ recommendedAction: "Record one delivered or failed acknowledgement for every queued request id."
138
+ }));
139
+ const statusUnavailableIds = [];
140
+ const notDeliveredIds = [];
141
+ for (const [id, acknowledgements] of acknowledgementsById) {
142
+ if (!requestsById.has(id) || acknowledgements.length !== 1) continue;
143
+ const delivered = acknowledgements[0]?.delivered;
144
+ if (delivered === null || delivered === void 0) statusUnavailableIds.push(id);
145
+ else if (!delivered) notDeliveredIds.push(id);
146
+ }
147
+ if (statusUnavailableIds.length > 0) out.push(issue({
148
+ code: "steer-ack-status-unavailable",
149
+ area: "capture-integrity",
150
+ severity: "high",
151
+ subject,
152
+ claim: "Some steer acknowledgements omit delivery status",
153
+ detail: `${statusUnavailableIds.length} id(s): ${idListDetail(statusUnavailableIds)}.`,
154
+ evidence: [evidence(`${prefix}/events/status-unavailable-request-ids`, statusUnavailableIds.slice(0, 20))],
155
+ recommendedAction: "Record delivered: true or delivered: false on each acknowledgement.",
156
+ metadata: {
157
+ assessment: "unavailable",
158
+ unavailable_count: statusUnavailableIds.length
159
+ }
160
+ }));
161
+ if (notDeliveredIds.length > 0) out.push(issue({
162
+ code: "steer-not-delivered",
163
+ area: "control-integrity",
164
+ severity: "high",
165
+ subject,
166
+ claim: "Queued steer requests were acknowledged but not delivered",
167
+ detail: `${notDeliveredIds.length} id(s): ${idListDetail(notDeliveredIds)}.`,
168
+ evidence: [evidence(`${prefix}/events/not-delivered-request-ids`, notDeliveredIds.slice(0, 20))],
169
+ recommendedAction: "Retry or explicitly fail the control request instead of treating it as delivered."
170
+ }));
171
+ return out;
172
+ }
173
+ //#endregion
174
+ //#region src/supervisor-run/integrity-source.ts
175
+ function sourceIdentityIssues(facts) {
176
+ const out = [];
177
+ const malformedSpawns = facts.spawns.filter((spawn) => !spawn.valid);
178
+ if (facts.journalInvalidRows > 0 || malformedSpawns.length > 0) out.push(issue({
179
+ code: "source-row-malformed",
180
+ area: "capture-integrity",
181
+ severity: "high",
182
+ subject: "journal-rows",
183
+ claim: "Some supervisor journal rows are malformed",
184
+ detail: `${facts.journalInvalidRows} non-object or invalid JSON row(s) and ${malformedSpawns.length} malformed spawn row(s) were excluded from structural conclusions.`,
185
+ evidence: [evidence("journal/malformed-json-rows/count", facts.journalInvalidRows), ...malformedSpawns.slice(0, 20).map((spawn) => evidence(`journal/spawns/${spawn.sourceRow}/invalid-fields`, spawn.invalidFields))],
186
+ recommendedAction: "Repair or reject malformed journal rows before using the run for structural analysis.",
187
+ metadata: {
188
+ assessment: "unavailable",
189
+ malformed_json_rows: facts.journalInvalidRows,
190
+ malformed_spawn_rows: malformedSpawns.length
191
+ }
192
+ }));
193
+ const unidentifiedSpawns = malformedSpawns.filter((spawn) => spawn.id.length === 0);
194
+ const unidentifiedCloses = facts.closes.filter((close) => close.id.length === 0);
195
+ if (unidentifiedSpawns.length + unidentifiedCloses.length > 0) out.push(issue({
196
+ code: "source-event-identity-unavailable",
197
+ area: "capture-integrity",
198
+ severity: "high",
199
+ subject: "journal-event-identity",
200
+ claim: "Some supervisor journal events have no usable invocation id",
201
+ detail: `${unidentifiedSpawns.length} spawn and ${unidentifiedCloses.length} terminal event(s) lack an id.`,
202
+ evidence: [evidence("journal/unidentified-spawns/count", unidentifiedSpawns.length), evidence("journal/unidentified-terminals/count", unidentifiedCloses.length)],
203
+ recommendedAction: "Reject or repair control events that omit their stable invocation id.",
204
+ metadata: {
205
+ assessment: "unavailable",
206
+ checks: ["journal-event-linkage"]
207
+ }
208
+ }));
209
+ const spawnCounts = /* @__PURE__ */ new Map();
210
+ for (const spawn of facts.spawns) {
211
+ if (!spawn.valid || spawn.id.length === 0) continue;
212
+ spawnCounts.set(spawn.id, (spawnCounts.get(spawn.id) ?? 0) + 1);
213
+ }
214
+ for (const [id, count] of spawnCounts) {
215
+ if (count < 2) continue;
216
+ out.push(issue({
217
+ code: "duplicate-spawn",
218
+ area: "control-integrity",
219
+ severity: "critical",
220
+ subject: id,
221
+ claim: "An invocation has more than one spawned control event",
222
+ detail: `${count} spawned events name invocation ${JSON.stringify(id)}.`,
223
+ evidence: [evidence(`journal/spawn-count/${encodeURIComponent(id)}`, count)],
224
+ recommendedAction: "Make spawn event append idempotent."
225
+ }));
226
+ }
227
+ const closeCounts = /* @__PURE__ */ new Map();
228
+ for (const close of facts.closes) {
229
+ if (close.id.length === 0) continue;
230
+ closeCounts.set(close.id, (closeCounts.get(close.id) ?? 0) + 1);
231
+ }
232
+ const uncertainSpawnIds = new Set(malformedSpawns.filter((spawn) => spawn.id.length > 0).map((spawn) => spawn.id));
233
+ if (unidentifiedSpawns.length === 0 && facts.journalInvalidRows === 0) {
234
+ const orphanCloses = /* @__PURE__ */ new Map();
235
+ for (const close of facts.closes) {
236
+ if (close.id.length === 0 || spawnCounts.has(close.id) || uncertainSpawnIds.has(close.id)) continue;
237
+ const rows = orphanCloses.get(close.id) ?? [];
238
+ rows.push(close);
239
+ orphanCloses.set(close.id, rows);
240
+ }
241
+ for (const [id, closes] of orphanCloses) out.push(issue({
242
+ code: "orphan-terminal",
243
+ area: "control-integrity",
244
+ severity: "critical",
245
+ subject: id,
246
+ claim: "A terminal control event names an invocation that was never spawned",
247
+ detail: `${closes.length} terminal event(s) for ${JSON.stringify(id)} have no matching spawn.`,
248
+ evidence: [evidence(`journal/closes/${encodeURIComponent(id)}/count`, closes.length), ...closes.slice(0, 20).map((close, index) => evidence(`journal/closes/${encodeURIComponent(id)}/${index}`, close))],
249
+ recommendedAction: "Retain the matching spawn event or reject the orphan terminal event."
250
+ }));
251
+ }
252
+ for (const [id, count] of closeCounts) {
253
+ if (count < 2) continue;
254
+ out.push(issue({
255
+ code: "duplicate-terminal",
256
+ area: "control-integrity",
257
+ severity: "critical",
258
+ subject: id,
259
+ claim: "An invocation has more than one terminal control event",
260
+ detail: `${count} settled/cancelled events name invocation ${JSON.stringify(id)}.`,
261
+ evidence: [evidence(`journal/terminal-count/${encodeURIComponent(id)}`, count)],
262
+ recommendedAction: "Make terminal event append idempotent."
263
+ }));
264
+ }
265
+ return out;
266
+ }
267
+ function joinWorkerControls(facts) {
268
+ const spawnsById = /* @__PURE__ */ new Map();
269
+ const spawnsByLabel = /* @__PURE__ */ new Map();
270
+ for (const spawn of facts.workerSpawns) {
271
+ const ids = spawnsById.get(spawn.id) ?? [];
272
+ ids.push(spawn);
273
+ spawnsById.set(spawn.id, ids);
274
+ const labels = spawnsByLabel.get(spawn.label) ?? [];
275
+ labels.push(spawn);
276
+ spawnsByLabel.set(spawn.label, labels);
277
+ }
278
+ const rowsWithoutIdByLabel = /* @__PURE__ */ new Map();
279
+ for (const row of facts.workerLogRows) {
280
+ if (row.workerId !== null && row.workerId.length > 0) continue;
281
+ const matches = rowsWithoutIdByLabel.get(row.label) ?? [];
282
+ matches.push(row);
283
+ rowsWithoutIdByLabel.set(row.label, matches);
284
+ }
285
+ const rowsBySpawn = /* @__PURE__ */ new Map();
286
+ const unmatchedRows = [];
287
+ const ambiguousRows = [];
288
+ for (const row of facts.workerLogRows) {
289
+ const matches = row.workerId !== null && row.workerId.length > 0 ? spawnsById.get(row.workerId) ?? [] : (rowsWithoutIdByLabel.get(row.label)?.length ?? 0) === 1 ? spawnsByLabel.get(row.label) ?? [] : [];
290
+ if (matches.length === 0) {
291
+ unmatchedRows.push(row.sourceIndex);
292
+ continue;
293
+ }
294
+ if (matches.length !== 1) {
295
+ ambiguousRows.push(row.sourceIndex);
296
+ continue;
297
+ }
298
+ const spawn = matches[0];
299
+ const joined = rowsBySpawn.get(spawn.id) ?? [];
300
+ joined.push(row);
301
+ rowsBySpawn.set(spawn.id, joined);
302
+ }
303
+ const joined = [];
304
+ const missingSpawnIds = [];
305
+ const duplicateSpawnIds = [];
306
+ for (const spawn of facts.workerSpawns) {
307
+ if ((spawnsById.get(spawn.id)?.length ?? 0) !== 1) continue;
308
+ const rows = rowsBySpawn.get(spawn.id) ?? [];
309
+ if (rows.length === 0) missingSpawnIds.push(spawn.id);
310
+ else if (rows.length > 1) duplicateSpawnIds.push(spawn.id);
311
+ else joined.push({
312
+ spawn,
313
+ facts: rows[0]
314
+ });
315
+ }
316
+ if (missingSpawnIds.length === 0 && duplicateSpawnIds.length === 0 && unmatchedRows.length === 0 && ambiguousRows.length === 0) return {
317
+ joined,
318
+ issue: null
319
+ };
320
+ return {
321
+ joined,
322
+ issue: issue({
323
+ code: "worker-control-join-unavailable",
324
+ area: "capture-integrity",
325
+ severity: "high",
326
+ subject: "worker-control-join",
327
+ claim: "Worker control-log coverage does not match recorded child invocations",
328
+ detail: `${missingSpawnIds.length} spawn(s) have no log, ${duplicateSpawnIds.length} have duplicate logs, ${unmatchedRows.length} log row(s) match no spawn, and ${ambiguousRows.length} row(s) are ambiguous.`,
329
+ evidence: [
330
+ evidence("workers/join/missing-spawn-ids", missingSpawnIds.slice(0, 20)),
331
+ evidence("workers/join/duplicate-spawn-ids", duplicateSpawnIds.slice(0, 20)),
332
+ evidence("workers/join/unmatched-source-rows", unmatchedRows.slice(0, 20)),
333
+ evidence("workers/join/ambiguous-source-rows", ambiguousRows.slice(0, 20))
334
+ ],
335
+ recommendedAction: "Retain exactly one worker control row with workerId for every spawned invocation.",
336
+ metadata: {
337
+ assessment: "unavailable",
338
+ missing_count: missingSpawnIds.length,
339
+ duplicate_count: duplicateSpawnIds.length,
340
+ unmatched_count: unmatchedRows.length,
341
+ ambiguous_count: ambiguousRows.length
342
+ }
343
+ })
344
+ };
345
+ }
346
+ function sourceIssues(source, facts, tree) {
347
+ const out = sourceIdentityIssues(facts);
348
+ if (source.workers === null) {
349
+ const reason = source.workersMissingReason ?? "worker control-log store was not captured";
350
+ out.push(issue({
351
+ code: "worker-controls-unavailable",
352
+ area: "capture-integrity",
353
+ severity: "medium",
354
+ subject: "steer-delivery",
355
+ claim: "Steer delivery integrity is unavailable because worker control logs were not captured",
356
+ detail: reason,
357
+ evidence: [evidence("sources/workers", null)],
358
+ recommendedAction: "Retain each worker inbox and event stream.",
359
+ metadata: {
360
+ assessment: "unavailable",
361
+ reason
362
+ }
363
+ }));
364
+ return out;
365
+ }
366
+ const joined = joinWorkerControls(facts);
367
+ if (joined.issue !== null) out.push(joined.issue);
368
+ const rootCompleted = tree.rootId !== null && tree.nodes.filter((node) => node.rollout_id === tree.rootId).length === 1 && tree.nodes.find((node) => node.rollout_id === tree.rootId)?.outcome.is_completed === true;
369
+ for (const row of joined.joined) out.push(...steerIssues(row, rootCompleted));
370
+ return out;
371
+ }
372
+ //#endregion
373
+ //#region src/supervisor-run/integrity-tree.ts
374
+ function isRecord(value) {
375
+ return typeof value === "object" && value !== null && !Array.isArray(value);
376
+ }
377
+ function nonEmptyString(value) {
378
+ return typeof value === "string" && value.length > 0 ? value : null;
379
+ }
380
+ function parentValue(raw) {
381
+ if (raw.parent_rollout_id === null) return null;
382
+ return nonEmptyString(raw.parent_rollout_id) ?? void 0;
383
+ }
384
+ function terminalValue(outcome) {
385
+ if (outcome === null) return null;
386
+ if (typeof outcome.is_completed !== "boolean" || typeof outcome.is_truncated !== "boolean") return null;
387
+ if (outcome.error !== null && typeof outcome.error !== "string") return null;
388
+ return outcome.is_completed || outcome.is_truncated || outcome.error !== null;
389
+ }
390
+ function nodeRecord(value, index) {
391
+ const raw = isRecord(value) ? value : {};
392
+ const policy = isRecord(raw.policy) ? raw.policy : null;
393
+ const outcome = isRecord(raw.outcome) ? raw.outcome : null;
394
+ return {
395
+ index,
396
+ raw,
397
+ errors: validateRolloutLine(value),
398
+ id: nonEmptyString(raw.rollout_id),
399
+ parent: parentValue(raw),
400
+ runId: nonEmptyString(raw.run_id),
401
+ role: nonEmptyString(raw.role),
402
+ messages: Array.isArray(raw.messages) ? raw.messages : null,
403
+ profileId: policy === null ? null : nonEmptyString(policy.agent_profile_cell_id),
404
+ completed: outcome !== null && typeof outcome.is_completed === "boolean" ? outcome.is_completed : null,
405
+ terminal: terminalValue(outcome)
406
+ };
407
+ }
408
+ function metric(record, keys) {
409
+ const outcome = isRecord(record.raw.outcome) ? record.raw.outcome : null;
410
+ const metrics = outcome !== null && isRecord(outcome.metrics) ? outcome.metrics : null;
411
+ if (metrics === null) return null;
412
+ for (const key of keys) {
413
+ const value = metrics[key];
414
+ if (typeof value === "number" && Number.isFinite(value)) return {
415
+ key,
416
+ value
417
+ };
418
+ }
419
+ return null;
420
+ }
421
+ function nodeEvidence(record, path, value) {
422
+ const key = record.id ?? `index-${record.index}`;
423
+ return evidence(`nodes/${encodeURIComponent(key)}/${path}`, value);
424
+ }
425
+ function unavailableNodeChecks(record) {
426
+ const checks = [];
427
+ if (record.id === null) checks.push("identity", "root-membership", "parent-link", "cycle");
428
+ if (record.parent === void 0) checks.push("parent-link", "cycle");
429
+ if (record.runId === null) checks.push("cross-run-parent");
430
+ if (record.role === null) checks.push("root-role");
431
+ if (!isRecord(record.raw.outcome)) checks.push("timing", "terminal-state");
432
+ return [...new Set(checks)];
433
+ }
434
+ function treeIssues(tree, uncertainty = {}) {
435
+ const out = [];
436
+ const records = tree.nodes.map(nodeRecord);
437
+ const malformedSourceRows = tree.gaps.filter((gap) => gap.code === "source-row-malformed").reduce((count, gap) => count + (gap.count ?? 1), 0);
438
+ for (const record of records) {
439
+ if (record.errors.length === 0) continue;
440
+ out.push(issue({
441
+ code: "node-schema-invalid",
442
+ area: "capture-integrity",
443
+ severity: "high",
444
+ subject: record.id ?? `node-${record.index}`,
445
+ claim: "A supervisor-tree node fails canonical rollout validation",
446
+ detail: record.errors.join("; "),
447
+ evidence: [evidence(`nodes/${record.index}/validation`, record.errors)],
448
+ recommendedAction: "Fix the producer so every node validates as the existing tangle.rollout.v1 shape before analysis.",
449
+ metadata: { validation_errors: record.errors }
450
+ }));
451
+ const unavailableChecks = unavailableNodeChecks(record);
452
+ if (unavailableChecks.length === 0) continue;
453
+ out.push(issue({
454
+ code: "node-checks-unavailable",
455
+ area: "capture-integrity",
456
+ severity: "medium",
457
+ subject: record.id ?? `node-${record.index}`,
458
+ claim: "Some integrity checks are unavailable for a malformed supervisor-tree node",
459
+ detail: `Unavailable checks: ${unavailableChecks.join(", ")}.`,
460
+ evidence: [evidence(`nodes/${record.index}/unavailable-checks`, unavailableChecks)],
461
+ recommendedAction: "Retain the named identity and relationship fields with their canonical types.",
462
+ metadata: {
463
+ assessment: "unavailable",
464
+ checks: unavailableChecks
465
+ }
466
+ }));
467
+ }
468
+ const byId = /* @__PURE__ */ new Map();
469
+ for (const record of records) {
470
+ if (record.id === null) continue;
471
+ const matches = byId.get(record.id) ?? [];
472
+ matches.push(record);
473
+ byId.set(record.id, matches);
474
+ }
475
+ for (const [rolloutId, matches] of byId) {
476
+ if (matches.length < 2) continue;
477
+ out.push(issue({
478
+ code: "duplicate-rollout-id",
479
+ area: "control-integrity",
480
+ severity: "critical",
481
+ subject: rolloutId,
482
+ claim: "A rollout id is duplicated within one supervisor tree",
483
+ detail: `${matches.length} nodes claim rollout_id ${JSON.stringify(rolloutId)}, so references are ambiguous.`,
484
+ evidence: [evidence(`duplicates/${encodeURIComponent(rolloutId)}/count`, matches.length), ...matches.slice(0, 20).map((record) => evidence(`nodes/${record.index}/identity`, {
485
+ rollout_id: record.id,
486
+ parent_rollout_id: record.parent,
487
+ role: record.role
488
+ }))],
489
+ recommendedAction: "Give every invocation one stable rollout id and regenerate the tree."
490
+ }));
491
+ }
492
+ const unknownIdentityRows = records.filter((record) => record.id === null).length + Math.max(malformedSourceRows, uncertainty.unknownIdentityRows ?? 0);
493
+ const unique = records.filter((record) => record.id !== null && byId.get(record.id)?.length === 1);
494
+ let root;
495
+ if (tree.rootId === null) out.push(issue({
496
+ code: "root-unavailable",
497
+ area: "capture-integrity",
498
+ severity: "high",
499
+ subject: "tree-root",
500
+ claim: "The supervisor-tree root is unavailable",
501
+ detail: `rootId is null while ${records.length} node row(s) were captured.`,
502
+ evidence: [evidence("rootId", null)],
503
+ recommendedAction: "Capture the root spawned event and preserve its rollout id as rootId.",
504
+ metadata: {
505
+ assessment: "unavailable",
506
+ node_rows: records.length
507
+ }
508
+ }));
509
+ else {
510
+ const rootRows = byId.get(tree.rootId) ?? [];
511
+ if (rootRows.length === 1) root = rootRows[0];
512
+ else if (rootRows.length === 0 && unknownIdentityRows > 0) out.push(issue({
513
+ code: "node-checks-unavailable",
514
+ area: "capture-integrity",
515
+ severity: "high",
516
+ subject: tree.rootId,
517
+ claim: "Root membership is unavailable because a malformed node lost its rollout id",
518
+ detail: `${unknownIdentityRows} malformed node row(s) have no usable rollout_id, so absence of root ${JSON.stringify(tree.rootId)} cannot be established.`,
519
+ evidence: [evidence("rootId", tree.rootId)],
520
+ recommendedAction: "Retain rollout_id even when another node field fails validation.",
521
+ metadata: {
522
+ assessment: "unavailable",
523
+ checks: ["root-membership"]
524
+ }
525
+ }));
526
+ else if (rootRows.length === 0) out.push(issue({
527
+ code: "declared-root-missing",
528
+ area: "control-integrity",
529
+ severity: "critical",
530
+ subject: tree.rootId,
531
+ claim: "The declared supervisor-tree root has no matching invocation node",
532
+ detail: `rootId ${JSON.stringify(tree.rootId)} is absent from ${records.length} node row(s).`,
533
+ evidence: [evidence("rootId", tree.rootId)],
534
+ recommendedAction: "Restore the root rollout row or correct rootId."
535
+ }));
536
+ }
537
+ if (root !== void 0) {
538
+ if (root.parent !== void 0 && root.parent !== null) out.push(issue({
539
+ code: "root-has-parent",
540
+ area: "control-integrity",
541
+ severity: "critical",
542
+ subject: root.id ?? tree.rootId ?? "tree-root",
543
+ claim: "The declared supervisor-tree root has a parent",
544
+ detail: `Root points to parent ${JSON.stringify(root.parent)}.`,
545
+ evidence: [nodeEvidence(root, "parent_rollout_id", root.parent)],
546
+ recommendedAction: "Record the root with parent_rollout_id null."
547
+ }));
548
+ if (root.role !== null && root.role !== "supervisor") out.push(issue({
549
+ code: "root-role-invalid",
550
+ area: "control-integrity",
551
+ severity: "high",
552
+ subject: root.id ?? tree.rootId ?? "tree-root",
553
+ claim: "The declared supervisor-tree root is not labeled as a supervisor",
554
+ detail: `Root has role ${JSON.stringify(root.role)}.`,
555
+ evidence: [nodeEvidence(root, "role", root.role)],
556
+ recommendedAction: "Preserve the root invocation role as supervisor when minting rollout rows."
557
+ }));
558
+ }
559
+ for (const record of unique) {
560
+ if (record.parent === void 0) continue;
561
+ if (record.parent === null) {
562
+ if (record.id !== tree.rootId) out.push(issue({
563
+ code: "detached-node",
564
+ area: "control-integrity",
565
+ severity: "critical",
566
+ subject: record.id,
567
+ claim: "A non-root invocation is recorded as a detached parentless root",
568
+ detail: `Node ${JSON.stringify(record.id)} is parentless while rootId is ${JSON.stringify(tree.rootId)}.`,
569
+ evidence: [nodeEvidence(record, "parent_rollout_id", null), evidence("rootId", tree.rootId)],
570
+ recommendedAction: "Attach the invocation to its spawning parent or record a separate run."
571
+ }));
572
+ continue;
573
+ }
574
+ const parentRows = byId.get(record.parent) ?? [];
575
+ if (parentRows.length === 0) {
576
+ if (unknownIdentityRows > 0 || uncertainty.uncertainIds?.has(record.parent) === true) out.push(issue({
577
+ code: "node-checks-unavailable",
578
+ area: "capture-integrity",
579
+ severity: "high",
580
+ subject: record.id,
581
+ claim: "Parent membership is unavailable because a malformed node lost its rollout id",
582
+ detail: uncertainty.uncertainIds?.has(record.parent) === true ? `Parent ${JSON.stringify(record.parent)} appears only in a malformed source row, so its absence cannot be established.` : `Parent ${JSON.stringify(record.parent)} cannot be matched while ${unknownIdentityRows} row(s) have no usable rollout_id.`,
583
+ evidence: [nodeEvidence(record, "parent_rollout_id", record.parent)],
584
+ recommendedAction: "Retain rollout_id on every row before assessing detached children.",
585
+ metadata: {
586
+ assessment: "unavailable",
587
+ checks: ["parent-membership"]
588
+ }
589
+ }));
590
+ else out.push(issue({
591
+ code: "parent-missing",
592
+ area: "control-integrity",
593
+ severity: "critical",
594
+ subject: record.id,
595
+ claim: "An invocation points to a parent absent from the supervisor tree",
596
+ detail: `Node ${JSON.stringify(record.id)} names missing parent ${JSON.stringify(record.parent)}.`,
597
+ evidence: [nodeEvidence(record, "parent_rollout_id", record.parent)],
598
+ recommendedAction: "Retain the parent invocation or remove the dangling edge."
599
+ }));
600
+ continue;
601
+ }
602
+ if (parentRows.length !== 1) continue;
603
+ const parent = parentRows[0];
604
+ if (record.runId !== null && parent.runId !== null && record.runId !== parent.runId) out.push(issue({
605
+ code: "cross-run-parent",
606
+ area: "control-integrity",
607
+ severity: "critical",
608
+ subject: record.id,
609
+ claim: "A parent-child edge crosses supervisor-run identities",
610
+ detail: `Child run_id ${JSON.stringify(record.runId)} differs from parent run_id ${JSON.stringify(parent.runId)}.`,
611
+ evidence: [nodeEvidence(record, "run_id", record.runId), nodeEvidence(parent, "run_id", parent.runId)],
612
+ recommendedAction: "Keep one run_id across a supervision tree or remove the cross-run edge."
613
+ }));
614
+ const childStart = metric(record, ["spawned_at", "started_at"]);
615
+ const parentStart = metric(parent, ["spawned_at", "started_at"]);
616
+ if (childStart !== null && parentStart !== null && childStart.value < parentStart.value) out.push(issue({
617
+ code: "child-before-parent",
618
+ area: "control-integrity",
619
+ severity: "high",
620
+ subject: record.id,
621
+ claim: "A child invocation is timestamped before its parent started",
622
+ detail: `Child ${childStart.key} ${childStart.value} precedes parent ${parentStart.key} ${parentStart.value}.`,
623
+ evidence: [nodeEvidence(record, `outcome.metrics.${childStart.key}`, childStart.value), nodeEvidence(parent, `outcome.metrics.${parentStart.key}`, parentStart.value)],
624
+ recommendedAction: "Use one ordered clock for spawn events or retain clock metadata."
625
+ }));
626
+ const parentClose = metric(parent, [
627
+ "settled_at",
628
+ "completed_at",
629
+ "finished_at"
630
+ ]);
631
+ if (childStart !== null && parentClose !== null && childStart.value > parentClose.value) out.push(issue({
632
+ code: "child-after-parent-close",
633
+ area: "control-integrity",
634
+ severity: "critical",
635
+ subject: record.id,
636
+ claim: "A child invocation is timestamped after its parent closed",
637
+ detail: `Child ${childStart.key} ${childStart.value} follows parent ${parentClose.key} ${parentClose.value}.`,
638
+ evidence: [nodeEvidence(record, `outcome.metrics.${childStart.key}`, childStart.value), nodeEvidence(parent, `outcome.metrics.${parentClose.key}`, parentClose.value)],
639
+ recommendedAction: "Correct the causal timestamps or attach the child to its actual spawner."
640
+ }));
641
+ }
642
+ for (const record of unique) {
643
+ const start = metric(record, ["spawned_at", "started_at"]);
644
+ const close = metric(record, [
645
+ "settled_at",
646
+ "completed_at",
647
+ "finished_at"
648
+ ]);
649
+ if (start === null || close === null || close.value >= start.value) continue;
650
+ out.push(issue({
651
+ code: "close-before-start",
652
+ area: "control-integrity",
653
+ severity: "high",
654
+ subject: record.id,
655
+ claim: "An invocation is timestamped as closing before it started",
656
+ detail: `${start.key} is ${start.value} and ${close.key} is ${close.value}.`,
657
+ evidence: [nodeEvidence(record, `outcome.metrics.${start.key}`, start.value), nodeEvidence(record, `outcome.metrics.${close.key}`, close.value)],
658
+ recommendedAction: "Correct the event ordering or retain clock-domain metadata."
659
+ }));
660
+ }
661
+ const emittedCycles = /* @__PURE__ */ new Set();
662
+ const resolved = /* @__PURE__ */ new Set();
663
+ for (const start of unique) {
664
+ if (resolved.has(start.id)) continue;
665
+ const path = [];
666
+ const positions = /* @__PURE__ */ new Map();
667
+ let current = start.id;
668
+ while (current !== null) {
669
+ if (resolved.has(current)) break;
670
+ const position = positions.get(current);
671
+ if (position !== void 0) {
672
+ const cycle = path.slice(position);
673
+ const key = [...cycle].sort().join("\0");
674
+ if (!emittedCycles.has(key)) {
675
+ emittedCycles.add(key);
676
+ out.push(issue({
677
+ code: "parent-cycle",
678
+ area: "control-integrity",
679
+ severity: "critical",
680
+ subject: [...cycle].sort()[0] ?? "cycle",
681
+ claim: "The supervisor tree contains a parent cycle",
682
+ detail: `${cycle.join(" -> ")} -> ${cycle[0]}`,
683
+ evidence: cycle.slice(0, 20).flatMap((id) => {
684
+ const rows = byId.get(id);
685
+ const record = rows?.length === 1 ? rows[0] : void 0;
686
+ return record === void 0 ? [] : [nodeEvidence(record, "parent_rollout_id", record.parent)];
687
+ }),
688
+ recommendedAction: "Correct parent ids at ingestion; a cyclic graph is not a run tree."
689
+ }));
690
+ }
691
+ break;
692
+ }
693
+ positions.set(current, path.length);
694
+ path.push(current);
695
+ const rows = byId.get(current);
696
+ if (rows?.length !== 1) break;
697
+ const parent = rows[0]?.parent;
698
+ if (parent === void 0) break;
699
+ current = parent;
700
+ }
701
+ for (const id of path) resolved.add(id);
702
+ }
703
+ if (root?.completed === true) {
704
+ const missingTerminal = unique.filter((record) => record.id !== root?.id && record.terminal === false);
705
+ if (missingTerminal.length > 0) out.push(issue({
706
+ code: "child-terminal-unavailable",
707
+ area: "capture-integrity",
708
+ severity: "high",
709
+ subject: "child-terminal-outcomes",
710
+ claim: "A completed supervisor run has children with no captured terminal outcome",
711
+ detail: `${missingTerminal.length}/${Math.max(0, unique.length - 1)} child node(s) are neither completed, truncated, nor errored.`,
712
+ evidence: [evidence("children/missing-terminal/count", missingTerminal.length), ...missingTerminal.slice(0, 20).map((record) => nodeEvidence(record, "outcome", record.raw.outcome))],
713
+ recommendedAction: "Retain each child settlement or cancellation event.",
714
+ metadata: {
715
+ assessment: "unavailable",
716
+ unavailable_count: missingTerminal.length
717
+ }
718
+ }));
719
+ }
720
+ const missingTranscripts = records.filter((record) => record.messages?.length === 0);
721
+ if (missingTranscripts.length > 0) out.push(issue({
722
+ code: "transcript-unavailable",
723
+ area: "capture-integrity",
724
+ severity: "medium",
725
+ subject: "decision-transcripts",
726
+ claim: "Decision transcripts are unavailable for some supervisor-tree invocations",
727
+ detail: `${missingTranscripts.length}/${records.length} node row(s) have messages: [].`,
728
+ evidence: [evidence("capture/missing-transcripts/count", missingTranscripts.length), ...missingTranscripts.slice(0, 20).map((record) => nodeEvidence(record, "messages", []))],
729
+ recommendedAction: "Hydrate canonical messages from the retained session transcript.",
730
+ metadata: {
731
+ assessment: "unavailable",
732
+ unavailable_count: missingTranscripts.length
733
+ }
734
+ }));
735
+ const missingProfiles = records.filter((record) => record.profileId === null);
736
+ if (missingProfiles.length > 0) out.push(issue({
737
+ code: "profile-id-unavailable",
738
+ area: "capture-integrity",
739
+ severity: "medium",
740
+ subject: "agent-profile-identity",
741
+ claim: "Canonical agent-profile identity is unavailable for some supervisor-tree invocations",
742
+ detail: `${missingProfiles.length}/${records.length} node row(s) omit policy.agent_profile_cell_id.`,
743
+ evidence: [evidence("capture/missing-agent-profile-cell-id/count", missingProfiles.length), ...missingProfiles.slice(0, 20).map((record) => nodeEvidence(record, "policy.agent_profile_cell_id", null))],
744
+ recommendedAction: "Record policy.agent_profile_cell_id on every invocation at dispatch time.",
745
+ metadata: {
746
+ assessment: "unavailable",
747
+ unavailable_count: missingProfiles.length
748
+ }
749
+ }));
750
+ return out;
751
+ }
752
+ //#endregion
753
+ //#region src/supervisor-run/integrity-types.ts
754
+ const SUPERVISOR_RUN_INTEGRITY_SCHEMA = "tangle.supervisor-run-integrity@1";
755
+ //#endregion
756
+ //#region src/supervisor-run/source-facts.ts
757
+ function asRecord(v) {
758
+ return typeof v === "object" && v !== null ? v : {};
759
+ }
760
+ function num(v) {
761
+ return typeof v === "number" && Number.isFinite(v) ? v : 0;
762
+ }
763
+ function readSpend(v) {
764
+ const rec = asRecord(v);
765
+ const tok = asRecord(rec.tokens);
766
+ const cacheRead = tok.cacheRead ?? tok.cache_read;
767
+ const cacheWrite = tok.cacheWrite ?? tok.cache_write;
768
+ return {
769
+ tokens: {
770
+ input: num(tok.input),
771
+ output: num(tok.output),
772
+ cacheRead: num(cacheRead),
773
+ cacheWrite: num(cacheWrite),
774
+ hasCache: cacheRead !== void 0 || cacheWrite !== void 0
775
+ },
776
+ usd: num(rec.usd)
777
+ };
778
+ }
779
+ function parseJsonl(text) {
780
+ return parseJsonlWithDiagnostics(text).rows;
781
+ }
782
+ function parseJsonlWithDiagnostics(text) {
783
+ if (text === null) return {
784
+ rows: [],
785
+ invalidRows: 0
786
+ };
787
+ const out = [];
788
+ let invalidRows = 0;
789
+ for (const line of text.split("\n")) {
790
+ const trimmed = line.trim();
791
+ if (!trimmed) continue;
792
+ try {
793
+ const parsed = JSON.parse(trimmed);
794
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) out.push(parsed);
795
+ else invalidRows += 1;
796
+ } catch {
797
+ invalidRows += 1;
798
+ }
799
+ }
800
+ return {
801
+ rows: out,
802
+ invalidRows
803
+ };
804
+ }
805
+ function parseJson(text) {
806
+ if (text === null) return null;
807
+ try {
808
+ const parsed = JSON.parse(text);
809
+ return typeof parsed === "object" && parsed !== null ? parsed : null;
810
+ } catch {
811
+ return null;
812
+ }
813
+ }
814
+ function ms(at) {
815
+ if (typeof at !== "string") return null;
816
+ const t = Date.parse(at);
817
+ return Number.isFinite(t) ? t : null;
818
+ }
819
+ function readVerdict(v) {
820
+ if (typeof v === "string") return {
821
+ label: v,
822
+ valid: null,
823
+ score: null,
824
+ raw: v
825
+ };
826
+ if (typeof v !== "object" || v === null) return {
827
+ label: null,
828
+ valid: null,
829
+ score: null,
830
+ raw: null
831
+ };
832
+ const rec = v;
833
+ const valid = typeof rec.valid === "boolean" ? rec.valid : null;
834
+ return {
835
+ label: valid === null ? null : valid ? "valid" : "invalid",
836
+ valid,
837
+ score: typeof rec.score === "number" && Number.isFinite(rec.score) ? rec.score : null,
838
+ raw: v
839
+ };
840
+ }
841
+ function workerSourceKey(worker) {
842
+ return worker.workerId ?? worker.label;
843
+ }
844
+ function parseSupervisorTree(src) {
845
+ const parsedJournal = parseJsonlWithDiagnostics(src.journal);
846
+ const events = parsedJournal.rows;
847
+ const state = parseJson(src.state);
848
+ const spawns = [];
849
+ const closes = [];
850
+ let brainIn = 0;
851
+ let brainOut = 0;
852
+ let brainCacheRead = 0;
853
+ let brainCacheWrite = 0;
854
+ let brainHasCache = false;
855
+ let brainUsd = 0;
856
+ let meteredCount = 0;
857
+ let rootId = null;
858
+ for (const [sourceRow, ev] of events.entries()) {
859
+ const kind = typeof ev.kind === "string" ? ev.kind : "";
860
+ const id = typeof ev.id === "string" ? ev.id : "";
861
+ if (kind === "spawned") {
862
+ const parent = typeof ev.parent === "string" && ev.parent.length > 0 ? ev.parent : null;
863
+ const label = typeof ev.label === "string" ? ev.label : "";
864
+ const invalidFields = [];
865
+ if (id.length === 0) invalidFields.push("id");
866
+ const parentOmittedForFirstRoot = ev.parent === void 0 && rootId === null;
867
+ if (ev.parent !== null && !parentOmittedForFirstRoot && !(typeof ev.parent === "string" && ev.parent.length > 0)) invalidFields.push("parent");
868
+ if (label.length === 0) invalidFields.push("label");
869
+ if (ev.role !== void 0 && ev.role !== "supervisor" && ev.role !== "worker") invalidFields.push("role");
870
+ const role = ev.role === "supervisor" || ev.role === "worker" ? ev.role : parent === null ? "supervisor" : "worker";
871
+ const valid = invalidFields.length === 0;
872
+ if (valid && parent === null && rootId === null) rootId = id;
873
+ spawns.push({
874
+ sourceRow,
875
+ id,
876
+ parent,
877
+ label,
878
+ role,
879
+ at: ms(ev.at),
880
+ valid,
881
+ invalidFields
882
+ });
883
+ } else if (kind === "settled") {
884
+ const verdict = readVerdict(ev.verdict);
885
+ closes.push({
886
+ id,
887
+ kind: "settled",
888
+ status: typeof ev.status === "string" ? ev.status : null,
889
+ verdict: verdict.label,
890
+ valid: verdict.valid,
891
+ score: verdict.score,
892
+ rawVerdict: verdict.raw,
893
+ at: ms(ev.at),
894
+ spend: readSpend(ev.spent),
895
+ hasSpend: asRecord(ev.spent).tokens !== void 0
896
+ });
897
+ } else if (kind === "cancelled") closes.push({
898
+ id,
899
+ kind: "cancelled",
900
+ status: "cancelled",
901
+ verdict: typeof ev.reason === "string" ? ev.reason : null,
902
+ valid: null,
903
+ score: null,
904
+ rawVerdict: typeof ev.reason === "string" ? ev.reason : null,
905
+ at: ms(ev.at),
906
+ spend: {
907
+ tokens: {
908
+ input: 0,
909
+ output: 0,
910
+ cacheRead: 0,
911
+ cacheWrite: 0,
912
+ hasCache: false
913
+ },
914
+ usd: 0
915
+ },
916
+ hasSpend: false
917
+ });
918
+ else if (kind === "metered") {
919
+ const s = readSpend(ev.spend);
920
+ brainIn += s.tokens.input;
921
+ brainOut += s.tokens.output;
922
+ brainCacheRead += s.tokens.cacheRead;
923
+ brainCacheWrite += s.tokens.cacheWrite;
924
+ brainHasCache = brainHasCache || s.tokens.hasCache;
925
+ brainUsd += s.usd;
926
+ meteredCount += 1;
927
+ }
928
+ }
929
+ const workerSpawns = spawns.filter((s) => s.valid && s.id !== rootId);
930
+ const workerIds = new Set(workerSpawns.map((s) => s.id));
931
+ const workerCloses = closes.filter((c) => workerIds.has(c.id));
932
+ const workerLogs = /* @__PURE__ */ new Map();
933
+ const workerLogRows = [];
934
+ for (const [sourceIndex, w] of (src.workers ?? []).entries()) {
935
+ const facts = {
936
+ sourceIndex,
937
+ workerId: w.workerId ?? null,
938
+ label: w.label,
939
+ inboxCaptured: w.inbox !== null,
940
+ eventsCaptured: w.events !== null,
941
+ inboxInvalidRows: 0,
942
+ eventsInvalidRows: 0,
943
+ started: null,
944
+ finished: false,
945
+ finishedAt: null,
946
+ passed: null,
947
+ score: null,
948
+ finishedPatchBytes: null,
949
+ evidenceBytes: 0,
950
+ steersQueued: null,
951
+ steersDelivered: null,
952
+ steersQueuedUnavailable: null,
953
+ steersDeliveredUnavailable: null,
954
+ questions: 0,
955
+ steerRequests: [],
956
+ steerAcknowledgements: []
957
+ };
958
+ const parsedInbox = parseJsonlWithDiagnostics(w.inbox);
959
+ const parsedEvents = parseJsonlWithDiagnostics(w.events);
960
+ facts.inboxInvalidRows = parsedInbox.invalidRows;
961
+ facts.eventsInvalidRows = parsedEvents.invalidRows;
962
+ for (const [row, req] of parsedInbox.rows.entries()) {
963
+ if (typeof req.message !== "string" || req.message.trim().length === 0) continue;
964
+ facts.steerRequests.push({
965
+ requestId: typeof req.id === "string" && req.id.length > 0 ? req.id : null,
966
+ row
967
+ });
968
+ }
969
+ for (const [row, ev] of parsedEvents.rows.entries()) {
970
+ const kind = ev.kind;
971
+ if (kind === "message") {
972
+ if (ev.direction === "up") {
973
+ facts.questions += 1;
974
+ continue;
975
+ }
976
+ facts.steerAcknowledgements.push({
977
+ requestId: typeof ev.requestId === "string" && ev.requestId.length > 0 ? ev.requestId : null,
978
+ delivered: typeof ev.delivered === "boolean" ? ev.delivered : null,
979
+ row
980
+ });
981
+ } else if (kind === "started") facts.started = ms(ev.at);
982
+ else if (kind === "finished") {
983
+ facts.finished = true;
984
+ facts.finishedAt = ms(ev.at);
985
+ facts.passed = typeof ev.passed === "boolean" ? ev.passed : null;
986
+ facts.score = typeof ev.score === "number" && Number.isFinite(ev.score) ? ev.score : null;
987
+ facts.finishedPatchBytes = typeof ev.patchBytes === "number" ? ev.patchBytes : null;
988
+ facts.evidenceBytes = typeof ev.evidence === "string" ? ev.evidence.length : 0;
989
+ }
990
+ }
991
+ const requestsById = /* @__PURE__ */ new Map();
992
+ for (const request of facts.steerRequests) {
993
+ if (request.requestId === null) continue;
994
+ requestsById.set(request.requestId, (requestsById.get(request.requestId) ?? 0) + 1);
995
+ }
996
+ const acknowledgementsById = /* @__PURE__ */ new Map();
997
+ for (const acknowledgement of facts.steerAcknowledgements) {
998
+ if (acknowledgement.requestId === null) continue;
999
+ const matches = acknowledgementsById.get(acknowledgement.requestId) ?? [];
1000
+ matches.push(acknowledgement);
1001
+ acknowledgementsById.set(acknowledgement.requestId, matches);
1002
+ }
1003
+ const queueProblems = [];
1004
+ if (!facts.inboxCaptured) queueProblems.push("inbox absent");
1005
+ if (!facts.eventsCaptured) queueProblems.push("events absent");
1006
+ if (facts.inboxInvalidRows > 0) queueProblems.push("inbox contains malformed rows");
1007
+ if (facts.eventsInvalidRows > 0) queueProblems.push("events contain malformed rows");
1008
+ if (facts.steerRequests.some((request) => request.requestId === null)) queueProblems.push("queued request id missing");
1009
+ if ([...requestsById.values()].some((count) => count > 1)) queueProblems.push("queued request id duplicated");
1010
+ if (facts.steerAcknowledgements.some((acknowledgement) => acknowledgement.requestId === null)) queueProblems.push("acknowledgement request id missing");
1011
+ if ([...acknowledgementsById.keys()].some((id) => !requestsById.has(id))) queueProblems.push("acknowledgement has no queued request");
1012
+ if (queueProblems.length === 0) facts.steersQueued = requestsById.size;
1013
+ else facts.steersQueuedUnavailable = [...new Set(queueProblems)].join("; ");
1014
+ const deliveryProblems = [...queueProblems];
1015
+ if ([...acknowledgementsById.values()].some((rows) => rows.length > 1)) deliveryProblems.push("acknowledgement request id duplicated");
1016
+ if (facts.steerAcknowledgements.some((acknowledgement) => acknowledgement.delivered === null)) deliveryProblems.push("acknowledgement delivery status missing");
1017
+ if (deliveryProblems.length === 0) facts.steersDelivered = [...requestsById.keys()].filter((id) => {
1018
+ const acknowledgement = acknowledgementsById.get(id);
1019
+ return acknowledgement?.length === 1 && acknowledgement[0]?.delivered === true;
1020
+ }).length;
1021
+ else facts.steersDeliveredUnavailable = [...new Set(deliveryProblems)].join("; ");
1022
+ workerLogRows.push(facts);
1023
+ workerLogs.set(workerSourceKey(w), facts);
1024
+ }
1025
+ const startedAt = ms(state?.startedAt) ?? spawns[0]?.at ?? null;
1026
+ const completedAt = ms(state?.completedAt) ?? [...spawns.map((s) => s.at), ...closes.map((c) => c.at)].reduce((acc, t) => t === null ? acc : acc === null ? t : Math.max(acc, t), null);
1027
+ return {
1028
+ rootId,
1029
+ spawns,
1030
+ closes,
1031
+ workerSpawns,
1032
+ workerCloses,
1033
+ brain: {
1034
+ tokensIn: brainIn,
1035
+ tokensOut: brainOut,
1036
+ cacheRead: brainCacheRead,
1037
+ cacheWrite: brainCacheWrite,
1038
+ hasCache: brainHasCache,
1039
+ usd: brainUsd,
1040
+ meteredCount
1041
+ },
1042
+ workerLogs,
1043
+ workerLogRows,
1044
+ journalInvalidRows: parsedJournal.invalidRows,
1045
+ state,
1046
+ startedAt,
1047
+ completedAt
1048
+ };
1049
+ }
1050
+ //#endregion
1051
+ //#region src/supervisor-run/rollout-nodes.ts
1052
+ /**
1053
+ * The supervision tree as `tangle.rollout.v1` rows.
1054
+ *
1055
+ * A supervisor run IS a tree of rollouts, so its nodes are not a new shape:
1056
+ * the root becomes one `RolloutLine` with `role: 'supervisor'`, and every
1057
+ * spawned invocation keeps its explicit supervisor/worker role with
1058
+ * `parent_rollout_id` pointing at its spawner. The rows append to the same
1059
+ * ledger as solo-agent rollouts and join to them with the same keys.
1060
+ *
1061
+ * What the journal CANNOT supply is the transcript: a worker's messages live
1062
+ * in its harness store (opencode sqlite, Claude Code jsonl), which the
1063
+ * `src/rollout/readers/*` intake readers own. Rows minted here are therefore
1064
+ * GAP lines (`messages: []`, `provenance.gap` set) carrying identity,
1065
+ * structure, outcome and cost; hydrating them with messages is the readers'
1066
+ * job, keyed on `artifacts.transcript_ref`.
1067
+ *
1068
+ * Timing lives in `outcome.metrics` (`spawned_at` / `settled_at` / `wall_ms`)
1069
+ * rather than a schema field: `tangle.rollout.v1` describes ONE invocation,
1070
+ * and the inter-invocation event timeline — which is what waves, concurrency,
1071
+ * idle and utilization are computed from — is a property of the journal, not
1072
+ * of any single row. The analyzer reads that timeline; these rows carry the
1073
+ * per-node facts.
1074
+ */
1075
+ const EMPTY_COST = {
1076
+ usd: null,
1077
+ tokens_in: null,
1078
+ tokens_out: null,
1079
+ tokens_reasoning: null,
1080
+ cache_read: null,
1081
+ cache_write: null,
1082
+ wall_s: null
1083
+ };
1084
+ function hasWorkerId(worker) {
1085
+ return worker.workerId !== void 0;
1086
+ }
1087
+ /**
1088
+ * Mint the supervision tree as rollout rows. Returns the rows plus the gaps
1089
+ * that made any of them incomplete — same unavailable-vs-zero discipline as
1090
+ * the report: a row with no transcript says WHY, it never pretends to be empty.
1091
+ */
1092
+ function supervisorRunRolloutLines(src, opts = {}) {
1093
+ return supervisorRunRolloutLinesFromFacts(src, parseSupervisorTree(src), opts);
1094
+ }
1095
+ /** Mint rollout rows from an already parsed source without reading its JSONL again. */
1096
+ function supervisorRunRolloutLinesFromFacts(src, tree, opts = {}) {
1097
+ const gaps = [];
1098
+ if (src.journal === null) {
1099
+ gaps.push({
1100
+ code: "journal-unavailable",
1101
+ message: `${src.supRunDir === null ? "no supervisor run dir" : "journal absent"}; no nodes recoverable`
1102
+ });
1103
+ return {
1104
+ rootId: null,
1105
+ nodes: [],
1106
+ gaps
1107
+ };
1108
+ }
1109
+ const malformedSourceRows = tree.journalInvalidRows + tree.spawns.filter((spawn) => !spawn.valid).length;
1110
+ if (malformedSourceRows > 0) gaps.push({
1111
+ code: "source-row-malformed",
1112
+ message: `${malformedSourceRows} malformed journal row(s) were excluded from the tree`,
1113
+ count: malformedSourceRows
1114
+ });
1115
+ const state = tree.state;
1116
+ const result = parseJson(src.result);
1117
+ const judge = parseJson(src.judge);
1118
+ const stateResult = asRecord(state?.result);
1119
+ const rootId = tree.rootId;
1120
+ const runId = opts.runId ?? rootId ?? src.runRef;
1121
+ const suite = opts.suite ?? "supervisor-run";
1122
+ const instanceId = src.instanceId ?? src.runRef;
1123
+ const split = opts.split ?? "search";
1124
+ const capturedAt = opts.capturedAt ?? (/* @__PURE__ */ new Date()).toISOString();
1125
+ const base = {
1126
+ schema: ROLLOUT_SCHEMA,
1127
+ run_id: runId,
1128
+ experiment_id: opts.experimentId ?? null,
1129
+ candidate_id: opts.candidateId ?? null,
1130
+ generation: opts.generation ?? null,
1131
+ candidate_index: opts.candidateIndex ?? null,
1132
+ task: {
1133
+ suite,
1134
+ instance_id: instanceId,
1135
+ split,
1136
+ seed: opts.seed ?? null,
1137
+ rep: opts.rep ?? 0
1138
+ },
1139
+ messages: [],
1140
+ tool_defs: []
1141
+ };
1142
+ const nodes = [];
1143
+ if (rootId !== null) {
1144
+ const rootSpawn = tree.spawns.find((spawn) => spawn.valid && spawn.id === rootId);
1145
+ const judgeResolved = typeof judge?.resolved === "boolean" ? judge.resolved : null;
1146
+ const reward = (typeof judge?.score === "number" ? judge.score : null) ?? (judgeResolved === null ? null : judgeResolved ? 1 : 0);
1147
+ if (reward === null) gaps.push({
1148
+ code: "root-reward-unavailable",
1149
+ message: "no judge verdict for this run",
1150
+ nodeId: rootId
1151
+ });
1152
+ const wallMs = tree.startedAt !== null && tree.completedAt !== null && tree.completedAt >= tree.startedAt ? tree.completedAt - tree.startedAt : null;
1153
+ nodes.push({
1154
+ ...base,
1155
+ rollout_id: rootId,
1156
+ parent_rollout_id: null,
1157
+ role: rootSpawn?.role ?? "supervisor",
1158
+ policy: {
1159
+ harness: opts.supervisorHarness ?? null,
1160
+ harness_version: null,
1161
+ model: opts.supervisorModel ?? null,
1162
+ provider: null,
1163
+ profile_commit: null,
1164
+ sampling: null
1165
+ },
1166
+ outcome: {
1167
+ ...unscreenedRewardFields(reward),
1168
+ reward_source: src.judgeSource,
1169
+ verdict: judge ?? null,
1170
+ metrics: {
1171
+ arm: src.arm,
1172
+ sup_status: typeof state?.status === "string" ? state.status : null,
1173
+ sup_verdict: typeof state?.verdict === "string" ? state.verdict : null,
1174
+ delivered: typeof stateResult.delivered === "boolean" ? stateResult.delivered : null,
1175
+ verify_pass: typeof result?.verify_pass === "boolean" ? result.verify_pass : null,
1176
+ started_at: tree.startedAt,
1177
+ completed_at: tree.completedAt,
1178
+ workers_spawned: tree.workerSpawns.length,
1179
+ brain_metered_events: tree.brain.meteredCount
1180
+ },
1181
+ is_completed: state?.status === "completed",
1182
+ is_truncated: false,
1183
+ error: null,
1184
+ realness_gated: false
1185
+ },
1186
+ cost: {
1187
+ ...EMPTY_COST,
1188
+ usd: src.limits.spendUsd !== null ? null : typeof stateResult.spentUsd === "number" ? stateResult.spentUsd : tree.brain.usd,
1189
+ tokens_in: src.limits.managerTokens === null ? tree.brain.tokensIn : null,
1190
+ tokens_out: src.limits.managerTokens === null ? tree.brain.tokensOut : null,
1191
+ cache_read: src.limits.managerTokens === null && tree.brain.hasCache ? tree.brain.cacheRead : null,
1192
+ cache_write: src.limits.managerTokens === null && tree.brain.hasCache ? tree.brain.cacheWrite : null,
1193
+ wall_s: wallMs === null ? null : wallMs / 1e3
1194
+ },
1195
+ artifacts: {
1196
+ patch_path: typeof result?.patchPath === "string" ? result.patchPath : null,
1197
+ run_dir: src.supRunDir,
1198
+ transcript_ref: src.rootTranscriptRef !== void 0 ? src.rootTranscriptRef : src.supRunDir === null ? null : `${src.supRunDir}/journal.jsonl`
1199
+ },
1200
+ provenance: {
1201
+ captured_at: capturedAt,
1202
+ capture: "backfill",
1203
+ gap: "supervision journal carries structure and spend, not the brain transcript"
1204
+ }
1205
+ });
1206
+ } else gaps.push({
1207
+ code: "root-spawn-unavailable",
1208
+ message: "no parentless spawned event in the journal"
1209
+ });
1210
+ const closeById = new Map(tree.closes.map((c) => [c.id, c]));
1211
+ const sourceById = new Map((src.workers ?? []).filter(hasWorkerId).map((worker) => [worker.workerId, worker]));
1212
+ const fallbackSourceByLabel = new Map((src.workers ?? []).filter((worker) => worker.workerId === void 0).map((worker) => [worker.label, worker]));
1213
+ for (const spawn of tree.workerSpawns) {
1214
+ const close = closeById.get(spawn.id) ?? null;
1215
+ const workerSource = sourceById.get(spawn.id) ?? fallbackSourceByLabel.get(spawn.label) ?? null;
1216
+ const facts = tree.workerLogs.get(workerSource?.workerId ?? workerSource?.label ?? spawn.label) ?? null;
1217
+ const wallMs = facts?.started != null && facts.finishedAt != null ? facts.finishedAt - facts.started : null;
1218
+ const score = close?.score ?? facts?.score ?? null;
1219
+ const passed = close?.valid ?? facts?.passed ?? null;
1220
+ const reward = score ?? (passed === null ? null : passed ? 1 : 0);
1221
+ if (reward === null) gaps.push({
1222
+ code: "child-reward-unavailable",
1223
+ message: `child ${JSON.stringify(spawn.label)} has no verify verdict`,
1224
+ nodeId: spawn.id
1225
+ });
1226
+ const isSupervisor = spawn.role === "supervisor";
1227
+ nodes.push({
1228
+ ...base,
1229
+ rollout_id: spawn.id,
1230
+ parent_rollout_id: spawn.parent,
1231
+ role: spawn.role,
1232
+ policy: {
1233
+ harness: isSupervisor ? opts.supervisorHarness ?? null : opts.workerHarness ?? null,
1234
+ harness_version: null,
1235
+ model: isSupervisor ? opts.supervisorModel ?? null : opts.workerModel ?? null,
1236
+ provider: null,
1237
+ profile_commit: null,
1238
+ sampling: null
1239
+ },
1240
+ outcome: {
1241
+ ...unscreenedRewardFields(reward),
1242
+ reward_source: reward === null ? null : score === null ? "worker-self-verify" : "worker-verdict-score",
1243
+ verdict: close === null ? null : {
1244
+ kind: close.kind,
1245
+ status: close.status,
1246
+ verdict: close.rawVerdict,
1247
+ valid: close.valid,
1248
+ score: close.score
1249
+ },
1250
+ metrics: {
1251
+ label: spawn.label,
1252
+ spawned_at: spawn.at,
1253
+ settled_at: close?.at ?? null,
1254
+ started_at: facts?.started ?? null,
1255
+ finished_at: facts?.finishedAt ?? null,
1256
+ wall_ms: wallMs,
1257
+ patch_bytes: workerSource?.patchBytes ?? facts?.finishedPatchBytes ?? null,
1258
+ evidence_bytes: facts?.evidenceBytes ?? null,
1259
+ steers_queued: facts?.steersQueued ?? null,
1260
+ steers_delivered: facts?.steersDelivered ?? null,
1261
+ questions: facts?.questions ?? null
1262
+ },
1263
+ is_completed: close?.kind === "settled",
1264
+ is_truncated: close?.kind === "cancelled",
1265
+ error: close?.kind === "cancelled" ? close.verdict ?? "cancelled" : null,
1266
+ realness_gated: false
1267
+ },
1268
+ cost: {
1269
+ ...EMPTY_COST,
1270
+ usd: src.limits.spendUsd !== null || !close?.hasSpend ? null : close.spend.usd,
1271
+ tokens_in: workerSource?.tokensIn ?? (close?.hasSpend ? close.spend.tokens.input : null),
1272
+ tokens_out: workerSource?.tokensOut ?? (close?.hasSpend ? close.spend.tokens.output : null),
1273
+ cache_read: workerSource?.cacheRead ?? (close?.hasSpend && close.spend.tokens.hasCache ? close.spend.tokens.cacheRead : null),
1274
+ cache_write: workerSource?.cacheWrite ?? (close?.hasSpend && close.spend.tokens.hasCache ? close.spend.tokens.cacheWrite : null),
1275
+ wall_s: wallMs === null ? null : wallMs / 1e3
1276
+ },
1277
+ artifacts: {
1278
+ patch_path: workerSource?.patchPath !== void 0 ? workerSource.patchPath : src.supRunDir === null ? null : `${src.supRunDir}/workers/${spawn.label}.patch`,
1279
+ run_dir: src.supRunDir,
1280
+ transcript_ref: workerSource?.transcriptRef !== void 0 ? workerSource.transcriptRef : src.supRunDir === null ? null : `${src.supRunDir}/workers/${spawn.label}.ndjson`
1281
+ },
1282
+ provenance: {
1283
+ captured_at: capturedAt,
1284
+ capture: "backfill",
1285
+ gap: workerSource?.transcriptRef == null ? "worker transcript lives in the harness session store — hydrate via src/rollout/readers" : "worker transcript recorded by the harness; messages not inlined into this row"
1286
+ }
1287
+ });
1288
+ }
1289
+ const invalid = nodes.filter((n) => !isRolloutLine(n));
1290
+ if (invalid.length > 0) gaps.push({
1291
+ code: "node-schema-invalid",
1292
+ message: `${invalid.length} node(s) failed tangle.rollout.v1 validation`,
1293
+ count: invalid.length
1294
+ });
1295
+ return {
1296
+ rootId,
1297
+ nodes,
1298
+ gaps
1299
+ };
1300
+ }
1301
+ //#endregion
1302
+ //#region src/supervisor-run/integrity.ts
1303
+ function isTree(input) {
1304
+ return "nodes" in input && Array.isArray(input.nodes);
1305
+ }
1306
+ function treeRunRef(tree) {
1307
+ const runIds = [...new Set(tree.nodes.map((node) => node.run_id).filter((runId) => typeof runId === "string" && runId.length > 0))].sort();
1308
+ if (runIds.length === 1) return runIds[0];
1309
+ if (runIds.length > 1) return `mixed-runs:${runIds.join(",")}`;
1310
+ return tree.rootId ?? "unidentified-tree";
1311
+ }
1312
+ /**
1313
+ * Run deterministic integrity checks over source bytes or an already minted tree.
1314
+ * Source input is parsed exactly once and reused by projection and source-only checks.
1315
+ */
1316
+ function analyzeSupervisorRunIntegrity(input, options = {}) {
1317
+ if (isTree(input)) {
1318
+ const sourceOnlyChecks = [
1319
+ "journal-event-cardinality",
1320
+ "worker-control-join",
1321
+ "steer-ack-correlation"
1322
+ ];
1323
+ return {
1324
+ schema: SUPERVISOR_RUN_INTEGRITY_SCHEMA,
1325
+ runRef: treeRunRef(input),
1326
+ input: "tree",
1327
+ tree: input,
1328
+ issues: [...treeIssues(input), issue({
1329
+ code: "source-checks-unavailable",
1330
+ area: "capture-integrity",
1331
+ severity: "medium",
1332
+ subject: "source-only-control-checks",
1333
+ claim: "Source-only control checks are unavailable for SupervisorRunTree input",
1334
+ detail: "Rollout rows do not retain journal event multiplicity or worker inbox and acknowledgement rows.",
1335
+ evidence: [evidence("input/source-only-checks", sourceOnlyChecks)],
1336
+ recommendedAction: "Pass SupervisorRunSources when journal and worker control checks are required.",
1337
+ metadata: {
1338
+ assessment: "unavailable",
1339
+ checks: sourceOnlyChecks
1340
+ }
1341
+ })]
1342
+ };
1343
+ }
1344
+ const facts = parseSupervisorTree(input);
1345
+ const tree = supervisorRunRolloutLinesFromFacts(input, facts, { capturedAt: options.capturedAt });
1346
+ return {
1347
+ schema: SUPERVISOR_RUN_INTEGRITY_SCHEMA,
1348
+ runRef: input.runRef,
1349
+ input: "sources",
1350
+ tree,
1351
+ issues: [...treeIssues(tree, {
1352
+ unknownIdentityRows: facts.journalInvalidRows + facts.spawns.filter((spawn) => !spawn.valid && spawn.id.length === 0).length,
1353
+ uncertainIds: new Set(facts.spawns.filter((spawn) => !spawn.valid && spawn.id.length > 0).map((spawn) => spawn.id))
1354
+ }), ...sourceIssues(input, facts, tree)]
1355
+ };
1356
+ }
1357
+ //#endregion
1358
+ export { parseJsonl as a, SUPERVISOR_RUN_INTEGRITY_SCHEMA as c, parseJson as i, supervisorRunRolloutLines as n, parseSupervisorTree as o, asRecord as r, workerSourceKey as s, analyzeSupervisorRunIntegrity as t };
1359
+
1360
+ //# sourceMappingURL=integrity-CCXTftiL.js.map