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
@@ -7,6 +7,7 @@ import {
7
7
  JobEndEventSchema,
8
8
  StageSubmittedEventSchema,
9
9
  StageExecutorMetricsEventSchema,
10
+ StageCompletedEventSchema,
10
11
  TaskEndEventSchema,
11
12
  SqlExecutionStartEventSchema,
12
13
  SqlAdaptiveExecutionUpdateEventSchema,
@@ -15,6 +16,7 @@ import {
15
16
  ExecutorAddedEventSchema,
16
17
  ExecutorRemovedEventSchema,
17
18
  BlockUpdatedEventSchema,
19
+ MAX_ACCUMULABLES_PER_TASK,
18
20
 
19
21
 
20
22
  } from './event-schemas.js';
@@ -24,6 +26,8 @@ import { finalizeStage } from './stage-quantiles.js';
24
26
  import { MAX_FAILURE_DETAILS_PER_STAGE, extractTaskFailureDetail, taskFailureKey, } from './task-failure.js';
25
27
  import { computeRunAggregates } from './run-aggregates.js';
26
28
  import { parseSparkMemoryMB } from './spark-memory.js';
29
+ import { effectiveSparkConf } from './spark-conf.js';
30
+ import { DRIVER_EXECUTOR_ID } from './executor-peaks.js';
27
31
 
28
32
 
29
33
 
@@ -51,6 +55,9 @@ import { parseSparkMemoryMB } from './spark-memory.js';
51
55
 
52
56
 
53
57
 
58
+
59
+
60
+
54
61
 
55
62
 
56
63
 
@@ -130,6 +137,8 @@ const MAX_TASK_SAMPLES = 20;
130
137
 
131
138
 
132
139
 
140
+
141
+
133
142
 
134
143
 
135
144
 
@@ -202,6 +211,8 @@ const MAX_TASK_SAMPLES = 20;
202
211
 
203
212
 
204
213
 
214
+
215
+
205
216
 
206
217
 
207
218
 
@@ -226,12 +237,21 @@ const MAX_TASK_SAMPLES = 20;
226
237
 
227
238
 
228
239
 
240
+
241
+
242
+
229
243
 
230
244
 
231
245
 
232
246
 
233
247
 
234
248
 
249
+
250
+
251
+
252
+
253
+
254
+
235
255
 
236
256
 
237
257
 
@@ -241,22 +261,18 @@ const MAX_TASK_SAMPLES = 20;
241
261
 
242
262
 
243
263
  // Splits decompressed byte chunks into NDJSON lines. Each chunk is decoded whole in one streaming
244
- // TextDecoder.decode call (per-line decode was ~8x slower: 227k calls vs ~36k on a 138MB / 3.5GB
245
- // log), then newlines are found with String.prototype.indexOf on that fresh, flat decoded text.
264
+ // TextDecoder.decode call (per-line decode is ~8x slower), then newlines are found with
265
+ // String.prototype.indexOf on that fresh, flat decoded text.
246
266
  // A '\n' char is exactly the 0x0A byte (0x0A never occurs inside a UTF-8 multibyte sequence), and
247
267
  // `{ stream: true }` reassembles a character split across a chunk boundary, so no byte-to-UTF-16
248
268
  // offset mapping is needed. Only the first line of a chunk joins the carried-over `pending` text.
249
269
  //
250
- // Measured 2026-09-23 against the previous raw-byte-scan version (identical output): 3.5s -> 2.8s
251
- // on that 3.5GB all-ASCII log, 0.37s -> 0.18s on 93MB of synthetic 2/3/4-byte-heavy NDJSON.
252
- //
253
270
  // A SQL UI event's physicalPlanDescription value (see stripPlanDescription) that is still open
254
271
  // when a chunk ends is cut off the pending line, and the following bytes are dropped up to its
255
- // closing quote without being decoded. On the largest real log that value is 1.9 GB of the
256
- // 3.5 GB stream, so it is never decoded, joined or scanned as text. A chunk longer than
257
- // MAX_DECODE_SLICE is decoded in slices of that size, so the skip also applies within one chunk:
258
- // Node's native zstd emits whole frames, up to 39 MB on that log, which held all but 51 MB of the
259
- // value inside a single chunk.
272
+ // closing quote without being decoded, so the value is never decoded, joined or scanned as text.
273
+ // A chunk longer than MAX_DECODE_SLICE is decoded in slices of that size, so the skip also applies
274
+ // within one chunk: Node's native zstd emits whole frames that can hold a value entirely.
275
+ // Figures: worker-protocol.md, "Decoder and parser calibration".
260
276
  const MAX_DECODE_SLICE = 512 * 1024;
261
277
 
262
278
  // A line joined from text decoded in more than one slice is a V8 cons string, which the first
@@ -438,10 +454,13 @@ export function createState() {
438
454
  skippedLines: 0,
439
455
  unreadableSqlStarts: new Set(),
440
456
  accumState: new Map(),
457
+ executorAccumState: new Map(),
441
458
  rddInfo: new Map(),
442
459
  rddBlocks: new Map(),
443
460
  rddBlockUpdates: 0,
444
461
  taskAccumStages: new Map(),
462
+ lastTaskAccumIds: new Map(),
463
+ executorPeakMetrics: new Map(),
445
464
  pendingAdaptiveUpdates: new Map(),
446
465
  resolvedPlanExecutions: new Set(),
447
466
  evidenceInputs: {
@@ -480,36 +499,45 @@ export function normalizeSparkProperties(
480
499
 
481
500
  export { parseSparkMemoryMB };
482
501
 
483
- // Derive an allocated-resource summary from the Spark config map. Absent keys degrade to null,
484
- // not guessed defaults.
485
- export function extractResources(config ) {
502
+ // Derive an allocated-resource summary from the Spark config map, each setting as the run's
503
+ // effective conf has it (the logged value, else Spark's default for `sparkVersion`). A key with no
504
+ // fixed default degrades to null, not a guess, and so does every key of a log that recorded no
505
+ // Spark properties at all: there is nothing to tell a default from a missing config.
506
+ export function extractResources(config , sparkVersion ) {
486
507
  const cfg = config ?? {};
508
+ const recorded = Object.keys(cfg).length > 0;
509
+ const value = (k ) => (recorded ? effectiveSparkConf({ sparkVersion, properties: cfg }, k)?.value : undefined);
487
510
  const int = (k ) => {
488
- if (cfg[k] == null) return null;
489
- const n = parseInt(cfg[k], 10);
511
+ const v = value(k);
512
+ if (v == null) return null;
513
+ const n = parseInt(v, 10);
490
514
  return Number.isFinite(n) ? n : null;
491
515
  };
492
- const memMB = (k ) => (cfg[k] != null ? parseSparkMemoryMB(cfg[k]) : null);
493
- const bool = (k ) => (cfg[k] != null ? String(cfg[k]).toLowerCase() === 'true' : null);
516
+ const memMB = (k ) => parseSparkMemoryMB(value(k));
517
+ const bool = (k ) => { const v = value(k); return v != null ? v.toLowerCase() === 'true' : null; };
494
518
  return {
495
519
  executor: {
520
+ // The executor size stays the logged one: Spark's 1g default does not apply to a local-mode run,
521
+ // which has no separate executor JVM, and the log does not say which master it ran under.
496
522
  memory: cfg['spark.executor.memory'] ?? null,
497
- memoryMB: memMB('spark.executor.memory'),
498
- memoryOverhead: cfg['spark.executor.memoryOverhead'] ?? null,
523
+ memoryMB: cfg['spark.executor.memory'] != null ? parseSparkMemoryMB(cfg['spark.executor.memory']) : null,
524
+ memoryOverhead: value('spark.executor.memoryOverhead') ?? null,
499
525
  memoryOverheadMB: memMB('spark.executor.memoryOverhead'),
500
526
  cores: int('spark.executor.cores'),
501
527
  instances: int('spark.executor.instances'),
502
528
  },
503
529
  driver: {
504
- memory: cfg['spark.driver.memory'] ?? null,
530
+ memory: value('spark.driver.memory') ?? null,
505
531
  memoryMB: memMB('spark.driver.memory'),
506
- memoryOverhead: cfg['spark.driver.memoryOverhead'] ?? null,
532
+ memoryOverhead: value('spark.driver.memoryOverhead') ?? null,
507
533
  memoryOverheadMB: memMB('spark.driver.memoryOverhead'),
508
534
  cores: int('spark.driver.cores'),
509
535
  },
510
536
  dynamicAllocationEnabled: bool('spark.dynamicAllocation.enabled'),
511
537
  shuffleServiceEnabled: bool('spark.shuffle.service.enabled'),
538
+ // The serializer a run set: Spark's default is reported as unset.
512
539
  serializer: cfg['spark.serializer'] ?? null,
540
+ stageExecutorMetricsLogging: bool('spark.eventLog.logStageExecutorMetrics'),
513
541
  };
514
542
  }
515
543
 
@@ -600,12 +628,45 @@ function taskRecordOf(event , failure
600
628
  executorRunTime: m['Executor Run Time'] ?? 0,
601
629
  executorCpuTime: m['Executor CPU Time'] ?? 0,
602
630
  inputBytes: inp['Bytes Read'] ?? 0,
631
+ inputRecords: inp['Records Read'] ?? 0,
632
+ shuffleReadRecords: sr['Total Records Read'] ?? 0,
603
633
  outputBytes: out['Bytes Written'] ?? 0,
604
634
  outputRecords: out['Records Written'] ?? null,
605
635
  };
606
636
  }
607
637
 
608
- export function accumulateTask(event , state ) {
638
+ // Max-folds the TaskEnd's executor metrics into the executor's run peak. Spark polls them at
639
+ // executor heartbeat (spark.executor.metrics.pollingInterval defaults to 0), so a peak is a lower
640
+ // bound. All-zero rows (local mode) carry no measurement and are skipped.
641
+ function foldTaskExecutorMetrics(event , state ) {
642
+ const raw = event['Task Executor Metrics'];
643
+ const executorId = event['Task Info']?.['Executor ID'];
644
+ if (!raw || executorId == null) return;
645
+ let peaks ;
646
+ let measured = false;
647
+ for (const [sparkName, ourName] of EXECUTOR_METRIC_FIELDS) {
648
+ const value = raw[sparkName];
649
+ if (typeof value !== 'number' || !(value > 0)) continue;
650
+ measured = true;
651
+ peaks ??= state.executorPeakMetrics.get(executorId) ?? {};
652
+ if (value > (peaks[ourName] ?? 0)) peaks[ourName] = value;
653
+ }
654
+ if (!measured || !peaks) return;
655
+ state.executorPeakMetrics.set(executorId, peaks);
656
+ if (executorId !== DRIVER_EXECUTOR_ID) state.evidenceInputs.executorMetricRows++;
657
+ }
658
+
659
+ function sameIds(a , b ) {
660
+ if (b === undefined || a.length !== b.length) return false;
661
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
662
+ return true;
663
+ }
664
+
665
+ // `accumulableIds`, when given, are the event's accumulator IDs, which then are not in the event.
666
+ export function accumulateTask(
667
+ event , state , accumulableIds ,
668
+ ) {
669
+ foldTaskExecutorMetrics(event, state);
609
670
  const stageId = event['Stage ID'];
610
671
  const stage = state.stages.get(stageId);
611
672
  if (!stage) return null;
@@ -623,10 +684,13 @@ export function accumulateTask(event , state
623
684
 
624
685
  state.evidenceInputs.taskRecords++;
625
686
 
626
- const accumulables = event['Task Info']?.Accumulables ?? [];
627
- for (const acc of accumulables) {
628
- if (!state.taskAccumStages.has(acc.ID)) state.taskAccumStages.set(acc.ID, new Set());
629
- state.taskAccumStages.get(acc.ID) .add(stageId);
687
+ const ids = accumulableIds ?? (event['Task Info']?.Accumulables ?? []).map((acc) => acc.ID);
688
+ if (!sameIds(ids, state.lastTaskAccumIds.get(stageId))) {
689
+ state.lastTaskAccumIds.set(stageId, ids);
690
+ for (const id of ids) {
691
+ if (!state.taskAccumStages.has(id)) state.taskAccumStages.set(id, new Set());
692
+ state.taskAccumStages.get(id) .add(stageId);
693
+ }
630
694
  }
631
695
 
632
696
  const info = event['Task Info'] ?? {};
@@ -695,32 +759,40 @@ function accountLateSpeculativeLoser(event ,
695
759
  return true;
696
760
  }
697
761
 
762
+
763
+
764
+ // Driver-side values (DriverAccumUpdates) win over executor-side ones (StageCompleted
765
+ // accumulables); a metric has one of the two sources, so the order only matters for a log that
766
+ // carries both.
698
767
  export function resolvePlanTree(
699
768
  rootInfo ,
700
769
  accumMap ,
701
770
  taskAccumStages ,
702
771
  executionStageIds ,
703
772
  executionId ,
773
+ executorAccumMap ,
704
774
  ) {
705
775
  const seen = new WeakSet ();
706
776
  const nodeMap = new WeakMap ();
707
777
  let nextId = 0;
708
- // Prefixed with the owning SQL execution so PlanNode.id is globally unique,
709
- // not just unique within this one call: two executions' trees can each
710
- // produce a node named "n1", and a consumer keying off id alone (e.g. a
711
- // findings-by-nodeId index) must be able to tell them apart without also
712
- // threading execution scope through separately. No executionId (e.g. the
713
- // legacy unit tests below) falls back to the old unprefixed "n0", "n1", ...
714
- // shape.
778
+ // Prefixed with the owning SQL execution so PlanNode.id is globally unique: two executions'
779
+ // trees can each produce a node named "n1", and a consumer keying off id alone (e.g. a
780
+ // findings-by-nodeId index) must tell them apart. No executionId (hand-built trees in tests)
781
+ // leaves ids unprefixed: "n0", "n1", ...
715
782
  const idPrefix = executionId != null ? `e${executionId}:` : '';
716
783
 
717
784
  function computeMetricsAndStageIds(info )
718
-
785
+
719
786
 
720
787
  {
721
- const metrics = (info.metrics ?? []).reduce ((acc, m) => {
722
- if (m.accumulatorId !== undefined && accumMap.has(m.accumulatorId)) {
788
+ const metrics = (info.metrics ?? []).reduce ((acc, m) => {
789
+ if (m.accumulatorId === undefined) return acc;
790
+ if (accumMap.has(m.accumulatorId)) {
723
791
  acc.push({ name: m.name, value: accumMap.get(m.accumulatorId) , metricType: m.metricType });
792
+ } else if (executorAccumMap?.has(m.accumulatorId) && m.metricType !== 'average') {
793
+ // An 'average' metric's accumulator holds a sum of per-task averages (scaled by 10), so
794
+ // its total is not the average the SQL tab shows; that needs the per-task values.
795
+ acc.push({ name: m.name, value: executorAccumMap.get(m.accumulatorId) , metricType: m.metricType, executorSide: true });
724
796
  }
725
797
  return acc;
726
798
  }, []);
@@ -826,17 +898,20 @@ const EXECUTOR_METRIC_FIELD_MAP = {
826
898
  MajorGCCount: 'majorGCCount', MajorGCTime: 'majorGCTime', TotalGCTime: 'totalGCTime',
827
899
  ConcurrentGCCount: 'concurrentGCCount', ConcurrentGCTime: 'concurrentGCTime',
828
900
  };
901
+ const EXECUTOR_METRIC_FIELDS = Object.entries(EXECUTOR_METRIC_FIELD_MAP);
829
902
 
830
903
  export function startApplication(event , state ) {
831
904
  const config = state.pendingConfig ?? {};
832
905
  state.app = {
833
906
  id: event['App ID'],
834
907
  name: event['App Name'],
908
+ // Only in cluster-mode logs: Spark writes no attempt id for a client or local run.
909
+ ...(event['App Attempt ID'] ? { attemptId: event['App Attempt ID'] } : {}),
835
910
  startTime: event['Timestamp'],
836
911
  endTime: null,
837
912
  sparkVersion: state.pendingSparkVersion ?? event['Spark Version'] ?? null,
838
913
  config,
839
- resources: state.pendingResources ?? extractResources(config),
914
+ resources: state.pendingResources ?? extractResources(config, state.pendingSparkVersion ?? event['Spark Version']),
840
915
  rddInfo: state.rddInfo,
841
916
  };
842
917
  return appMessage(state);
@@ -845,7 +920,7 @@ export function startApplication(event
845
920
  export function updateEnvironment(event , state ) {
846
921
  state.evidenceInputs.environmentUpdates++;
847
922
  const config = normalizeSparkProperties(event['Spark Properties']);
848
- const resources = extractResources(config);
923
+ const resources = extractResources(config, state.app?.sparkVersion ?? state.pendingSparkVersion);
849
924
  // EnvironmentUpdate normally precedes ApplicationStart: stash the config so ApplicationStart can
850
925
  // attach it. If it arrives after (a mid-run update), apply live and re-post the app.
851
926
  if (state.app) {
@@ -1112,10 +1187,11 @@ export function recordStageExecutorMetrics(event
1112
1187
  if (!stage) return null;
1113
1188
  const raw = event['Executor Metrics'] ?? {};
1114
1189
  const metrics = {};
1115
- for (const [sparkName, ourName] of Object.entries(EXECUTOR_METRIC_FIELD_MAP)) {
1190
+ for (const [sparkName, ourName] of EXECUTOR_METRIC_FIELDS) {
1116
1191
  if (raw[sparkName] != null) metrics[ourName] = raw[sparkName];
1117
1192
  }
1118
- if (Object.keys(metrics).length > 0) {
1193
+ // A row for the driver's own heap is no executor measurement: the heap-peak finding leaves it out too.
1194
+ if (Object.keys(metrics).length > 0 && event['Executor ID'] !== DRIVER_EXECUTOR_ID) {
1119
1195
  state.evidenceInputs.executorMetricRows++;
1120
1196
  }
1121
1197
  stage.executorMetrics.set(event['Executor ID'], metrics);
@@ -1132,6 +1208,7 @@ export function startSqlExecution(event
1132
1208
  hadAdaptiveUpdate: false,
1133
1209
  };
1134
1210
  if (event.rootExecutionId !== undefined) exec.rootExecutionId = event.rootExecutionId;
1211
+ if (event.modifiedConfigs !== undefined && Object.keys(event.modifiedConfigs).length > 0) exec.modifiedConfigs = event.modifiedConfigs;
1135
1212
  if (event.physicalPlanDescription && KEPT_ARGUMENTS.test(event.physicalPlanDescription)) {
1136
1213
  exec.commandArguments = event.physicalPlanDescription;
1137
1214
  }
@@ -1140,6 +1217,7 @@ export function startSqlExecution(event
1140
1217
  state.resolvedPlanExecutions.delete(exec.id);
1141
1218
  if (sparkPlanInfo !== null) {
1142
1219
  state.accumState.set(exec.id, new Map());
1220
+ state.executorAccumState.set(exec.id, new Map());
1143
1221
  }
1144
1222
  return { type: 'sql', data: exec };
1145
1223
  }
@@ -1157,14 +1235,17 @@ export function endSqlExecution(
1157
1235
  const planInfo = exec?.sparkPlanInfo ?? null;
1158
1236
  if (!planInfo || !planInfo.nodeName) {
1159
1237
  state.accumState.delete(event.executionId);
1238
+ state.executorAccumState.delete(event.executionId);
1160
1239
  return exec ? { type: 'sql', data: { ...exec } } : null;
1161
1240
  }
1162
1241
 
1163
1242
  const accumMap = state.accumState.get(event.executionId) ?? new Map ();
1164
1243
  const planTree = resolvePlanTree(
1165
1244
  planInfo, accumMap, state.taskAccumStages, state.sqlExecStages.get(event.executionId), event.executionId,
1245
+ state.executorAccumState.get(event.executionId),
1166
1246
  );
1167
1247
  state.accumState.delete(event.executionId);
1248
+ state.executorAccumState.delete(event.executionId);
1168
1249
  // The resolved tree is all anything downstream reads; the raw plan (often megabytes per
1169
1250
  // execution under AQE) would otherwise stay live for the rest of the parse.
1170
1251
  exec .sparkPlanInfo = null;
@@ -1174,6 +1255,24 @@ export function endSqlExecution(
1174
1255
  return { type: 'sqlPlan', data: { executionId: event.executionId, planTree } };
1175
1256
  }
1176
1257
 
1258
+ // Keeps the latest total of every SQL metric accumulator a stage reports, for the execution that
1259
+ // ran the stage. A stage's accumulables hold each accumulator's running total across all stages
1260
+ // and attempts, so a later stage overwrites an earlier one rather than adding to it (Spark's
1261
+ // DAGScheduler.updateAccumulators merges into the driver-side accumulator and records its value;
1262
+ // the SQL tab sums the same task updates). Spark writes the value as a decimal string.
1263
+ export function recordStageSqlAccumulables(info , state ) {
1264
+ const accumulables = info.Accumulables;
1265
+ if (!accumulables || accumulables.length === 0) return;
1266
+ const executionId = state.stageToSqlExec.get(info['Stage ID']);
1267
+ const totals = executionId === undefined ? undefined : state.executorAccumState.get(executionId);
1268
+ if (!totals) return;
1269
+ for (const acc of accumulables) {
1270
+ if (acc.Metadata !== 'sql') continue;
1271
+ const value = typeof acc.Value === 'string' ? Number(acc.Value) : acc.Value;
1272
+ if (typeof value === 'number' && Number.isFinite(value)) totals.set(acc.ID, value);
1273
+ }
1274
+ }
1275
+
1177
1276
  export function applyDriverAccumUpdates(event , state ) {
1178
1277
  const { executionId, accumUpdates } = event;
1179
1278
  if (!state.accumState.has(executionId)) return null; // late update, discard
@@ -1266,6 +1365,7 @@ export function processEvent(event , state ) {
1266
1365
  const id = info['Stage ID'];
1267
1366
  const stage = state.stages.get(id);
1268
1367
  if (!stage) return null;
1368
+ recordStageSqlAccumulables(info, state);
1269
1369
  stage.completedAt = info['Completion Time'] ?? 0;
1270
1370
  // Older Spark (seen on 1.x-2.0 logs) posts StageSubmitted before the stage's submission
1271
1371
  // time is set; StageCompleted carries it. Without this backfill submittedAt stays 0 and
@@ -1321,9 +1421,8 @@ const KNOWN_EVENT_TYPES = new Set(
1321
1421
  );
1322
1422
 
1323
1423
  // SQLExecutionStart and SQLAdaptiveExecutionUpdate carry `physicalPlanDescription`, Spark's text
1324
- // rendering of the plan. The plan tree comes from sparkPlanInfo, yet on a real 3.5 GB log the
1325
- // description was 72% of the AQE-update bytes, which were themselves 73% of the log. Cutting its
1326
- // string value out before JSON.parse halves the parse cost of those lines.
1424
+ // rendering of the plan. The plan tree comes from sparkPlanInfo, so the description's string value
1425
+ // is cut out before JSON.parse (worker-protocol.md, "Decoder and parser calibration").
1327
1426
  //
1328
1427
  // One exception: a Delta write command's node prints only its bare name in sparkPlanInfo, and its
1329
1428
  // target table appears only on its `Arguments:` line of the description. When the description is
@@ -1435,13 +1534,25 @@ const ACCUMULABLE_ID_KEY = '"ID":';
1435
1534
  const MAX_ACCUMULABLE_ID_DIGITS = 15;
1436
1535
 
1437
1536
  // Parses a TaskEnd line with its Task Info Accumulables array reduced to the `{ID}` entries
1438
- // accumulateTask reads, or returns null for the caller to parse the line whole. That array is 71%
1439
- // of TaskEnd bytes on the largest real log, where parsing its TaskEnd lines took 1.3s (0.5s here).
1537
+ // accumulateTask reads, or returns null for the caller to parse the line whole. That array is most
1538
+ // of a TaskEnd line's bytes (worker-protocol.md, "Decoder and parser calibration").
1440
1539
  // The IDs are read with a string scan and the array is cut out before JSON.parse, but only when
1441
1540
  // every entry has Spark's flat form (`{"ID":n,...}`, ID first, no nested array): any `{` that
1442
1541
  // doesn't open `{"ID":n` or any `[` in the array (updatedBlockStatuses) falls back. A `]` inside a
1443
1542
  // Name string cuts the array short and leaves invalid JSON, which falls back too.
1444
1543
  export function parseTaskEnd(line ) {
1544
+ const split = splitTaskEnd(line);
1545
+ if (!split) return null;
1546
+ split.parsed['Task Info'] .Accumulables = split.ids.map((ID) => ({ ID }));
1547
+ return split.parsed;
1548
+ }
1549
+
1550
+
1551
+
1552
+ // parseTaskEnd's work with the IDs left apart from the parsed event, whose Accumulables is empty:
1553
+ // the scan already read the IDs as digits, so parseAndDispatch hands them straight to accumulateTask
1554
+ // instead of validating them again.
1555
+ function splitTaskEnd(line ) {
1445
1556
  if (!line.startsWith(TASK_END_PREFIX)) return null;
1446
1557
  const keyAt = line.indexOf(ACCUMULABLES_KEY);
1447
1558
  if (keyAt === -1) return null;
@@ -1461,6 +1572,7 @@ export function parseTaskEnd(line ) {
1461
1572
  if (after !== 0x2c && after !== 0x7d) return null; // `,` or `}`
1462
1573
  ids.push(id);
1463
1574
  }
1575
+ if (ids.length > MAX_ACCUMULABLES_PER_TASK) return null; // the schema rejects it: parse whole
1464
1576
  let parsed ;
1465
1577
  try {
1466
1578
  parsed = JSON.parse(line.slice(0, from) + line.slice(close));
@@ -1469,8 +1581,7 @@ export function parseTaskEnd(line ) {
1469
1581
  }
1470
1582
  const info = parsed?.['Task Info'];
1471
1583
  if (!info || !Array.isArray(info.Accumulables) || info.Accumulables.length !== 0) return null;
1472
- info.Accumulables = ids.map((ID) => ({ ID }));
1473
- return parsed;
1584
+ return { parsed: parsed , ids };
1474
1585
  }
1475
1586
 
1476
1587
  const ADAPTIVE_UPDATE_PREFIX =
@@ -1481,16 +1592,14 @@ const MAX_EXECUTION_ID_DIGITS = 20;
1481
1592
 
1482
1593
  // An AQE update replaces its execution's whole plan, and nothing reads a plan an update replaced:
1483
1594
  // only the last one before SQLExecutionEnd is resolved. So while the execution is open, only its
1484
- // latest update is kept, as unparsed text, and superseded ones are never parsed. On the largest
1485
- // real log 541 of 664 updates were superseded, 1.1 MB of plan JSON each after the plan text is
1486
- // cut. Returns false (parse it now, as any other line) unless the line is Spark's compact form
1595
+ // latest update is kept, as unparsed text, and superseded ones are never parsed (figures:
1596
+ // worker-protocol.md, "Decoder and parser calibration"). Returns false (parse it now, as any other line) unless the line is Spark's compact form
1487
1597
  // ending in an object-valued `sparkPlanInfo`, its last field: a null-plan update keeps the
1488
1598
  // previous plan, so it can't supersede one. Trade-off: a malformed superseded update is never
1489
- // seen, so it no longer counts as a skipped line.
1599
+ // seen, so it does not count as a skipped line.
1490
1600
  //
1491
1601
  // These updates span decode slices, so `line` is a cons string: the prefix and suffix checks read
1492
- // `joined`'s flat pieces instead, or a superseded update is copied flat only to be dropped (541
1493
- // copies of 1.1 MB on that log). A piece too short to hold what is checked falls back to `line`.
1602
+ // `joined`'s flat pieces instead, or a superseded update is copied flat only to be dropped. A piece too short to hold what is checked falls back to `line`.
1494
1603
  function deferAdaptiveUpdate(
1495
1604
  line , state , emit , joined ,
1496
1605
  ) {
@@ -1548,8 +1657,11 @@ function noteUnreadableSqlStart(line , state ) {
1548
1657
  function parseAndDispatch(line , state , emit ) {
1549
1658
  if (line.startsWith(BLOCK_UPDATED_PREFIX) && !line.includes(RDD_BLOCK_ID_FRAGMENT)) return;
1550
1659
  let parsed ;
1660
+ let taskEndIds ;
1551
1661
  try {
1552
- parsed = parseTaskEnd(line) ?? JSON.parse(stripPlanDescription(line));
1662
+ const split = splitTaskEnd(line);
1663
+ if (split) ({ parsed, ids: taskEndIds } = split);
1664
+ else parsed = JSON.parse(stripPlanDescription(line));
1553
1665
  } catch {
1554
1666
  state.skippedLines++;
1555
1667
  noteUnreadableSqlStart(line, state);
@@ -1573,7 +1685,9 @@ function parseAndDispatch(line , state , emit
1573
1685
  }
1574
1686
  let msg ;
1575
1687
  try {
1576
- msg = processEvent(result.data, state);
1688
+ msg = taskEndIds && result.data.Event === 'SparkListenerTaskEnd'
1689
+ ? accumulateTask(result.data, state, taskEndIds)
1690
+ : processEvent(result.data, state);
1577
1691
  } catch (err) {
1578
1692
  // processEvent's switch is exhaustive over the schema-validated union (assertNever default), so
1579
1693
  // this is unreachable for any event that got this far. If it fires it's a handler bug, not
@@ -1623,11 +1737,14 @@ export function collectLateAttemptWork(state )
1623
1737
  }
1624
1738
 
1625
1739
  export function emitParseCompletion(state , emit , linesProcessed ) {
1626
- // Executions that never ended keep their latest AQE update, as they did before it was deferred.
1740
+ // Executions that never ended still apply their latest deferred AQE update.
1627
1741
  for (const executionId of [...state.pendingAdaptiveUpdates.keys()]) flushAdaptiveUpdate(executionId, state, emit);
1628
1742
  emit({ type: 'progress', pct: 1, linesProcessed });
1629
1743
  emit({ type: 'stageLateAttemptWork', data: collectLateAttemptWork(state) });
1630
- emit({ type: 'runAggregates', data: computeRunAggregates(state.taskStore) });
1744
+ emit({
1745
+ type: 'runAggregates',
1746
+ data: { ...computeRunAggregates(state.taskStore), executorPeakMetrics: Object.fromEntries(state.executorPeakMetrics) },
1747
+ });
1631
1748
  emit({ type: 'stageSpeculationWaste', data: collectLateSpeculationWaste(state) });
1632
1749
  emit({ type: 'stageExecutorMetrics', data: collectStageExecutorMetrics(state) });
1633
1750
  emit(appMessage(state));
@@ -1637,4 +1754,5 @@ export function emitParseCompletion(state , emit
1637
1754
  ...(state.unreadableSqlStarts.size > 0 ? { unreadableSqlExecutions: [...state.unreadableSqlStarts].sort((a, b) => a - b) } : {}),
1638
1755
  });
1639
1756
  state.accumState.clear();
1757
+ state.executorAccumState.clear();
1640
1758
  }