sparkforensics-server 0.6.0 → 0.8.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 (410) hide show
  1. package/index.js +16 -7
  2. package/package.json +1 -1
  3. package/public/assets/AutoscalingChurn-DenWKTv-.js +1 -0
  4. package/public/assets/CacheUtilization-DLz7NdjV.js +1 -0
  5. package/public/assets/CachingOpportunity-zpQIqhzm.js +1 -0
  6. package/public/assets/ChartTheme-DAiARbLP.js +8 -0
  7. package/public/assets/ColdStart-C-9HjxWl.js +1 -0
  8. package/public/assets/ConfigAudit-D0V9tcrJ.js +1 -0
  9. package/public/assets/CoreUsageArea-yeerrljd.js +1 -0
  10. package/public/assets/Dashboard-DkUPrHKV.js +1 -0
  11. package/public/assets/DisclosureContext-DK1_iz6J.js +1 -0
  12. package/public/assets/DuplicatePlanSubtree-xDNDbCd3.js +1 -0
  13. package/public/assets/DurationHistogram-B6jsE0Ov.js +1 -0
  14. package/public/assets/EmptyState-C1uL40VU.js +1 -0
  15. package/public/assets/ExecutorUtilization-DXDZKmSc.js +1 -0
  16. package/public/assets/GcPressure-MRf7I4IG.js +1 -0
  17. package/public/assets/ImpactEstimate-Dm3xWbzV.js +1 -0
  18. package/public/assets/IncompleteRun-CaLOhMtf.js +1 -0
  19. package/public/assets/JobFailures-CzmzlvhO.js +1 -0
  20. package/public/assets/MemoryUtilization-AWUMUjCH.js +1 -0
  21. package/public/assets/NestedLoopJoin-C9sNr6Ss.js +1 -0
  22. package/public/assets/OverBroadcast-BOmRE6ca.js +1 -0
  23. package/public/assets/PartitionHint-BL2EmJfE.js +1 -0
  24. package/public/assets/PartitionSizing-BzBySvUq.js +1 -0
  25. package/public/assets/PlanExplorer-DdiQRcJU.js +1 -0
  26. package/public/assets/PlanGraphRoute-CIH9gBuR.js +1 -0
  27. package/public/assets/PlanView-B1hxzFRs.js +2 -0
  28. package/public/assets/PythonUdf-DvgCCueD.js +1 -0
  29. package/public/assets/RetryWaste-uXXu_CyM.js +1 -0
  30. package/public/assets/RowPagination-C-LzWkI5.js +1 -0
  31. package/public/assets/RowStatusCluster-Dbtmte7l.js +1 -0
  32. package/public/assets/ShuffleIO-nALwzC1_.js +1 -0
  33. package/public/assets/Skew-CHMEKgvk.js +1 -0
  34. package/public/assets/SlowHost-yyvjpHk6.js +1 -0
  35. package/public/assets/SmallFiles-y6odJ97n.js +1 -0
  36. package/public/assets/SpeculationWaste-C-SD2LOt.js +1 -0
  37. package/public/assets/Spill-DHTMmOup.js +1 -0
  38. package/public/assets/StageFailed-CQ3DxC9R.js +1 -0
  39. package/public/assets/StageFindingGroup-BgYLyj5q.js +1 -0
  40. package/public/assets/StageHeader-CIETkjVZ.js +1 -0
  41. package/public/assets/StagePill--P7PDd3s.js +1 -0
  42. package/public/assets/StageShape-BIv8vx_o.js +1 -0
  43. package/public/assets/StageSlowness-MMqzERxN.js +1 -0
  44. package/public/assets/Straggler-BgrY7AyW.js +1 -0
  45. package/public/assets/TaskFailures-BHHyKYTs.js +1 -0
  46. package/public/assets/TinyTask-DDScNhCl.js +1 -0
  47. package/public/assets/UnderBroadcast-CGIC04TI.js +1 -0
  48. package/public/assets/WidgetLeadSummary-CXFZP00C.js +1 -0
  49. package/public/assets/button-DbpTz2yk.js +1 -0
  50. package/public/assets/charts-vendor-CtTxwTeu.js +59 -0
  51. package/public/assets/composite-C9Nu2EI9.js +1 -0
  52. package/public/assets/d3-vendor-DNlD2-cz.js +1 -0
  53. package/public/assets/detector-registry-Bp8hrLua.js +2 -0
  54. package/public/assets/downsample-WmNM1qc9.js +1 -0
  55. package/public/assets/finding-anchor-Dr40LkS9.js +1 -0
  56. package/public/assets/findings-of-type-BL2u3RGZ.js +1 -0
  57. package/public/assets/impact-sort-mC5ISgX8.js +1 -0
  58. package/public/assets/inbox-KPU1Gppm.js +1 -0
  59. package/public/assets/index-CaRVPvAT.css +2 -0
  60. package/public/assets/index-fDiwfuoO.js +14 -0
  61. package/public/assets/inertValue-L9VxV0VE.js +1 -0
  62. package/public/assets/layout.worker-CLcBHxVp.js +3 -0
  63. package/public/assets/{parser-worker-CA5RxqKc.js → parser-worker-CRIOPMOW.js} +8 -8
  64. package/public/assets/plan-graph-vendor-Ce6d4HU6.js +7 -0
  65. package/public/assets/react-vendor-C6KcUD36.js +9 -0
  66. package/public/assets/select-BHqOqun5.js +1 -0
  67. package/public/assets/toggle-DPeN6aNa.js +1 -0
  68. package/public/assets/toggle-group-Kl3uyHSr.js +1 -0
  69. package/public/assets/usePagedRows-BafD6bEN.js +1 -0
  70. package/public/assets/useRecentFiles-DzJZAoOT.js +3 -0
  71. package/public/assets/useRegisterFieldControl-pQv-ZDoU.js +1 -0
  72. package/public/assets/useScrollLock-BCxn_dMp.js +1 -0
  73. package/public/assets/useSortMode-DjrAN-z4.js +1 -0
  74. package/public/docs/404.html +3 -3
  75. package/public/docs/assets/{app.CnWL6bKV.js → app.1gvzd5Zb.js} +1 -1
  76. package/public/docs/assets/chunks/@localSearchIndexroot.DS2TQP5_.js +1 -0
  77. package/public/docs/assets/chunks/{VPLocalSearchBox.C0IBKeRQ.js → VPLocalSearchBox.CNxT3s0I.js} +1 -1
  78. package/public/docs/assets/chunks/{theme.BH-2HuRf.js → theme.C5lLqUv7.js} +2 -2
  79. package/public/docs/assets/contributor-guide_architecture_board-widgets.md.CqKM_jEG.js +1 -0
  80. package/public/docs/assets/contributor-guide_architecture_board-widgets.md.CqKM_jEG.lean.js +1 -0
  81. package/public/docs/assets/{contributor-guide_architecture_board-widgets_caching-and-memory.md.CQgn1t_M.js → contributor-guide_architecture_board-widgets_caching-and-memory.md.BYDo3ClR.js} +1 -1
  82. package/public/docs/assets/contributor-guide_architecture_board-widgets_plan-advisor.md.ajUAVdbc.js +1 -0
  83. package/public/docs/assets/{contributor-guide_architecture_board-widgets_visual-system-and-templating.md.wulOueeB.js → contributor-guide_architecture_board-widgets_visual-system-and-templating.md.WqY74T0T.js} +1 -1
  84. package/public/docs/assets/contributor-guide_architecture_board-widgets_visual-system-and-templating.md.WqY74T0T.lean.js +1 -0
  85. package/public/docs/assets/contributor-guide_architecture_detector-contract.md.CHHgywoP.js +1 -0
  86. package/public/docs/assets/{contributor-guide_architecture_detector-contract.md.N4hhenrA.lean.js → contributor-guide_architecture_detector-contract.md.CHHgywoP.lean.js} +1 -1
  87. package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.DIz3_oz9.js +1 -0
  88. package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.DIz3_oz9.lean.js +1 -0
  89. package/public/docs/assets/contributor-guide_architecture_detector-contract_threshold-tables.md.WH4tRHPL.js +1 -0
  90. package/public/docs/assets/{contributor-guide_architecture_detector-contract_threshold-tables.md.Dp3c40rx.lean.js → contributor-guide_architecture_detector-contract_threshold-tables.md.WH4tRHPL.lean.js} +1 -1
  91. package/public/docs/assets/{contributor-guide_architecture_drill-down.md.CqZPrdo9.js → contributor-guide_architecture_drill-down.md.yB-Hz2XT.js} +1 -1
  92. package/public/docs/assets/{contributor-guide_architecture_drill-down.md.CqZPrdo9.lean.js → contributor-guide_architecture_drill-down.md.yB-Hz2XT.lean.js} +1 -1
  93. package/public/docs/assets/contributor-guide_architecture_drill-down_plan-graph.md.BQZE04sw.js +1 -0
  94. package/public/docs/assets/{contributor-guide_architecture_drill-down_plan-graph.md.DwOozIEs.lean.js → contributor-guide_architecture_drill-down_plan-graph.md.BQZE04sw.lean.js} +1 -1
  95. package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.K71YB-Rr.js +1 -0
  96. package/public/docs/assets/{contributor-guide_architecture_impact-estimation.md.DfvbqEUu.lean.js → contributor-guide_architecture_impact-estimation.md.K71YB-Rr.lean.js} +1 -1
  97. package/public/docs/assets/contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.DLCAffwn.js +1 -0
  98. package/public/docs/assets/{contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.Cwm1jHmn.lean.js → contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.DLCAffwn.lean.js} +1 -1
  99. package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.JFrzDMwo.js +1 -0
  100. package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.JFrzDMwo.lean.js +1 -0
  101. package/public/docs/assets/contributor-guide_architecture_overview.md.nxkpOhgf.js +1 -0
  102. package/public/docs/assets/contributor-guide_architecture_state-and-history_run-comparison-internals.md.U-9KMDFK.js +1 -0
  103. package/public/docs/assets/{contributor-guide_architecture_state-and-history_run-comparison-internals.md.CZA0JLtV.lean.js → contributor-guide_architecture_state-and-history_run-comparison-internals.md.U-9KMDFK.lean.js} +1 -1
  104. package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.BrSz8Ezh.js +1 -0
  105. package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.B7rjSF_h.js +1 -0
  106. package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.B7rjSF_h.lean.js +1 -0
  107. package/public/docs/assets/{contributor-guide_architecture_widget-rendering_list-sort-mode.md.BuHKgH0C.js → contributor-guide_architecture_widget-rendering_list-sort-mode.md.37hcZJ7q.js} +1 -1
  108. package/public/docs/assets/{contributor-guide_architecture_widget-rendering_row-expand-contract.md.ZT89u9f1.js → contributor-guide_architecture_widget-rendering_row-expand-contract.md.mK_VmAWk.js} +1 -1
  109. package/public/docs/assets/{contributor-guide_architecture_worker-protocol.md.BAnhi2-d.js → contributor-guide_architecture_worker-protocol.md.Br6OE7RW.js} +1 -1
  110. package/public/docs/assets/contributor-guide_architecture_worker-protocol.md.Br6OE7RW.lean.js +1 -0
  111. package/public/docs/assets/contributor-guide_architecture_worker-protocol_cli-and-mcp.md.IVyaXrGe.js +1 -0
  112. package/public/docs/assets/{contributor-guide_architecture_worker-protocol_cli-and-mcp.md.BX1XnFbq.lean.js → contributor-guide_architecture_worker-protocol_cli-and-mcp.md.IVyaXrGe.lean.js} +1 -1
  113. package/public/docs/assets/{contributor-guide_architecture_worker-protocol_core-and-event-validation.md.DSVFPpWb.js → contributor-guide_architecture_worker-protocol_core-and-event-validation.md.DVSXn-bb.js} +1 -1
  114. package/public/docs/assets/{contributor-guide_architecture_worker-protocol_evidence-availability.md.CjimIDBY.js → contributor-guide_architecture_worker-protocol_evidence-availability.md.D3S5cyOW.js} +2 -2
  115. package/public/docs/assets/{contributor-guide_architecture_worker-protocol_evidence-availability.md.CjimIDBY.lean.js → contributor-guide_architecture_worker-protocol_evidence-availability.md.D3S5cyOW.lean.js} +1 -1
  116. package/public/docs/assets/contributor-guide_architecture_worker-protocol_evidence-report.md.CtmCU57I.js +1 -0
  117. package/public/docs/assets/{contributor-guide_architecture_worker-protocol_evidence-report.md.CiQl1MLb.lean.js → contributor-guide_architecture_worker-protocol_evidence-report.md.CtmCU57I.lean.js} +1 -1
  118. package/public/docs/assets/contributor-guide_contributing.md.DBW4drtZ.js +1 -0
  119. package/public/docs/assets/contributor-guide_development-setup.md.B21ZTDhn.js +15 -0
  120. package/public/docs/assets/contributor-guide_development-setup.md.B21ZTDhn.lean.js +1 -0
  121. package/public/docs/assets/contributor-guide_testing.md.DpkozNoG.js +4 -0
  122. package/public/docs/assets/{contributor-guide_testing.md.By6XGR9a.lean.js → contributor-guide_testing.md.DpkozNoG.lean.js} +1 -1
  123. package/public/docs/assets/{index.md.CHJVslga.js → index.md.lMm7SUMz.js} +1 -1
  124. package/public/docs/assets/{index.md.CHJVslga.lean.js → index.md.lMm7SUMz.lean.js} +1 -1
  125. package/public/docs/assets/{style.CyK9Ss73.css → style.Cfxqlj0y.css} +1 -1
  126. package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.C0pcQoNc.js +29 -0
  127. package/public/docs/assets/{user-guide_getting-started_ci-and-automation.md.BEepbDDL.lean.js → user-guide_getting-started_ci-and-automation.md.C0pcQoNc.lean.js} +1 -1
  128. package/public/docs/assets/user-guide_getting-started_reading-the-dashboard.md.CGaxPX_1.js +1 -0
  129. package/public/docs/assets/{user-guide_getting-started_tuning-thresholds.md.B_MyjyZm.js → user-guide_getting-started_tuning-thresholds.md.BGVE-wrc.js} +2 -2
  130. package/public/docs/assets/{user-guide_getting-started_tuning-thresholds.md.B_MyjyZm.lean.js → user-guide_getting-started_tuning-thresholds.md.BGVE-wrc.lean.js} +1 -1
  131. package/public/docs/assets/{user-guide_mcp-tools.md.C65SjWUL.js → user-guide_mcp-tools.md.49r3L2YE.js} +1 -1
  132. package/public/docs/assets/{user-guide_mcp-tools_compare-runs.md.Cc_gLoix.js → user-guide_mcp-tools_compare-runs.md.BQ_kIptC.js} +11 -6
  133. package/public/docs/assets/user-guide_mcp-tools_compare-runs.md.BQ_kIptC.lean.js +1 -0
  134. package/public/docs/assets/user-guide_mcp-tools_diagnose-run.md.azCZXkH8.js +44 -0
  135. package/public/docs/assets/{user-guide_mcp-tools_diagnose-run.md.BP1vnHA5.lean.js → user-guide_mcp-tools_diagnose-run.md.azCZXkH8.lean.js} +1 -1
  136. package/public/docs/assets/{user-guide_mcp-tools_evaluate-budgets.md.BB0nDY3Z.js → user-guide_mcp-tools_evaluate-budgets.md.dpQpRQ7z.js} +2 -2
  137. package/public/docs/assets/{user-guide_mcp-tools_evaluate-budgets.md.BB0nDY3Z.lean.js → user-guide_mcp-tools_evaluate-budgets.md.dpQpRQ7z.lean.js} +1 -1
  138. package/public/docs/assets/{user-guide_mcp-tools_get-finding-evidence.md.rKyu4wts.js → user-guide_mcp-tools_get-finding-evidence.md.CH6FkNLX.js} +2 -1
  139. package/public/docs/assets/{user-guide_mcp-tools_get-finding-evidence.md.rKyu4wts.lean.js → user-guide_mcp-tools_get-finding-evidence.md.CH6FkNLX.lean.js} +1 -1
  140. package/public/docs/assets/user-guide_run-comparison.md.B_LOkdBf.js +1 -0
  141. package/public/docs/assets/user-guide_run-comparison.md.B_LOkdBf.lean.js +1 -0
  142. package/public/docs/assets/user-guide_understanding-findings.md.C4riKCKl.js +1 -0
  143. package/public/docs/assets/user-guide_understanding-findings.md.C4riKCKl.lean.js +1 -0
  144. package/public/docs/contributor-guide/architecture/board-widgets/caching-and-memory.html +6 -6
  145. package/public/docs/contributor-guide/architecture/board-widgets/plan-advisor.html +6 -6
  146. package/public/docs/contributor-guide/architecture/board-widgets/visual-system-and-templating.html +6 -6
  147. package/public/docs/contributor-guide/architecture/board-widgets.html +6 -6
  148. package/public/docs/contributor-guide/architecture/detector-contract/plan-attribution.html +6 -6
  149. package/public/docs/contributor-guide/architecture/detector-contract/threshold-tables.html +6 -6
  150. package/public/docs/contributor-guide/architecture/detector-contract.html +6 -6
  151. package/public/docs/contributor-guide/architecture/drill-down/plan-graph.html +6 -6
  152. package/public/docs/contributor-guide/architecture/drill-down.html +6 -6
  153. package/public/docs/contributor-guide/architecture/impact-estimation/caveats-tuning-and-coverage.html +6 -6
  154. package/public/docs/contributor-guide/architecture/impact-estimation/stage-union-rollup.html +6 -6
  155. package/public/docs/contributor-guide/architecture/impact-estimation.html +6 -6
  156. package/public/docs/contributor-guide/architecture/index.html +5 -5
  157. package/public/docs/contributor-guide/architecture/overview.html +6 -6
  158. package/public/docs/contributor-guide/architecture/state-and-history/history-server-intake.html +5 -5
  159. package/public/docs/contributor-guide/architecture/state-and-history/run-comparison-internals.html +6 -6
  160. package/public/docs/contributor-guide/architecture/state-and-history.html +5 -5
  161. package/public/docs/contributor-guide/architecture/widget-rendering/findings-tab.html +6 -6
  162. package/public/docs/contributor-guide/architecture/widget-rendering/investigation-routing.html +5 -5
  163. package/public/docs/contributor-guide/architecture/widget-rendering/list-sort-mode.html +6 -6
  164. package/public/docs/contributor-guide/architecture/widget-rendering/reference-section.html +5 -5
  165. package/public/docs/contributor-guide/architecture/widget-rendering/row-expand-contract.html +6 -6
  166. package/public/docs/contributor-guide/architecture/widget-rendering.html +6 -6
  167. package/public/docs/contributor-guide/architecture/worker-protocol/cli-and-mcp.html +6 -6
  168. package/public/docs/contributor-guide/architecture/worker-protocol/core-and-event-validation.html +6 -6
  169. package/public/docs/contributor-guide/architecture/worker-protocol/evidence-availability.html +7 -7
  170. package/public/docs/contributor-guide/architecture/worker-protocol/evidence-report.html +6 -6
  171. package/public/docs/contributor-guide/architecture/worker-protocol.html +6 -6
  172. package/public/docs/contributor-guide/contributing.html +6 -6
  173. package/public/docs/contributor-guide/development-setup.html +8 -8
  174. package/public/docs/contributor-guide/testing.html +9 -6
  175. package/public/docs/hashmap.json +1 -1
  176. package/public/docs/index.html +6 -6
  177. package/public/docs/logo-dark.svg +1 -0
  178. package/public/docs/logo-light.svg +1 -0
  179. package/public/docs/tuning-reference/anti-patterns.html +5 -5
  180. package/public/docs/tuning-reference/aqe.html +5 -5
  181. package/public/docs/tuning-reference/bottleneck-broadcast-sizing.html +5 -5
  182. package/public/docs/tuning-reference/bottleneck-cold-start.html +5 -5
  183. package/public/docs/tuning-reference/bottleneck-duplicate-plan-subtree.html +5 -5
  184. package/public/docs/tuning-reference/bottleneck-failures.html +5 -5
  185. package/public/docs/tuning-reference/bottleneck-gc.html +5 -5
  186. package/public/docs/tuning-reference/bottleneck-job-failure-rate.html +5 -5
  187. package/public/docs/tuning-reference/bottleneck-memory-utilization.html +5 -5
  188. package/public/docs/tuning-reference/bottleneck-retry-waste.html +5 -5
  189. package/public/docs/tuning-reference/bottleneck-shuffle.html +5 -5
  190. package/public/docs/tuning-reference/bottleneck-skew.html +5 -5
  191. package/public/docs/tuning-reference/bottleneck-slow-host.html +5 -5
  192. package/public/docs/tuning-reference/bottleneck-small-files.html +5 -5
  193. package/public/docs/tuning-reference/bottleneck-spill.html +5 -5
  194. package/public/docs/tuning-reference/bottleneck-straggler.html +5 -5
  195. package/public/docs/tuning-reference/bottleneck-tiny-tasks.html +5 -5
  196. package/public/docs/tuning-reference/bottleneck-utilization.html +5 -5
  197. package/public/docs/tuning-reference/caching.html +5 -5
  198. package/public/docs/tuning-reference/cluster-config.html +5 -5
  199. package/public/docs/tuning-reference/config.html +5 -5
  200. package/public/docs/tuning-reference/data-formats.html +5 -5
  201. package/public/docs/tuning-reference/index.html +5 -5
  202. package/public/docs/tuning-reference/intro.html +5 -5
  203. package/public/docs/tuning-reference/joins.html +5 -5
  204. package/public/docs/tuning-reference/memory-model.html +5 -5
  205. package/public/docs/tuning-reference/metrics.html +5 -5
  206. package/public/docs/tuning-reference/partitioning.html +5 -5
  207. package/public/docs/tuning-reference/pyspark.html +5 -5
  208. package/public/docs/tuning-reference/shuffle.html +5 -5
  209. package/public/docs/tuning-reference/spark-architecture.html +5 -5
  210. package/public/docs/tuning-reference/table-formats.html +5 -5
  211. package/public/docs/user-guide/alternative-log-retrieval/managed-platforms.html +5 -5
  212. package/public/docs/user-guide/alternative-log-retrieval/ssh-bastion.html +5 -5
  213. package/public/docs/user-guide/alternative-log-retrieval.html +5 -5
  214. package/public/docs/user-guide/getting-started/ci-and-automation.html +16 -11
  215. package/public/docs/user-guide/getting-started/reading-the-dashboard.html +6 -6
  216. package/public/docs/user-guide/getting-started/regression-metric-keys.html +5 -5
  217. package/public/docs/user-guide/getting-started/tuning-thresholds.html +7 -7
  218. package/public/docs/user-guide/getting-started.html +5 -5
  219. package/public/docs/user-guide/mcp-tools/compare-runs.html +16 -11
  220. package/public/docs/user-guide/mcp-tools/diagnose-run.html +28 -7
  221. package/public/docs/user-guide/mcp-tools/evaluate-budgets.html +6 -6
  222. package/public/docs/user-guide/mcp-tools/get-finding-documentation.html +5 -5
  223. package/public/docs/user-guide/mcp-tools/get-finding-evidence.html +7 -6
  224. package/public/docs/user-guide/mcp-tools/get-reference-doc.html +5 -5
  225. package/public/docs/user-guide/mcp-tools/get-run-summary.html +5 -5
  226. package/public/docs/user-guide/mcp-tools/list-runs.html +5 -5
  227. package/public/docs/user-guide/mcp-tools.html +6 -6
  228. package/public/docs/user-guide/run-comparison/how-stages-are-matched.html +5 -5
  229. package/public/docs/user-guide/run-comparison.html +6 -6
  230. package/public/docs/user-guide/understanding-findings.html +6 -6
  231. package/public/export-template.html +45 -45
  232. package/public/index.html +8 -7
  233. package/public/sample-runs/sample-run.ndjson.gz +0 -0
  234. package/vendor-core/allocation.js +25 -23
  235. package/vendor-core/analyzer.js +85 -58
  236. package/vendor-core/api-merge-writes.js +2 -0
  237. package/vendor-core/aqe-skew.js +347 -0
  238. package/vendor-core/cli/budgets.js +6 -4
  239. package/vendor-core/cli/collect-run-pair.js +89 -0
  240. package/vendor-core/cli/collect-run-worker.js +16 -0
  241. package/vendor-core/cli/collect-run.js +1 -2
  242. package/vendor-core/cli/native-zstd.js +12 -16
  243. package/vendor-core/comparability.js +234 -0
  244. package/vendor-core/comparison-output.js +15 -7
  245. package/vendor-core/comparison-verdict.js +105 -25
  246. package/vendor-core/core-source-hash.txt +1 -1
  247. package/vendor-core/core-time-series.js +0 -1
  248. package/vendor-core/core-usage-locality.js +1 -1
  249. package/vendor-core/detectors.js +1007 -380
  250. package/vendor-core/docs-config.js +0 -4
  251. package/vendor-core/docs-content/detection/cache.md +2 -1
  252. package/vendor-core/docs-content/detection/cfg.md +19 -11
  253. package/vendor-core/docs-content/detection/cold.md +2 -1
  254. package/vendor-core/docs-content/detection/gc.md +7 -3
  255. package/vendor-core/docs-content/detection/host.md +11 -1
  256. package/vendor-core/docs-content/detection/mem.md +17 -6
  257. package/vendor-core/docs-content/detection/part.md +3 -1
  258. package/vendor-core/docs-content/detection/plan.md +55 -13
  259. package/vendor-core/docs-content/detection/shfl.md +11 -5
  260. package/vendor-core/docs-content/detection/skew.md +47 -1
  261. package/vendor-core/docs-content/detection/slow.md +4 -2
  262. package/vendor-core/docs-content/detection/spec.md +6 -1
  263. package/vendor-core/docs-content/detection/spill.md +6 -3
  264. package/vendor-core/docs-content/detection/strag.md +19 -6
  265. package/vendor-core/docs-content/detection/tiny.md +9 -3
  266. package/vendor-core/effective-conf.js +7 -10
  267. package/vendor-core/efficiency-model.js +2 -2
  268. package/vendor-core/etl-phases.js +1 -2
  269. package/vendor-core/event-handlers.js +175 -57
  270. package/vendor-core/event-schemas.js +21 -16
  271. package/vendor-core/evidence-availability.js +9 -2
  272. package/vendor-core/evidence-report.js +118 -35
  273. package/vendor-core/executor-peaks.js +41 -0
  274. package/vendor-core/export-data.js +5 -5
  275. package/vendor-core/finding-identity.js +78 -0
  276. package/vendor-core/finding-presentation.js +116 -33
  277. package/vendor-core/finding-types.js +88 -21
  278. package/vendor-core/format-utils.js +14 -15
  279. package/vendor-core/html-export.js +1 -1
  280. package/vendor-core/impact-band.js +2 -2
  281. package/vendor-core/impact-format.js +47 -19
  282. package/vendor-core/impact-model.js +26 -15
  283. package/vendor-core/job-groups.js +4 -4
  284. package/vendor-core/mcp-server-factory.js +48 -12
  285. package/vendor-core/mcp-tools.js +179 -24
  286. package/vendor-core/median.js +2 -2
  287. package/vendor-core/nested-loop-join.js +73 -0
  288. package/vendor-core/occupancy.js +1 -2
  289. package/vendor-core/parser-worker.js +1 -1
  290. package/vendor-core/plan-graph-model.js +14 -22
  291. package/vendor-core/plan-node-detail.js +2 -3
  292. package/vendor-core/plan-summary.js +24 -19
  293. package/vendor-core/plan-tree-walk.js +6 -8
  294. package/vendor-core/python-stage.js +12 -1
  295. package/vendor-core/recommendation-rollup.js +13 -12
  296. package/vendor-core/redact.js +52 -46
  297. package/vendor-core/run-aggregates.js +6 -8
  298. package/vendor-core/run-comparison.js +226 -66
  299. package/vendor-core/run-identity.js +12 -0
  300. package/vendor-core/run-interpretation.js +21 -0
  301. package/vendor-core/run-metrics.js +2 -2
  302. package/vendor-core/run-output.js +32 -10
  303. package/vendor-core/run-totals.js +12 -0
  304. package/vendor-core/run-verdict.js +76 -3
  305. package/vendor-core/scaling-sim.js +3 -4
  306. package/vendor-core/snappy-block.js +2 -2
  307. package/vendor-core/spark-conf.js +168 -0
  308. package/vendor-core/sql-stages.js +2 -2
  309. package/vendor-core/stage-alignment.js +2 -12
  310. package/vendor-core/stage-identity.js +58 -3
  311. package/vendor-core/stage-label.js +14 -0
  312. package/vendor-core/stage-plan-nodes.js +17 -3
  313. package/vendor-core/stage-quantiles.js +141 -9
  314. package/vendor-core/stage-structure.js +17 -3
  315. package/vendor-core/threshold-overrides.js +9 -3
  316. package/vendor-core/types.js +58 -21
  317. package/vendor-core/wasted-core-hours.js +0 -1
  318. package/vendor-core/write-targets.js +114 -14
  319. package/vendor-core/zstd-worker-client.js +1 -1
  320. package/vendor-core/zstd-worker.js +1 -1
  321. package/public/assets/AutoscalingChurn-CRA2Mb7F.js +0 -1
  322. package/public/assets/CacheUtilization-Bh9OY_V9.js +0 -1
  323. package/public/assets/CachingOpportunity-oCiQwmTJ.js +0 -1
  324. package/public/assets/ColdStart-BJrtjfmn.js +0 -1
  325. package/public/assets/ConfigAudit-DMvKxySP.js +0 -1
  326. package/public/assets/CoreUsageArea-REC2zsKw.js +0 -1
  327. package/public/assets/Dashboard-BHZ76JCu.js +0 -2
  328. package/public/assets/DisclosureContext-OO7X1HAx.js +0 -1
  329. package/public/assets/DuplicatePlanSubtree-BgG-L-cK.js +0 -1
  330. package/public/assets/ExecutorUtilization-D2KhSVbY.js +0 -1
  331. package/public/assets/GcPressure-D1BTE8qh.js +0 -1
  332. package/public/assets/IncompleteRun-DD40xUpy.js +0 -1
  333. package/public/assets/JobFailures-YwNzCF19.js +0 -1
  334. package/public/assets/MemoryUtilization-9b8ee_3_.js +0 -1
  335. package/public/assets/OverBroadcast-CfctS8a8.js +0 -1
  336. package/public/assets/PartitionHint-CUvbnnxy.js +0 -1
  337. package/public/assets/PartitionSizing-VG8kBgG5.js +0 -1
  338. package/public/assets/PlanExplorer-DmubTAOP.js +0 -1
  339. package/public/assets/PlanGraphRoute-DctPZjS0.js +0 -1
  340. package/public/assets/RetryWaste-CnpM8bB7.js +0 -1
  341. package/public/assets/ShuffleIO-D13vGmas.js +0 -1
  342. package/public/assets/Skew-DlagstYP.js +0 -1
  343. package/public/assets/SlowHost-cSkLRZ_e.js +0 -1
  344. package/public/assets/SmallFiles-B-kERMcq.js +0 -1
  345. package/public/assets/SpeculationWaste-DaysAzXO.js +0 -1
  346. package/public/assets/Spill-H44Gz4US.js +0 -1
  347. package/public/assets/StageFailed-DCfii9h_.js +0 -1
  348. package/public/assets/StageFindingGroup-BXJ0Mj5n.js +0 -1
  349. package/public/assets/StageHeader--oN2J-oD.js +0 -1
  350. package/public/assets/StageShape-IZ80CZiJ.js +0 -1
  351. package/public/assets/StageSlowness--BRIaBc1.js +0 -1
  352. package/public/assets/Straggler-Ctq0Df_o.js +0 -1
  353. package/public/assets/TaskFailures-Bks_gqhL.js +0 -1
  354. package/public/assets/TinyTask-BssfzGFB.js +0 -1
  355. package/public/assets/UnderBroadcast-DsScrxc-.js +0 -1
  356. package/public/assets/charts-vendor-0VMaLVLN.js +0 -59
  357. package/public/assets/finding-anchor-BYW1oz5R.js +0 -1
  358. package/public/assets/impact-sort-xzy0g-Vh.js +0 -1
  359. package/public/assets/index-DhpRGMZc.css +0 -2
  360. package/public/assets/index-DwqkFUFk.js +0 -14
  361. package/public/assets/plan-graph-vendor-BYl-AzN5.js +0 -7
  362. package/public/assets/react-vendor-DfpuPQZf.js +0 -9
  363. package/public/assets/select-C8edjYTq.js +0 -1
  364. package/public/assets/usePagedRows-CsuUFeYx.js +0 -1
  365. package/public/assets/useRecentFiles-BvkKo3JN.js +0 -11
  366. package/public/assets/useSortMode-joX71BZN.js +0 -1
  367. package/public/docs/assets/chunks/@localSearchIndexroot.tE5pz54z.js +0 -1
  368. package/public/docs/assets/contributor-guide_architecture_board-widgets.md.YQcj5EI4.js +0 -1
  369. package/public/docs/assets/contributor-guide_architecture_board-widgets.md.YQcj5EI4.lean.js +0 -1
  370. package/public/docs/assets/contributor-guide_architecture_board-widgets_plan-advisor.md.RlzvUmNm.js +0 -1
  371. package/public/docs/assets/contributor-guide_architecture_board-widgets_visual-system-and-templating.md.wulOueeB.lean.js +0 -1
  372. package/public/docs/assets/contributor-guide_architecture_detector-contract.md.N4hhenrA.js +0 -1
  373. package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.yNex4bys.js +0 -1
  374. package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.yNex4bys.lean.js +0 -1
  375. package/public/docs/assets/contributor-guide_architecture_detector-contract_threshold-tables.md.Dp3c40rx.js +0 -1
  376. package/public/docs/assets/contributor-guide_architecture_drill-down_plan-graph.md.DwOozIEs.js +0 -1
  377. package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.DfvbqEUu.js +0 -1
  378. package/public/docs/assets/contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.Cwm1jHmn.js +0 -1
  379. package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.DdCGtAGu.js +0 -1
  380. package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.DdCGtAGu.lean.js +0 -1
  381. package/public/docs/assets/contributor-guide_architecture_overview.md.C1sSvm9i.js +0 -1
  382. package/public/docs/assets/contributor-guide_architecture_state-and-history_run-comparison-internals.md.CZA0JLtV.js +0 -1
  383. package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.BOZ1azXG.js +0 -1
  384. package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.BQXycK6G.js +0 -1
  385. package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.BQXycK6G.lean.js +0 -1
  386. package/public/docs/assets/contributor-guide_architecture_worker-protocol.md.BAnhi2-d.lean.js +0 -1
  387. package/public/docs/assets/contributor-guide_architecture_worker-protocol_cli-and-mcp.md.BX1XnFbq.js +0 -1
  388. package/public/docs/assets/contributor-guide_architecture_worker-protocol_evidence-report.md.CiQl1MLb.js +0 -1
  389. package/public/docs/assets/contributor-guide_contributing.md.CQMvGwFt.js +0 -1
  390. package/public/docs/assets/contributor-guide_development-setup.md.DQdzera7.js +0 -15
  391. package/public/docs/assets/contributor-guide_development-setup.md.DQdzera7.lean.js +0 -1
  392. package/public/docs/assets/contributor-guide_testing.md.By6XGR9a.js +0 -1
  393. package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.BEepbDDL.js +0 -24
  394. package/public/docs/assets/user-guide_getting-started_reading-the-dashboard.md.Obf3W3__.js +0 -1
  395. package/public/docs/assets/user-guide_mcp-tools_compare-runs.md.Cc_gLoix.lean.js +0 -1
  396. package/public/docs/assets/user-guide_mcp-tools_diagnose-run.md.BP1vnHA5.js +0 -23
  397. package/public/docs/assets/user-guide_run-comparison.md.BUALkohi.js +0 -1
  398. package/public/docs/assets/user-guide_run-comparison.md.BUALkohi.lean.js +0 -1
  399. package/public/docs/assets/user-guide_understanding-findings.md.3C7KCEZO.js +0 -1
  400. package/public/docs/assets/user-guide_understanding-findings.md.3C7KCEZO.lean.js +0 -1
  401. /package/public/docs/assets/{contributor-guide_architecture_board-widgets_caching-and-memory.md.CQgn1t_M.lean.js → contributor-guide_architecture_board-widgets_caching-and-memory.md.BYDo3ClR.lean.js} +0 -0
  402. /package/public/docs/assets/{contributor-guide_architecture_board-widgets_plan-advisor.md.RlzvUmNm.lean.js → contributor-guide_architecture_board-widgets_plan-advisor.md.ajUAVdbc.lean.js} +0 -0
  403. /package/public/docs/assets/{contributor-guide_architecture_overview.md.C1sSvm9i.lean.js → contributor-guide_architecture_overview.md.nxkpOhgf.lean.js} +0 -0
  404. /package/public/docs/assets/{contributor-guide_architecture_widget-rendering.md.BOZ1azXG.lean.js → contributor-guide_architecture_widget-rendering.md.BrSz8Ezh.lean.js} +0 -0
  405. /package/public/docs/assets/{contributor-guide_architecture_widget-rendering_list-sort-mode.md.BuHKgH0C.lean.js → contributor-guide_architecture_widget-rendering_list-sort-mode.md.37hcZJ7q.lean.js} +0 -0
  406. /package/public/docs/assets/{contributor-guide_architecture_widget-rendering_row-expand-contract.md.ZT89u9f1.lean.js → contributor-guide_architecture_widget-rendering_row-expand-contract.md.mK_VmAWk.lean.js} +0 -0
  407. /package/public/docs/assets/{contributor-guide_architecture_worker-protocol_core-and-event-validation.md.DSVFPpWb.lean.js → contributor-guide_architecture_worker-protocol_core-and-event-validation.md.DVSXn-bb.lean.js} +0 -0
  408. /package/public/docs/assets/{contributor-guide_contributing.md.CQMvGwFt.lean.js → contributor-guide_contributing.md.DBW4drtZ.lean.js} +0 -0
  409. /package/public/docs/assets/{user-guide_getting-started_reading-the-dashboard.md.Obf3W3__.lean.js → user-guide_getting-started_reading-the-dashboard.md.CGaxPX_1.lean.js} +0 -0
  410. /package/public/docs/assets/{user-guide_mcp-tools.md.C65SjWUL.lean.js → user-guide_mcp-tools.md.49r3L2YE.lean.js} +0 -0
@@ -0,0 +1,234 @@
1
+ // The facts that decide whether two runs can be read against each other: what configuration and
2
+ // plan they share, how much data they handled, the capacity each had, and how those figures
3
+ // bridge the wall-clock difference. Built once per comparison, so every surface reports the same.
4
+ import { allocatedCoreMs, computeAllocation, lastObservedTimestamp } from './allocation.js';
5
+ import { buildEffectiveConf } from './effective-conf.js';
6
+ import { MAX_NORMALIZE_PATTERN_LENGTH, MAX_NORMALIZE_PATTERNS } from './stage-alignment.js';
7
+ import { totalOutputRows, withEarlierAttempts } from './run-totals.js';
8
+
9
+
10
+
11
+
12
+ /** A configuration key whose value differs between the runs. A side that does not set the key is
13
+ * null. */
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+ /** One data figure of both runs. `relativeDifference` is (candidate - baseline) / baseline, null
38
+ * when either side is unmeasured or the baseline is 0 and the candidate is not. */
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+
48
+
49
+
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+ /** wall clock = task time / (mean executors x cores x task time's share of allocated core time),
58
+ * so for runs with equal executor cores the wall-clock ratio is the task-time ratio over the
59
+ * product of the other two. `explainedWallClock` is that quotient. */
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+
86
+
87
+
88
+
89
+ /** Keys the cluster sets per run (application id, start time, driver address). Two runs of one job
90
+ * always differ here, so they never count as a configuration difference. */
91
+ const RUN_IDENTITY_CONF = /^spark\.(app\.(id|startTime|submitTime)|driver\.(host|port)|yarn\.app\.id|ui\.port)$/;
92
+
93
+ /** Compiles the caller's ignore patterns, matched against configuration keys. Throws on a
94
+ * non-string or empty pattern, an over-long one, an invalid one, or too many. */
95
+ export function compileConfIgnorePatterns(patterns ) {
96
+ if (!patterns || patterns.length === 0) return [];
97
+ if (patterns.length > MAX_NORMALIZE_PATTERNS) {
98
+ throw new Error(`At most ${MAX_NORMALIZE_PATTERNS} ignore patterns are accepted, got ${patterns.length}.`);
99
+ }
100
+ return patterns.map((source) => {
101
+ if (typeof source !== 'string' || source.length === 0) throw new Error('An ignore pattern must be a non-empty string.');
102
+ if (source.length > MAX_NORMALIZE_PATTERN_LENGTH) {
103
+ throw new Error(`Ignore pattern "${source.slice(0, 40)}..." is ${source.length} characters; the limit is ${MAX_NORMALIZE_PATTERN_LENGTH}.`);
104
+ }
105
+ try {
106
+ return new RegExp(source);
107
+ } catch (e) {
108
+ throw new Error(`Ignore pattern "${source}" is not a valid regular expression: ${(e ).message}`);
109
+ }
110
+ });
111
+ }
112
+
113
+ function compareConf(baseSnap , candSnap , ignore , userPattern ) {
114
+ const base = buildEffectiveConf(baseSnap.app, { userPattern });
115
+ const cand = buildEffectiveConf(candSnap.app, { userPattern });
116
+ if (base == null || cand == null) return { equal: null, differences: [], ignoredKeys: [], withheldKeys: [] };
117
+ const withheld = new Set([...base.maskedKeys, ...cand.maskedKeys]);
118
+ const keys = [...new Set([...Object.keys(base.values), ...Object.keys(cand.values)])].filter((key) => !withheld.has(key)).sort();
119
+ const differences = [];
120
+ const ignoredKeys = [];
121
+ for (const key of keys) {
122
+ const baseline = base.values[key] ?? null;
123
+ const candidate = cand.values[key] ?? null;
124
+ if (baseline === candidate) continue;
125
+ if (RUN_IDENTITY_CONF.test(key) || ignore.some((pattern) => pattern.test(key))) ignoredKeys.push(key);
126
+ else differences.push({ key, baseline, candidate });
127
+ }
128
+ return { equal: differences.length === 0, differences, ignoredKeys, withheldKeys: [...withheld].sort() };
129
+ }
130
+
131
+ function comparePlan(alignment ) {
132
+ const unmatched = { baseline: alignment.unmatched.baseStageIds.length, candidate: alignment.unmatched.candStageIds.length };
133
+ return {
134
+ equal: unmatched.baseline === 0 && unmatched.candidate === 0 && alignment.replanned.length === 0,
135
+ unmatched,
136
+ replanned: alignment.replanned.length,
137
+ };
138
+ }
139
+
140
+ function relativeDifference(baseline , candidate ) {
141
+ if (baseline == null || candidate == null) return null;
142
+ if (baseline === 0) return candidate === 0 ? 0 : null;
143
+ return (candidate - baseline) / Math.abs(baseline);
144
+ }
145
+
146
+ function figure(baseline , candidate ) {
147
+ return { baseline, candidate, relativeDifference: relativeDifference(baseline, candidate) };
148
+ }
149
+
150
+ /** Whether two measured values sit within `noiseBand` of the baseline. A baseline of 0 only
151
+ * matches a candidate of 0. */
152
+ export function withinBand(baseline , candidate , noiseBand ) {
153
+ if (baseline === 0) return candidate === 0;
154
+ return Math.abs(candidate - baseline) / Math.abs(baseline) < noiseBand;
155
+ }
156
+
157
+ function compareVolume(
158
+ baseStages , candStages , metrics , noiseBand ,
159
+ ) {
160
+ const row = (key ) => metrics.find((m) => m.key === key);
161
+ const inputBytes = figure(row('inputBytes')?.baseline ?? null, row('inputBytes')?.candidate ?? null);
162
+ const outputBytes = figure(row('outputBytes')?.baseline ?? null, row('outputBytes')?.candidate ?? null);
163
+ const outputRows = figure(totalOutputRows(baseStages), totalOutputRows(candStages));
164
+ const verdicts = [];
165
+ for (const fig of [inputBytes, outputBytes]) {
166
+ if (fig.baseline != null && fig.candidate != null) verdicts.push(withinBand(fig.baseline, fig.candidate, noiseBand));
167
+ }
168
+ if (outputRows.baseline != null && outputRows.candidate != null) verdicts.push(outputRows.baseline === outputRows.candidate);
169
+ const outputMeasured = [outputRows, outputBytes].some((fig) => fig.baseline != null && fig.candidate != null);
170
+ return { inputBytes, outputRows, outputBytes, equal: !outputMeasured ? null : verdicts.every(Boolean) };
171
+ }
172
+
173
+ function ratio(baseline , candidate ) {
174
+ return baseline == null || candidate == null || baseline === 0 ? null : candidate / baseline;
175
+ }
176
+
177
+ function factor(baseline , candidate ) {
178
+ return { baseline, candidate, ratio: ratio(baseline, candidate) };
179
+ }
180
+
181
+ function runWindow(snap ) {
182
+ const start = snap.app?.startTime;
183
+ const end = snap.app?.endTime ?? (snap.executors ? lastObservedTimestamp(snap) : null);
184
+ return start != null && end != null && end >= start ? [start, end] : null;
185
+ }
186
+
187
+ /** The comparability facts of a comparison. `metrics` are the comparison's own rows, so the
188
+ * volume and bridge figures are the ones the metrics table shows. */
189
+ export function buildComparability(
190
+ baseSnap , candSnap ,
191
+ alignment ,
192
+ metrics ,
193
+ { ignoreConf, confRedactRegex, noiseBand } ,
194
+ ) {
195
+ const attempts = (snap ) => withEarlierAttempts([...snap.stages.values()]);
196
+ const allocation = (snap ) => (snap.executors ? computeAllocation(snap) : null);
197
+ const baseAlloc = allocation(baseSnap), candAlloc = allocation(candSnap);
198
+ const row = (key ) => metrics.find((m) => m.key === key);
199
+ const share = (snap , runTimeMs ) => {
200
+ const allocated = snap.executors ? allocatedCoreMs(snap) : null;
201
+ return runTimeMs != null && allocated != null ? runTimeMs / allocated : null;
202
+ };
203
+ const taskTime = factor(row('executorRunTime')?.baseline ?? null, row('executorRunTime')?.candidate ?? null);
204
+ const meanExecutors = factor(baseAlloc?.executorsMean ?? null, candAlloc?.executorsMean ?? null);
205
+ const taskShare = factor(share(baseSnap, taskTime.baseline), share(candSnap, taskTime.candidate));
206
+ const parts = [taskTime.ratio, meanExecutors.ratio, taskShare.ratio];
207
+ const explainedWallClock = parts.every((r) => r != null && r > 0)
208
+ ? parts[0] / (parts[1] * parts[2] )
209
+ : null;
210
+
211
+ const baseWindow = runWindow(baseSnap), candWindow = runWindow(candSnap);
212
+ const overlapMs = baseWindow && candWindow
213
+ ? Math.max(0, Math.min(baseWindow[1], candWindow[1]) - Math.max(baseWindow[0], candWindow[0]))
214
+ : null;
215
+
216
+ return {
217
+ noiseBand,
218
+ conf: compareConf(baseSnap, candSnap, compileConfIgnorePatterns(ignoreConf), confRedactRegex),
219
+ plan: comparePlan(alignment),
220
+ volume: compareVolume(attempts(baseSnap), attempts(candSnap), metrics, noiseBand),
221
+ capacity: {
222
+ baseline: { meanExecutors: baseAlloc?.executorsMean ?? null, peakExecutors: baseAlloc?.executorsPeak ?? null },
223
+ candidate: { meanExecutors: candAlloc?.executorsMean ?? null, peakExecutors: candAlloc?.executorsPeak ?? null },
224
+ },
225
+ overlapMs,
226
+ wallClockBridge: {
227
+ wallClock: factor(row('wallClock')?.baseline ?? null, row('wallClock')?.candidate ?? null),
228
+ taskTime,
229
+ meanExecutors,
230
+ taskShareOfAllocation: taskShare,
231
+ explainedWallClock,
232
+ },
233
+ };
234
+ }
@@ -1,6 +1,7 @@
1
1
  import { buildComparison, } from './run-comparison.js';
2
2
  import { comparisonVerdict, } from './comparison-verdict.js';
3
3
  import { redactComparison } from './redact.js';
4
+
4
5
 
5
6
 
6
7
 
@@ -8,10 +9,11 @@ import { redactComparison } from './redact.js';
8
9
  // The comparison a surface reports: the CLI's `comparison` object and the shared part of MCP
9
10
  // compare_runs. A field added here reaches every surface.
10
11
  // `stagePairs` is the one large field (a row per paired stage), so the `summary` view leaves it out.
11
-
12
+ // `generator` is the build that wrote the output, present when the surface supplies it.
13
+
12
14
 
13
-
14
-
15
+
16
+
15
17
 
16
18
 
17
19
  /** `full` carries every field (the CLI); `summary` leaves out `stagePairs` (MCP, which returns them
@@ -19,9 +21,14 @@ import { redactComparison } from './redact.js';
19
21
 
20
22
 
21
23
  /** The reported projection of a comparison. Key order is the CLI's JSON order. */
22
- export function comparisonOutput(comparison , view = 'full') {
24
+ export function comparisonOutput(
25
+ comparison , view = 'full', generator ,
26
+ ) {
23
27
  return {
28
+ ...(generator ? { generator } : {}),
24
29
  verdict: comparisonVerdict(comparison),
30
+ baselineApp: comparison.baselineApp,
31
+ candidateApp: comparison.candidateApp,
25
32
  confidence: comparison.confidence,
26
33
  reason: comparison.reason,
27
34
  matchedCoverage: comparison.matchedCoverage,
@@ -34,6 +41,7 @@ export function comparisonOutput(comparison , view
34
41
  replanned: comparison.replanned,
35
42
  bookkeepingStageIds: comparison.bookkeepingStageIds,
36
43
  executionAlignment: comparison.executionAlignment,
44
+ comparability: comparison.comparability,
37
45
  };
38
46
  }
39
47
 
@@ -42,9 +50,9 @@ export function comparisonOutput(comparison , view
42
50
  * and Markdown; `output` is the projection to serialize. */
43
51
  export function buildComparisonOutput(
44
52
  baseline , candidate ,
45
- { redact, normalizePath, view } = {},
53
+ { redact, normalizePath, ignoreConf, confRedactRegex, noiseBand, view, generator } = {},
46
54
  ) {
47
- const built = buildComparison(baseline, candidate, { normalizePath });
55
+ const built = buildComparison(baseline, candidate, { normalizePath, ignoreConf, confRedactRegex, noiseBand });
48
56
  const comparison = redact ? redactComparison(built) : built;
49
- return { comparison, output: comparisonOutput(comparison, view) };
57
+ return { comparison, output: comparisonOutput(comparison, view, generator) };
50
58
  }
@@ -1,6 +1,7 @@
1
1
  import { formatDuration, typeTag } from './format-utils.js';
2
2
  import { TAG_HELP } from './finding-tag-help.js';
3
- import { NEUTRAL_METRIC_KEYS, } from './run-comparison.js';
3
+ import { DEFAULT_NOISE_BAND, isEscalation, NEUTRAL_METRIC_KEYS, } from './run-comparison.js';
4
+
4
5
 
5
6
  /** The slice of a comparison metric row the verdict reads. */
6
7
 
@@ -10,15 +11,27 @@ import { NEUTRAL_METRIC_KEYS, } from './run-comparison.js
10
11
 
11
12
 
12
13
 
14
+
15
+
13
16
 
14
17
 
15
- /** The slice of a finding-category delta the verdict reads. */
18
+ /** The slice of the comparability block the verdict reads. */
19
+
20
+
21
+ /** The slice of an introduced or resolved finding row the verdict reads. */
16
22
 
17
23
 
18
24
 
19
25
 
20
26
 
21
27
 
28
+ /** The slice of a changed finding row the verdict reads. */
29
+
30
+
31
+
32
+
33
+
34
+
22
35
  /** How many of a run's ended jobs failed, counted as `summarizeRunOutcome`
23
36
  * counts them. */
24
37
 
@@ -29,7 +42,9 @@ import { NEUTRAL_METRIC_KEYS, } from './run-comparison.js
29
42
 
30
43
 
31
44
 
32
-
45
+ /** `equivalent`: configuration, plan and output agree, so a time difference is a difference in
46
+ * capacity or scheduling. `mixed`: wall clock and cost moved opposite ways. */
47
+
33
48
 
34
49
 
35
50
 
@@ -40,17 +55,58 @@ import { NEUTRAL_METRIC_KEYS, } from './run-comparison.js
40
55
  /** A change under this share of the baseline's value reads as "about the same", for
41
56
  * run time and cost metrics alike: run-to-run noise on a shared cluster easily
42
57
  * moves a job a percent or two. */
43
- export const SAME_CHANGE_SHARE = 0.02;
58
+ export const SAME_CHANGE_SHARE = DEFAULT_NOISE_BAND;
44
59
 
45
60
  function measured(metric ) {
46
61
  return metric.baseline != null && metric.candidate != null;
47
62
  }
48
63
 
49
64
  function movedPastNoise(metric ) {
65
+ if (metric.withinNoise !== undefined) return !metric.withinNoise;
50
66
  if (metric.baseline === 0) return metric.candidate !== 0;
51
67
  return Math.abs(metric.candidate - metric.baseline) / Math.abs(metric.baseline) >= SAME_CHANGE_SHARE;
52
68
  }
53
69
 
70
+ const percent = (share ) => `${Math.round(Math.abs(share) * 100)}%`;
71
+ const times = (ratio ) => `×${ratio.toFixed(2)}`;
72
+
73
+ /** The facts a time difference rests on: the factors of the wall-clock bridge that moved past the
74
+ * noise band, each stated for both runs. */
75
+ function bridgeReasons(comparability ) {
76
+ const { wallClockBridge: bridge, noiseBand } = comparability;
77
+ const moved = (ratio ) => ratio != null && Math.abs(ratio - 1) >= noiseBand;
78
+ const reasons = [];
79
+ const { meanExecutors, taskTime, taskShareOfAllocation: share } = bridge;
80
+ if (moved(meanExecutors.ratio)) reasons.push(`executors averaged ${meanExecutors.baseline .toFixed(1)} in the baseline and ${meanExecutors.candidate .toFixed(1)} in the candidate`);
81
+ if (moved(taskTime.ratio)) reasons.push(`task time was ${formatDuration(taskTime.baseline )} in the baseline and ${formatDuration(taskTime.candidate )} in the candidate`);
82
+ if (moved(share.ratio)) reasons.push(`task time filled ${percent(share.baseline )} of allocated core time in the baseline and ${percent(share.candidate )} in the candidate`);
83
+ return reasons;
84
+ }
85
+
86
+ /** "Wall clock ×1.49 = task time ×0.82 / (mean executors ×0.57 × task share of allocation ×0.96)",
87
+ * candidate over baseline; null when a factor is unmeasured. */
88
+ function bridgeSentence({ wallClockBridge: bridge } ) {
89
+ const { wallClock, taskTime, meanExecutors, taskShareOfAllocation: share } = bridge;
90
+ if (wallClock.ratio == null || taskTime.ratio == null || meanExecutors.ratio == null || share.ratio == null) return null;
91
+ return `Candidate over baseline: wall clock ${times(wallClock.ratio)} is task time ${times(taskTime.ratio)} divided by mean executors ${times(meanExecutors.ratio)} times task time's share of allocated core time ${times(share.ratio)}.`;
92
+ }
93
+
94
+ function configAndPlanAgree({ conf, plan } ) {
95
+ return conf.equal === true && plan.equal;
96
+ }
97
+
98
+ /** The cost metrics that moved past noise against the direction wall clock moved, as phrases such
99
+ * as "executor run-time is 18% lower". Empty when wall clock stayed within noise. */
100
+ function opposingCost(wall , metrics ) {
101
+ if (!movedPastNoise(wall)) return [];
102
+ const wallUp = wall.candidate > wall.baseline;
103
+ return metrics
104
+ .filter((m) => m.key === 'executorRunTime' || m.key === 'allocatedCoreHours')
105
+ .filter(measured)
106
+ .filter((m) => m.baseline > 0 && movedPastNoise(m) && (m.candidate > m.baseline) !== wallUp)
107
+ .map((m) => `${m.label.toLowerCase()} is ${percent((m.candidate - m.baseline) / m.baseline)} ${m.candidate < m.baseline ? 'lower' : 'higher'}`);
108
+ }
109
+
54
110
  /** Plain name for a finding type ("Memory and disk spill"), falling back to
55
111
  * its tag when the tag has no help entry. */
56
112
  function categoryName(type ) {
@@ -58,13 +114,11 @@ function categoryName(type ) {
58
114
  return TAG_HELP[tag]?.expansion ?? tag;
59
115
  }
60
116
 
61
- /** Net count change per displayed category name. Categories are tallied per
62
- * (rule, impact band), and several rules share one name (every Plan Advisor
63
- * type reads "Plan advisor", every stage-shape sub-rule "Stage shape"), so a
64
- * rule whose findings moved from critical to warning, or two rules under one
65
- * name moving opposite ways, would otherwise
66
- * read as both "introduced" and "resolved". Order follows first appearance,
67
- * introduced before resolved. */
117
+ /** Net count change per displayed category name. Several rules share one name (every Plan Advisor
118
+ * type reads "Plan advisor", every stage-shape sub-rule "Stage shape"), so two rules under one
119
+ * name moving opposite ways net out rather than read as both "new" and "less frequent". Findings
120
+ * that only changed impact band are not in `introduced` or `resolved`, so they do not move a
121
+ * category. Order follows first appearance, introduced before resolved. */
68
122
  function netByCategory(findings ) {
69
123
  const net = new Map ();
70
124
  for (const item of [...findings.introduced, ...findings.resolved]) {
@@ -105,18 +159,18 @@ function failureHeadline(base , cand )
105
159
  }
106
160
 
107
161
  /** One plain answer to "did the candidate get better or worse than the baseline", from the
108
- * comparison's own whole-run metrics and finding-category tallies: run time
109
- * first, then which cost metrics moved each way past run-to-run noise, then which finding
110
- * categories appeared or went away. When either run had failed jobs, that
111
- * leads instead and run time becomes the first sentence. When either log is
112
- * incomplete, run time is stated as what each log covers, never as faster or
113
- * slower, and the tone stays neutral. Volume and count metrics (input, output,
114
- * tasks, executors) are left out because more or less of them is not
115
- * inherently better or worse. */
162
+ * comparison's whole-run metrics and finding-category tallies: run time, then the cost metrics
163
+ * that moved past run-to-run noise, then the finding categories that appeared or went away. Failed
164
+ * jobs lead when either run had any; an incomplete log states run time as what each log covers
165
+ * with a neutral tone. Volume and count metrics are left out (NEUTRAL_METRIC_KEYS). With the
166
+ * comparability block, runs that agree in configuration, plan and output read `equivalent`, and a
167
+ * wall clock that moved against the cost metrics reads `mixed`. The rules are in
168
+ * docs-site/user-guide/run-comparison.md. */
116
169
  export function summarizeComparison(
117
170
  metrics ,
118
- findings ,
171
+ findings ,
119
172
  jobs ,
173
+ comparability ,
120
174
  ) {
121
175
  const wall = metrics.find((metric) => metric.key === 'wallClock');
122
176
  let title = 'Run time could not be compared between the baseline and the candidate';
@@ -132,7 +186,7 @@ export function summarizeComparison(
132
186
  } else if (wall && wall.baseline != null && wall.baseline > 0 && wall.candidate != null) {
133
187
  const change = wall.candidate - wall.baseline;
134
188
  const share = change / wall.baseline;
135
- if (Math.abs(share) < SAME_CHANGE_SHARE) {
189
+ if (!movedPastNoise({ ...wall, baseline: wall.baseline, candidate: wall.candidate })) {
136
190
  title = 'The candidate took about as long as the baseline';
137
191
  tone = 'same';
138
192
  } else {
@@ -142,12 +196,36 @@ export function summarizeComparison(
142
196
  }
143
197
  }
144
198
 
199
+ const sentences = [];
200
+ const failure = jobs ? failureHeadline(jobs.baseline, jobs.candidate) : null;
201
+ if (comparability && !failure && incompleteRuns.length === 0 && wall && measured(wall) && wall.baseline > 0) {
202
+ const change = wall.candidate - wall.baseline;
203
+ const drift = movedPastNoise(wall)
204
+ ? `Time differs by ${formatDuration(Math.abs(change))} (${percent(change / wall.baseline)})`
205
+ : 'The candidate took about as long as the baseline';
206
+ if (configAndPlanAgree(comparability) && comparability.volume.equal !== false) {
207
+ const outputAgrees = comparability.volume.equal === true;
208
+ const gap = movedPastNoise(wall);
209
+ const reasons = gap ? bridgeReasons(comparability) : [];
210
+ const lead = outputAgrees ? 'Same configuration, plan and output' : 'Same configuration and plan. Output was not compared';
211
+ const explanation = reasons.length > 0 ? `: ${reasons.join(', ')}` : gap ? ', and no measured factor explains it' : '';
212
+ title = `${lead}. ${drift}${explanation}.`;
213
+ if (outputAgrees && (!gap || reasons.length > 0)) tone = 'equivalent';
214
+ const bridge = gap ? bridgeSentence(comparability) : null;
215
+ if (bridge) sentences.push(bridge);
216
+ } else {
217
+ const opposed = opposingCost(wall, metrics);
218
+ if (opposed.length > 0) {
219
+ title = `The candidate finished ${formatDuration(Math.abs(change))} ${change < 0 ? 'faster' : 'slower'} than the baseline (${percent(change / wall.baseline)}), while ${opposed.join(' and ')}`;
220
+ tone = 'mixed';
221
+ }
222
+ }
223
+ }
224
+
145
225
  const cost = metrics.filter((metric) => metric.key !== 'wallClock' && !NEUTRAL_METRIC_KEYS.has(metric.key)).filter(measured);
146
226
  const moved = cost.filter(movedPastNoise);
147
227
  const worse = moved.filter((metric) => metric.direction === 'regression').map((metric) => metric.label);
148
228
  const better = moved.filter((metric) => metric.direction === 'improvement').map((metric) => metric.label);
149
- const sentences = [];
150
- const failure = jobs ? failureHeadline(jobs.baseline, jobs.candidate) : null;
151
229
  if (failure) {
152
230
  sentences.push(`${title}.`);
153
231
  title = failure.title;
@@ -167,11 +245,13 @@ export function summarizeComparison(
167
245
  const resolved = namesWhere(net, (change) => change < 0);
168
246
  if (introduced.length > 0) sentences.push(`New or more frequent in the candidate: ${introduced.join(', ')}.`);
169
247
  if (resolved.length > 0) sentences.push(`Less frequent in the candidate: ${resolved.join(', ')}.`);
248
+ const escalated = [...new Set((findings.changed ?? []).filter(isEscalation).map((c) => categoryName(c.type)))];
249
+ if (escalated.length > 0) sentences.push(`More severe in the candidate: ${escalated.join(', ')}.`);
170
250
  return { title, tone, sentences };
171
251
  }
172
252
 
173
253
  /** The comparison verdict for a core comparison result: the one the dashboard's comparison page
174
254
  * leads with, and the one the CLI's --baseline report and MCP compare_runs carry. */
175
- export function comparisonVerdict(result ) {
176
- return summarizeComparison(result.metrics, result.findings, result.jobOutcomes);
255
+ export function comparisonVerdict(result ) {
256
+ return summarizeComparison(result.metrics, result.findings, result.jobOutcomes, result.comparability);
177
257
  }
@@ -1 +1 @@
1
- 9c33274bcae34f425c7d625bdea9cfacdb400f333bc3a5ced9c86a0e75094916
1
+ 06b1ffacd20e0b5e1d7b6a19ed645fb088c6049114893b77cf4662261d610924
@@ -65,7 +65,6 @@ export function computeCoreTimeSeries(intervals , {
65
65
  return { mode: 'coreCount', histogram };
66
66
  }
67
67
 
68
- // bucketBy === 'time'
69
68
  if (segments.length === 0) {
70
69
  return { mode: 'time', bucketWidthMs, startTime: null, endTime: null, buckets: [] };
71
70
  }
@@ -105,7 +105,7 @@ export function buildLocalityChart(
105
105
  const point = { t: Math.round((t - start) / 1000) };
106
106
  // The series averages each bucket over its full width, so a last bucket
107
107
  // that runs past the series' own end (or a run shorter than one bucket)
108
- // reads diluted: a 10s stage in a 60s bucket showed well under its busy
108
+ // reads diluted: a 10s stage in a 60s bucket shows well under its busy
109
109
  // cores. Rescale to the part of the bucket the series actually covers.
110
110
  const coveredMs = Math.min(bucketWidthMs, seriesEnd - t);
111
111
  const scale = bucketWidthMs / coveredMs;