sparkforensics-server 0.5.0 → 0.5.2

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 (309) hide show
  1. package/package.json +3 -3
  2. package/public/assets/{AutoscalingChurn-9Zo6IMGS.js → AutoscalingChurn-8t3U3isq.js} +1 -1
  3. package/public/assets/{CacheUtilization-MBDfMsKC.js → CacheUtilization-CJs9tTum.js} +1 -1
  4. package/public/assets/CachingOpportunity-D1NcHs0V.js +1 -0
  5. package/public/assets/{ColdStart-BtS12b6_.js → ColdStart-CdRJ9gD8.js} +1 -1
  6. package/public/assets/{ConfigAudit-DNPV6EQg.js → ConfigAudit-B5Gbe5js.js} +1 -1
  7. package/public/assets/CoreUsageArea-a--NTRGY.js +1 -0
  8. package/public/assets/{Dashboard-CC_BXrNy.js → Dashboard-C1hNeX49.js} +2 -2
  9. package/public/assets/DisclosureContext-ZpE6CkHx.js +1 -0
  10. package/public/assets/{DuplicatePlanSubtree-vN3zqRsi.js → DuplicatePlanSubtree-dobXdz7E.js} +1 -1
  11. package/public/assets/{ExecutorUtilization-CZObSLIj.js → ExecutorUtilization-D9grwi7t.js} +1 -1
  12. package/public/assets/{GcPressure-ByJlLOua.js → GcPressure-8-G2ri10.js} +1 -1
  13. package/public/assets/{IncompleteRun-MzqV4W8y.js → IncompleteRun-B91lky7a.js} +1 -1
  14. package/public/assets/{JobFailures-LbMvAwAb.js → JobFailures-BsWvBMyJ.js} +1 -1
  15. package/public/assets/{MemoryUtilization-DH4jYvJK.js → MemoryUtilization-BM5schZI.js} +1 -1
  16. package/public/assets/{OverBroadcast-euk6HKg4.js → OverBroadcast-DdN8JIJH.js} +1 -1
  17. package/public/assets/{PartitionHint-DThaMMRT.js → PartitionHint-CUvbnnxy.js} +1 -1
  18. package/public/assets/{PartitionSizing-yIN-iZzx.js → PartitionSizing-CY5HDYtp.js} +1 -1
  19. package/public/assets/{PlanExplorer-Dd9WPK4S.js → PlanExplorer-DgHCDBD_.js} +1 -1
  20. package/public/assets/PlanGraphRoute-DH3KJb8j.js +1 -0
  21. package/public/assets/{RetryWaste-CXH4D-xH.js → RetryWaste-DOnE9vGF.js} +1 -1
  22. package/public/assets/{ShuffleIO-CW7U68ph.js → ShuffleIO-MMnDESkF.js} +1 -1
  23. package/public/assets/{Skew-PL79TppX.js → Skew-BmZZJ5ms.js} +1 -1
  24. package/public/assets/{SlowHost-GBO73hCi.js → SlowHost-CsJzfEqo.js} +1 -1
  25. package/public/assets/{SmallFiles-B5cUjEav.js → SmallFiles-BoK3R1Ex.js} +1 -1
  26. package/public/assets/{SpeculationWaste-mxi3jgb0.js → SpeculationWaste-CnVxB5AJ.js} +1 -1
  27. package/public/assets/Spill-BhNicyBy.js +1 -0
  28. package/public/assets/{StageFailed-YvxC11ii.js → StageFailed-mAhHU4l_.js} +1 -1
  29. package/public/assets/StageFindingGroup-BEtPWcT0.js +1 -0
  30. package/public/assets/{StageHeader-BMNmUy-s.js → StageHeader-DJkYkrsU.js} +1 -1
  31. package/public/assets/{StageShape-DO3mQ6NS.js → StageShape-BDxRknLp.js} +1 -1
  32. package/public/assets/{StageSlowness-SwRxfAEx.js → StageSlowness-BPRw-otN.js} +1 -1
  33. package/public/assets/{Straggler-DHLTiBY-.js → Straggler-CZCbnOWP.js} +1 -1
  34. package/public/assets/{TaskFailures-Bj0QxiOc.js → TaskFailures-C57YcLFQ.js} +1 -1
  35. package/public/assets/{TinyTask-COUQQ5bQ.js → TinyTask-Bi84qU7n.js} +1 -1
  36. package/public/assets/{UnderBroadcast-RAUbQ6vw.js → UnderBroadcast-DdQV7htE.js} +1 -1
  37. package/public/assets/{finding-anchor-Dl8mIHiU.js → finding-anchor-Cl6_5hfV.js} +1 -1
  38. package/public/assets/{impact-sort-Eihua9dY.js → impact-sort-CssZ6MT2.js} +1 -1
  39. package/public/assets/index-2h_TgUAb.css +2 -0
  40. package/public/assets/index-B-03mG7K.js +14 -0
  41. package/public/assets/parser-worker-CA5RxqKc.js +122 -0
  42. package/public/assets/{plan-graph-vendor-B0sEms9Z.js → plan-graph-vendor-BYl-AzN5.js} +1 -1
  43. package/public/assets/{react-vendor-BAKt3vny.js → react-vendor-DEdfrRTJ.js} +1 -1
  44. package/public/assets/{select-DkjFHlXR.js → select-CQjNhprm.js} +1 -1
  45. package/public/assets/{usePagedRows-Bo-aSztx.js → usePagedRows-Dyl6T8Ok.js} +1 -1
  46. package/public/assets/useRecentFiles-XZEnLlal.js +11 -0
  47. package/public/assets/{useSortMode-lDedD5a3.js → useSortMode-Dhvit8dt.js} +1 -1
  48. package/public/docs/404.html +2 -2
  49. package/public/docs/assets/{app.yjczoibf.js → app.eYTPqTeq.js} +1 -1
  50. package/public/docs/assets/chunks/@localSearchIndexroot.u_e5bQz4.js +1 -0
  51. package/public/docs/assets/chunks/{VPLocalSearchBox.DOBbPfix.js → VPLocalSearchBox.DsZrQKQR.js} +1 -1
  52. package/public/docs/assets/chunks/{theme.DFcAl8Ws.js → theme.DsTNE2pE.js} +2 -2
  53. package/public/docs/assets/contributor-guide_architecture_board-widgets.md.YQcj5EI4.js +1 -0
  54. package/public/docs/assets/{contributor-guide_architecture_board-widgets.md.D0Kkk_r5.lean.js → contributor-guide_architecture_board-widgets.md.YQcj5EI4.lean.js} +1 -1
  55. package/public/docs/assets/contributor-guide_architecture_board-widgets_caching-and-memory.md.8bH1HnZp.js +1 -0
  56. package/public/docs/assets/contributor-guide_architecture_board-widgets_caching-and-memory.md.8bH1HnZp.lean.js +1 -0
  57. package/public/docs/assets/contributor-guide_architecture_board-widgets_plan-advisor.md.RlzvUmNm.js +1 -0
  58. package/public/docs/assets/contributor-guide_architecture_board-widgets_plan-advisor.md.RlzvUmNm.lean.js +1 -0
  59. package/public/docs/assets/contributor-guide_architecture_board-widgets_visual-system-and-templating.md.DYgUpIJB.js +1 -0
  60. package/public/docs/assets/contributor-guide_architecture_board-widgets_visual-system-and-templating.md.DYgUpIJB.lean.js +1 -0
  61. package/public/docs/assets/contributor-guide_architecture_detector-contract.md.wj6Et6UD.js +1 -0
  62. package/public/docs/assets/contributor-guide_architecture_detector-contract.md.wj6Et6UD.lean.js +1 -0
  63. package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.yNex4bys.js +1 -0
  64. package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.yNex4bys.lean.js +1 -0
  65. package/public/docs/assets/contributor-guide_architecture_detector-contract_threshold-tables.md.CKjWG56Y.js +1 -0
  66. package/public/docs/assets/contributor-guide_architecture_detector-contract_threshold-tables.md.CKjWG56Y.lean.js +1 -0
  67. package/public/docs/assets/contributor-guide_architecture_drill-down.md.CqZPrdo9.js +1 -0
  68. package/public/docs/assets/contributor-guide_architecture_drill-down.md.CqZPrdo9.lean.js +1 -0
  69. package/public/docs/assets/contributor-guide_architecture_drill-down_plan-graph.md.DwOozIEs.js +1 -0
  70. package/public/docs/assets/contributor-guide_architecture_drill-down_plan-graph.md.DwOozIEs.lean.js +1 -0
  71. package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.CZv_ZGOp.js +1 -0
  72. package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.CZv_ZGOp.lean.js +1 -0
  73. package/public/docs/assets/contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.BUucRc0A.js +1 -0
  74. package/public/docs/assets/contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.BUucRc0A.lean.js +1 -0
  75. package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.DdCGtAGu.js +1 -0
  76. package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.DdCGtAGu.lean.js +1 -0
  77. package/public/docs/assets/contributor-guide_architecture_index.md.tymxf1Fh.js +1 -0
  78. package/public/docs/assets/{contributor-guide_architecture_index.md.B9yZieYL.lean.js → contributor-guide_architecture_index.md.tymxf1Fh.lean.js} +1 -1
  79. package/public/docs/assets/contributor-guide_architecture_overview.md.C1sSvm9i.js +1 -0
  80. package/public/docs/assets/contributor-guide_architecture_state-and-history.md.DyCBdjuM.js +1 -0
  81. package/public/docs/assets/{contributor-guide_architecture_state-and-history.md.w4lwybdh.lean.js → contributor-guide_architecture_state-and-history.md.DyCBdjuM.lean.js} +1 -1
  82. package/public/docs/assets/contributor-guide_architecture_state-and-history_history-server-intake.md.C298hrHZ.js +1 -0
  83. package/public/docs/assets/contributor-guide_architecture_state-and-history_history-server-intake.md.C298hrHZ.lean.js +1 -0
  84. package/public/docs/assets/contributor-guide_architecture_state-and-history_run-comparison-internals.md.CZA0JLtV.js +1 -0
  85. package/public/docs/assets/contributor-guide_architecture_state-and-history_run-comparison-internals.md.CZA0JLtV.lean.js +1 -0
  86. package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.BOZ1azXG.js +1 -0
  87. package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.BOZ1azXG.lean.js +1 -0
  88. package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.BQXycK6G.js +1 -0
  89. package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.BQXycK6G.lean.js +1 -0
  90. package/public/docs/assets/contributor-guide_architecture_widget-rendering_investigation-routing.md.BGdiyElu.js +1 -0
  91. package/public/docs/assets/contributor-guide_architecture_widget-rendering_investigation-routing.md.BGdiyElu.lean.js +1 -0
  92. package/public/docs/assets/contributor-guide_architecture_widget-rendering_list-sort-mode.md.BuHKgH0C.js +1 -0
  93. package/public/docs/assets/contributor-guide_architecture_widget-rendering_list-sort-mode.md.BuHKgH0C.lean.js +1 -0
  94. package/public/docs/assets/contributor-guide_architecture_widget-rendering_reference-section.md.DWqC80bN.js +1 -0
  95. package/public/docs/assets/contributor-guide_architecture_widget-rendering_reference-section.md.DWqC80bN.lean.js +1 -0
  96. package/public/docs/assets/contributor-guide_architecture_widget-rendering_row-expand-contract.md.ZT89u9f1.js +1 -0
  97. package/public/docs/assets/contributor-guide_architecture_widget-rendering_row-expand-contract.md.ZT89u9f1.lean.js +1 -0
  98. package/public/docs/assets/contributor-guide_architecture_worker-protocol.md.BAnhi2-d.js +1 -0
  99. package/public/docs/assets/contributor-guide_architecture_worker-protocol.md.BAnhi2-d.lean.js +1 -0
  100. package/public/docs/assets/contributor-guide_architecture_worker-protocol_cli-and-mcp.md.DC1YqXBp.js +1 -0
  101. package/public/docs/assets/contributor-guide_architecture_worker-protocol_cli-and-mcp.md.DC1YqXBp.lean.js +1 -0
  102. package/public/docs/assets/contributor-guide_architecture_worker-protocol_core-and-event-validation.md.DSVFPpWb.js +1 -0
  103. package/public/docs/assets/contributor-guide_architecture_worker-protocol_core-and-event-validation.md.DSVFPpWb.lean.js +1 -0
  104. package/public/docs/assets/contributor-guide_architecture_worker-protocol_evidence-availability.md.CjimIDBY.js +6 -0
  105. package/public/docs/assets/contributor-guide_architecture_worker-protocol_evidence-availability.md.CjimIDBY.lean.js +1 -0
  106. package/public/docs/assets/contributor-guide_architecture_worker-protocol_evidence-report.md.DVGZOWvW.js +1 -0
  107. package/public/docs/assets/contributor-guide_architecture_worker-protocol_evidence-report.md.DVGZOWvW.lean.js +1 -0
  108. package/public/docs/assets/user-guide_alternative-log-retrieval.md.CJpYvHGo.js +4 -0
  109. package/public/docs/assets/user-guide_alternative-log-retrieval.md.CJpYvHGo.lean.js +1 -0
  110. package/public/docs/assets/user-guide_alternative-log-retrieval_managed-platforms.md.DvDwHNtC.js +4 -0
  111. package/public/docs/assets/user-guide_alternative-log-retrieval_managed-platforms.md.DvDwHNtC.lean.js +1 -0
  112. package/public/docs/assets/user-guide_alternative-log-retrieval_ssh-bastion.md.Gszzlplv.js +1 -0
  113. package/public/docs/assets/user-guide_alternative-log-retrieval_ssh-bastion.md.Gszzlplv.lean.js +1 -0
  114. package/public/docs/assets/user-guide_getting-started.md.DYHHHkQR.js +1 -0
  115. package/public/docs/assets/user-guide_getting-started.md.DYHHHkQR.lean.js +1 -0
  116. package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.OUQqw3mI.js +23 -0
  117. package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.OUQqw3mI.lean.js +1 -0
  118. package/public/docs/assets/user-guide_getting-started_reading-the-dashboard.md.Obf3W3__.js +1 -0
  119. package/public/docs/assets/user-guide_getting-started_reading-the-dashboard.md.Obf3W3__.lean.js +1 -0
  120. package/public/docs/assets/user-guide_getting-started_regression-metric-keys.md.B_gzr2Mw.js +1 -0
  121. package/public/docs/assets/user-guide_getting-started_regression-metric-keys.md.B_gzr2Mw.lean.js +1 -0
  122. package/public/docs/assets/user-guide_getting-started_tuning-thresholds.md.B_MyjyZm.js +4 -0
  123. package/public/docs/assets/user-guide_getting-started_tuning-thresholds.md.B_MyjyZm.lean.js +1 -0
  124. package/public/docs/assets/user-guide_mcp-tools.md.C65SjWUL.js +16 -0
  125. package/public/docs/assets/user-guide_mcp-tools.md.C65SjWUL.lean.js +1 -0
  126. package/public/docs/assets/user-guide_mcp-tools_compare-runs.md.Cc_gLoix.js +56 -0
  127. package/public/docs/assets/user-guide_mcp-tools_compare-runs.md.Cc_gLoix.lean.js +1 -0
  128. package/public/docs/assets/user-guide_mcp-tools_diagnose-run.md.BP1vnHA5.js +23 -0
  129. package/public/docs/assets/user-guide_mcp-tools_diagnose-run.md.BP1vnHA5.lean.js +1 -0
  130. package/public/docs/assets/user-guide_mcp-tools_evaluate-budgets.md.BB0nDY3Z.js +26 -0
  131. package/public/docs/assets/user-guide_mcp-tools_evaluate-budgets.md.BB0nDY3Z.lean.js +1 -0
  132. package/public/docs/assets/user-guide_mcp-tools_get-finding-documentation.md.BtTTHNV6.js +14 -0
  133. package/public/docs/assets/user-guide_mcp-tools_get-finding-documentation.md.BtTTHNV6.lean.js +1 -0
  134. package/public/docs/assets/user-guide_mcp-tools_get-finding-evidence.md.rKyu4wts.js +20 -0
  135. package/public/docs/assets/user-guide_mcp-tools_get-finding-evidence.md.rKyu4wts.lean.js +1 -0
  136. package/public/docs/assets/user-guide_mcp-tools_get-reference-doc.md.Yac4tH3u.js +5 -0
  137. package/public/docs/assets/user-guide_mcp-tools_get-reference-doc.md.Yac4tH3u.lean.js +1 -0
  138. package/public/docs/assets/user-guide_mcp-tools_get-run-summary.md.DpxZ4ZNp.js +15 -0
  139. package/public/docs/assets/user-guide_mcp-tools_get-run-summary.md.DpxZ4ZNp.lean.js +1 -0
  140. package/public/docs/assets/user-guide_mcp-tools_list-runs.md.DpQPeCM3.js +12 -0
  141. package/public/docs/assets/user-guide_mcp-tools_list-runs.md.DpQPeCM3.lean.js +1 -0
  142. package/public/docs/assets/user-guide_run-comparison.md.BUALkohi.js +1 -0
  143. package/public/docs/assets/user-guide_run-comparison.md.BUALkohi.lean.js +1 -0
  144. package/public/docs/assets/user-guide_run-comparison_how-stages-are-matched.md.DSXGu2tA.js +1 -0
  145. package/public/docs/assets/user-guide_run-comparison_how-stages-are-matched.md.DSXGu2tA.lean.js +1 -0
  146. package/public/docs/assets/user-guide_understanding-findings.md.C9YRruMT.js +1 -0
  147. package/public/docs/assets/user-guide_understanding-findings.md.C9YRruMT.lean.js +1 -0
  148. package/public/docs/contributor-guide/architecture/board-widgets/caching-and-memory.html +30 -0
  149. package/public/docs/contributor-guide/architecture/board-widgets/plan-advisor.html +30 -0
  150. package/public/docs/contributor-guide/architecture/board-widgets/visual-system-and-templating.html +30 -0
  151. package/public/docs/contributor-guide/architecture/board-widgets.html +5 -5
  152. package/public/docs/contributor-guide/architecture/detector-contract/plan-attribution.html +30 -0
  153. package/public/docs/contributor-guide/architecture/detector-contract/threshold-tables.html +30 -0
  154. package/public/docs/contributor-guide/architecture/detector-contract.html +5 -5
  155. package/public/docs/contributor-guide/architecture/drill-down/plan-graph.html +30 -0
  156. package/public/docs/contributor-guide/architecture/drill-down.html +5 -5
  157. package/public/docs/contributor-guide/architecture/impact-estimation/caveats-tuning-and-coverage.html +30 -0
  158. package/public/docs/contributor-guide/architecture/impact-estimation/stage-union-rollup.html +30 -0
  159. package/public/docs/contributor-guide/architecture/impact-estimation.html +5 -5
  160. package/public/docs/contributor-guide/architecture/index.html +5 -5
  161. package/public/docs/contributor-guide/architecture/overview.html +5 -5
  162. package/public/docs/contributor-guide/architecture/state-and-history/history-server-intake.html +30 -0
  163. package/public/docs/contributor-guide/architecture/state-and-history/run-comparison-internals.html +30 -0
  164. package/public/docs/contributor-guide/architecture/state-and-history.html +5 -5
  165. package/public/docs/contributor-guide/architecture/widget-rendering/findings-tab.html +30 -0
  166. package/public/docs/contributor-guide/architecture/widget-rendering/investigation-routing.html +30 -0
  167. package/public/docs/contributor-guide/architecture/widget-rendering/list-sort-mode.html +30 -0
  168. package/public/docs/contributor-guide/architecture/widget-rendering/reference-section.html +30 -0
  169. package/public/docs/contributor-guide/architecture/widget-rendering/row-expand-contract.html +30 -0
  170. package/public/docs/contributor-guide/architecture/widget-rendering.html +5 -5
  171. package/public/docs/contributor-guide/architecture/worker-protocol/cli-and-mcp.html +30 -0
  172. package/public/docs/contributor-guide/architecture/worker-protocol/core-and-event-validation.html +30 -0
  173. package/public/docs/contributor-guide/architecture/worker-protocol/evidence-availability.html +35 -0
  174. package/public/docs/contributor-guide/architecture/worker-protocol/evidence-report.html +30 -0
  175. package/public/docs/contributor-guide/architecture/worker-protocol.html +5 -10
  176. package/public/docs/contributor-guide/contributing.html +4 -4
  177. package/public/docs/contributor-guide/development-setup.html +4 -4
  178. package/public/docs/contributor-guide/testing.html +4 -4
  179. package/public/docs/hashmap.json +1 -1
  180. package/public/docs/index.html +3 -3
  181. package/public/docs/tuning-reference/anti-patterns.html +3 -3
  182. package/public/docs/tuning-reference/aqe.html +3 -3
  183. package/public/docs/tuning-reference/bottleneck-broadcast-sizing.html +3 -3
  184. package/public/docs/tuning-reference/bottleneck-cold-start.html +3 -3
  185. package/public/docs/tuning-reference/bottleneck-duplicate-plan-subtree.html +3 -3
  186. package/public/docs/tuning-reference/bottleneck-failures.html +3 -3
  187. package/public/docs/tuning-reference/bottleneck-gc.html +3 -3
  188. package/public/docs/tuning-reference/bottleneck-job-failure-rate.html +3 -3
  189. package/public/docs/tuning-reference/bottleneck-memory-utilization.html +3 -3
  190. package/public/docs/tuning-reference/bottleneck-retry-waste.html +3 -3
  191. package/public/docs/tuning-reference/bottleneck-shuffle.html +3 -3
  192. package/public/docs/tuning-reference/bottleneck-skew.html +3 -3
  193. package/public/docs/tuning-reference/bottleneck-slow-host.html +3 -3
  194. package/public/docs/tuning-reference/bottleneck-small-files.html +3 -3
  195. package/public/docs/tuning-reference/bottleneck-spill.html +3 -3
  196. package/public/docs/tuning-reference/bottleneck-straggler.html +3 -3
  197. package/public/docs/tuning-reference/bottleneck-tiny-tasks.html +3 -3
  198. package/public/docs/tuning-reference/bottleneck-utilization.html +3 -3
  199. package/public/docs/tuning-reference/caching.html +3 -3
  200. package/public/docs/tuning-reference/cluster-config.html +3 -3
  201. package/public/docs/tuning-reference/config.html +3 -3
  202. package/public/docs/tuning-reference/data-formats.html +3 -3
  203. package/public/docs/tuning-reference/index.html +3 -3
  204. package/public/docs/tuning-reference/intro.html +3 -3
  205. package/public/docs/tuning-reference/joins.html +3 -3
  206. package/public/docs/tuning-reference/memory-model.html +3 -3
  207. package/public/docs/tuning-reference/metrics.html +3 -3
  208. package/public/docs/tuning-reference/partitioning.html +3 -3
  209. package/public/docs/tuning-reference/pyspark.html +3 -3
  210. package/public/docs/tuning-reference/shuffle.html +3 -3
  211. package/public/docs/tuning-reference/spark-architecture.html +3 -3
  212. package/public/docs/tuning-reference/table-formats.html +3 -3
  213. package/public/docs/user-guide/alternative-log-retrieval/managed-platforms.html +33 -0
  214. package/public/docs/user-guide/alternative-log-retrieval/ssh-bastion.html +30 -0
  215. package/public/docs/user-guide/alternative-log-retrieval.html +6 -9
  216. package/public/docs/user-guide/getting-started/ci-and-automation.html +52 -0
  217. package/public/docs/user-guide/getting-started/reading-the-dashboard.html +30 -0
  218. package/public/docs/user-guide/getting-started/regression-metric-keys.html +30 -0
  219. package/public/docs/user-guide/getting-started/tuning-thresholds.html +33 -0
  220. package/public/docs/user-guide/getting-started.html +5 -28
  221. package/public/docs/user-guide/mcp-tools/compare-runs.html +85 -0
  222. package/public/docs/user-guide/mcp-tools/diagnose-run.html +52 -0
  223. package/public/docs/user-guide/mcp-tools/evaluate-budgets.html +55 -0
  224. package/public/docs/user-guide/mcp-tools/get-finding-documentation.html +43 -0
  225. package/public/docs/user-guide/mcp-tools/get-finding-evidence.html +49 -0
  226. package/public/docs/user-guide/mcp-tools/get-reference-doc.html +34 -0
  227. package/public/docs/user-guide/mcp-tools/get-run-summary.html +44 -0
  228. package/public/docs/user-guide/mcp-tools/list-runs.html +41 -0
  229. package/public/docs/user-guide/mcp-tools.html +8 -140
  230. package/public/docs/user-guide/run-comparison/how-stages-are-matched.html +30 -0
  231. package/public/docs/user-guide/run-comparison.html +5 -5
  232. package/public/docs/user-guide/understanding-findings.html +5 -5
  233. package/public/export-template.html +45 -45
  234. package/public/index.html +5 -5
  235. package/vendor-core/analyzer.js +1 -1
  236. package/vendor-core/api-merge-writes.js +80 -0
  237. package/vendor-core/cli/budgets.js +62 -2
  238. package/vendor-core/cli/regression-budgets.js +35 -0
  239. package/vendor-core/comparison-output.js +50 -0
  240. package/vendor-core/core-source-hash.txt +1 -1
  241. package/vendor-core/delta-commands.js +13 -0
  242. package/vendor-core/delta-log-paths.js +42 -0
  243. package/vendor-core/delta-targets.js +127 -0
  244. package/vendor-core/detectors.js +219 -56
  245. package/vendor-core/docs-content/detection/chrn.md +3 -1
  246. package/vendor-core/docs-content/detection/cold.md +2 -1
  247. package/vendor-core/docs-content/detection/plan.md +9 -2
  248. package/vendor-core/docs-content/detection/shfl.md +8 -2
  249. package/vendor-core/docs-content/detection/skew.md +11 -5
  250. package/vendor-core/docs-content/detection/slow.md +6 -2
  251. package/vendor-core/docs-content/detection/spill.md +4 -1
  252. package/vendor-core/docs-content/detection/strag.md +5 -3
  253. package/vendor-core/docs-content/detection/tiny.md +3 -2
  254. package/vendor-core/event-handlers.js +95 -9
  255. package/vendor-core/event-schemas.js +3 -1
  256. package/vendor-core/evidence-report.js +24 -15
  257. package/vendor-core/finding-presentation.js +39 -13
  258. package/vendor-core/finding-types.js +50 -6
  259. package/vendor-core/mcp-server-factory.js +17 -6
  260. package/vendor-core/mcp-tools.js +53 -35
  261. package/vendor-core/median.js +8 -0
  262. package/vendor-core/plan-graph-model.js +1 -1
  263. package/vendor-core/plan-summary.js +13 -10
  264. package/vendor-core/run-comparison.js +81 -120
  265. package/vendor-core/run-output.js +26 -0
  266. package/vendor-core/sequence-alignment.js +83 -0
  267. package/vendor-core/stage-alignment.js +586 -0
  268. package/vendor-core/stage-identity.js +112 -0
  269. package/vendor-core/stage-quantiles.js +3 -1
  270. package/vendor-core/stage-structure.js +83 -0
  271. package/vendor-core/types.js +5 -0
  272. package/vendor-core/write-target-args.js +48 -0
  273. package/vendor-core/write-targets.js +35 -52
  274. package/public/assets/CachingOpportunity-Cfw3wAFa.js +0 -1
  275. package/public/assets/CoreUsageArea-BObEeenE.js +0 -1
  276. package/public/assets/DisclosureContext-Ds0hcZFn.js +0 -1
  277. package/public/assets/PlanGraphRoute-NV5mCXRm.js +0 -1
  278. package/public/assets/Spill-CH1bNpxL.js +0 -1
  279. package/public/assets/StageFindingGroup-B1A0nf-e.js +0 -1
  280. package/public/assets/index-BcqTLKFp.css +0 -2
  281. package/public/assets/index-DaC8A2FS.js +0 -13
  282. package/public/assets/parser-worker-CfDvPkwF.js +0 -122
  283. package/public/assets/useRecentFiles-Bb4-ukVz.js +0 -11
  284. package/public/docs/assets/chunks/@localSearchIndexroot.r9imX8Aq.js +0 -1
  285. package/public/docs/assets/contributor-guide_architecture_board-widgets.md.D0Kkk_r5.js +0 -1
  286. package/public/docs/assets/contributor-guide_architecture_detector-contract.md.caA46z-u.js +0 -1
  287. package/public/docs/assets/contributor-guide_architecture_detector-contract.md.caA46z-u.lean.js +0 -1
  288. package/public/docs/assets/contributor-guide_architecture_drill-down.md.CdtlKHl-.js +0 -1
  289. package/public/docs/assets/contributor-guide_architecture_drill-down.md.CdtlKHl-.lean.js +0 -1
  290. package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.BNp14LtL.js +0 -1
  291. package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.BNp14LtL.lean.js +0 -1
  292. package/public/docs/assets/contributor-guide_architecture_index.md.B9yZieYL.js +0 -1
  293. package/public/docs/assets/contributor-guide_architecture_overview.md.DBGo-bwn.js +0 -1
  294. package/public/docs/assets/contributor-guide_architecture_state-and-history.md.w4lwybdh.js +0 -1
  295. package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.CcvqxnZI.js +0 -1
  296. package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.CcvqxnZI.lean.js +0 -1
  297. package/public/docs/assets/contributor-guide_architecture_worker-protocol.md.CKeFttKj.js +0 -6
  298. package/public/docs/assets/contributor-guide_architecture_worker-protocol.md.CKeFttKj.lean.js +0 -1
  299. package/public/docs/assets/user-guide_alternative-log-retrieval.md.z-8npxNk.js +0 -7
  300. package/public/docs/assets/user-guide_alternative-log-retrieval.md.z-8npxNk.lean.js +0 -1
  301. package/public/docs/assets/user-guide_getting-started.md.BJW5oVhe.js +0 -24
  302. package/public/docs/assets/user-guide_getting-started.md.BJW5oVhe.lean.js +0 -1
  303. package/public/docs/assets/user-guide_mcp-tools.md.DcLiOa9t.js +0 -148
  304. package/public/docs/assets/user-guide_mcp-tools.md.DcLiOa9t.lean.js +0 -1
  305. package/public/docs/assets/user-guide_run-comparison.md.qin0a4IX.js +0 -1
  306. package/public/docs/assets/user-guide_run-comparison.md.qin0a4IX.lean.js +0 -1
  307. package/public/docs/assets/user-guide_understanding-findings.md.DzReyCXb.js +0 -1
  308. package/public/docs/assets/user-guide_understanding-findings.md.DzReyCXb.lean.js +0 -1
  309. /package/public/docs/assets/{contributor-guide_architecture_overview.md.DBGo-bwn.lean.js → contributor-guide_architecture_overview.md.C1sSvm9i.lean.js} +0 -0
@@ -9,11 +9,11 @@
9
9
  <link rel="preload stylesheet" href="/docs/assets/style.tmDhD3c8.css" as="style">
10
10
  <link rel="preload stylesheet" href="/docs/vp-icons.css" as="style">
11
11
 
12
- <script type="module" src="/docs/assets/app.yjczoibf.js"></script>
12
+ <script type="module" src="/docs/assets/app.eYTPqTeq.js"></script>
13
13
  <link rel="preload" href="/docs/assets/inter-roman-latin.Di8DUHzh.woff2" as="font" type="font/woff2" crossorigin="">
14
- <link rel="modulepreload" href="/docs/assets/chunks/theme.DFcAl8Ws.js">
14
+ <link rel="modulepreload" href="/docs/assets/chunks/theme.DsTNE2pE.js">
15
15
  <link rel="modulepreload" href="/docs/assets/chunks/framework.DSg0KOwT.js">
16
- <link rel="modulepreload" href="/docs/assets/contributor-guide_architecture_widget-rendering.md.CcvqxnZI.lean.js">
16
+ <link rel="modulepreload" href="/docs/assets/contributor-guide_architecture_widget-rendering.md.BOZ1azXG.lean.js">
17
17
  <script>(()=>{const c=`${`[${["data","shuffle","product","bar"].join("-")}]`}, .VPNav, .VPLocalNav`,a=24,d=document.documentElement,l=t=>{const e=getComputedStyle(t);return e.display==="none"||t.getClientRects().length===0?0:e.position==="fixed"?t.getBoundingClientRect().bottom:e.position==="sticky"?(parseFloat(e.top)||0)+t.offsetHeight:0},u=()=>{try{return location.hash?document.getElementById(decodeURIComponent(location.hash.slice(1))):null}catch{return null}};let r=null;const s=()=>{const t=Array.from(document.querySelectorAll(c),l),e=Math.ceil(Math.max(0,...t))+a;if(e===r)return;const n=r;r=e,d.style.setProperty("--sf-anchor-offset",`${r}px`);const o=n===null?null:u();if(!o)return;const f=(parseFloat(getComputedStyle(o).paddingTop)||0)+2;Math.abs(o.getBoundingClientRect().top-n)<=f&&o.scrollIntoView()},i=()=>{const t=document.createElement("div");t.setAttribute("data-sf-anchor-offset",""),t.setAttribute("aria-hidden","true"),document.body.appendChild(t);const e=window.ResizeObserver?new ResizeObserver(s):null,n=()=>{e&&document.querySelectorAll(c).forEach(o=>e.observe(o)),s()};n(),new MutationObserver(n).observe(document.body,{childList:!0,subtree:!0}),window.addEventListener("resize",s),window.addEventListener("load",s)};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",i,{once:!0}):i()})();</script>
18
18
  <link rel="icon" type="image/svg+xml" href="/docs/favicon.svg">
19
19
  <link rel="preconnect" href="https://fonts.googleapis.com">
@@ -23,8 +23,8 @@
23
23
  <script id="check-mac-os">document.documentElement.classList.toggle("mac",/Mac|iPhone|iPod|iPad/i.test(navigator.platform));</script>
24
24
  </head>
25
25
  <body>
26
- <div id="app"><div class="Layout" data-v-5d98c3a5><!--[--><!--]--><!--[--><span tabindex="-1" data-v-0b0ada53></span><a href="#VPContent" class="VPSkipLink visually-hidden" data-v-0b0ada53>Skip to content</a><!--]--><!----><header class="VPNav" data-v-5d98c3a5 data-v-ae24b3ad><div class="VPNavBar" data-v-ae24b3ad data-v-6aa21345><div class="wrapper" data-v-6aa21345><div class="container" data-v-6aa21345><div class="title" data-v-6aa21345><div class="VPNavBarTitle has-sidebar" data-v-6aa21345 data-v-1168a8e4><a class="title" href="/docs/" data-v-1168a8e4><!--[--><!--]--><!--[--><img class="VPImage logo" src="/docs/favicon.svg" alt data-v-8426fc1a><!--]--><span data-v-1168a8e4>SparkForensics</span><!--[--><!--]--></a></div></div><div class="content" data-v-6aa21345><div class="content-body" data-v-6aa21345><!--[--><!--]--><div class="VPNavBarSearch search" data-v-6aa21345><!--[--><!----><div id="local-search"><button type="button" class="DocSearch DocSearch-Button" aria-label="Search"><span class="DocSearch-Button-Container"><span class="vp-icon DocSearch-Search-Icon"></span><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"><kbd class="DocSearch-Button-Key"></kbd><kbd class="DocSearch-Button-Key">K</kbd></span></button></div><!--]--></div><nav aria-labelledby="main-nav-aria-label" class="VPNavBarMenu menu" data-v-6aa21345 data-v-dc692963><span id="main-nav-aria-label" class="visually-hidden" data-v-dc692963> Main Navigation </span><!--[--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/docs/user-guide/getting-started.html" tabindex="0" data-v-dc692963 data-v-e56f3d57><!--[--><span data-v-e56f3d57>User Guide</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/docs/contributor-guide/development-setup.html" tabindex="0" data-v-dc692963 data-v-e56f3d57><!--[--><span data-v-e56f3d57>Contributor Guide</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/docs/tuning-reference/" tabindex="0" data-v-dc692963 data-v-e56f3d57><!--[--><span data-v-e56f3d57>Tuning Reference</span><!--]--></a><!--]--><!--]--></nav><!----><div class="VPNavBarAppearance appearance" data-v-6aa21345 data-v-6c893767><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title aria-checked="false" data-v-6c893767 data-v-5337faa4 data-v-1d5665e3><span class="check" data-v-1d5665e3><span class="icon" data-v-1d5665e3><!--[--><span class="vpi-sun sun" data-v-5337faa4></span><span class="vpi-moon moon" data-v-5337faa4></span><!--]--></span></span></button></div><!----><div class="VPFlyout VPNavBarExtra extra" data-v-6aa21345 data-v-bb2aa2f0 data-v-cf11d7a2><button type="button" class="button" aria-haspopup="true" aria-expanded="false" aria-label="extra navigation" data-v-cf11d7a2><span class="vpi-more-horizontal icon" data-v-cf11d7a2></span></button><div class="menu" data-v-cf11d7a2><div class="VPMenu" data-v-cf11d7a2 data-v-b98bc113><!----><!--[--><!--[--><!----><div class="group" data-v-bb2aa2f0><div class="item appearance" data-v-bb2aa2f0><p class="label" data-v-bb2aa2f0>Appearance</p><div class="appearance-action" data-v-bb2aa2f0><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title aria-checked="false" data-v-bb2aa2f0 data-v-5337faa4 data-v-1d5665e3><span class="check" data-v-1d5665e3><span class="icon" data-v-1d5665e3><!--[--><span class="vpi-sun sun" data-v-5337faa4></span><span class="vpi-moon moon" data-v-5337faa4></span><!--]--></span></span></button></div></div></div><!----><!--]--><!--]--></div></div></div><!--[--><!--]--><button type="button" class="VPNavBarHamburger hamburger" aria-label="mobile navigation" aria-expanded="false" aria-controls="VPNavScreen" data-v-6aa21345 data-v-e5dd9c1c><span class="container" data-v-e5dd9c1c><span class="top" data-v-e5dd9c1c></span><span class="middle" data-v-e5dd9c1c></span><span class="bottom" data-v-e5dd9c1c></span></span></button></div></div></div></div><div class="divider" data-v-6aa21345><div class="divider-line" data-v-6aa21345></div></div></div><!----></header><div class="VPLocalNav has-sidebar empty" data-v-5d98c3a5 data-v-a6f0e41e><div class="container" data-v-a6f0e41e><button class="menu" aria-expanded="false" aria-controls="VPSidebarNav" data-v-a6f0e41e><span class="vpi-align-left menu-icon" data-v-a6f0e41e></span><span class="menu-text" data-v-a6f0e41e>Menu</span></button><div class="VPLocalNavOutlineDropdown" style="--vp-vh:0px;" data-v-a6f0e41e data-v-8a42e2b4><button data-v-8a42e2b4>Return to top</button><!----></div></div></div><aside class="VPSidebar" data-v-5d98c3a5 data-v-319d5ca6><div class="curtain" data-v-319d5ca6></div><nav class="nav" id="VPSidebarNav" aria-labelledby="sidebar-aria-label" tabindex="-1" data-v-319d5ca6><span class="visually-hidden" id="sidebar-aria-label" data-v-319d5ca6> Sidebar Navigation </span><!--[--><!--]--><!--[--><div class="no-transition group" data-v-c40bc020><section class="VPSidebarItem level-0 has-active" data-v-c40bc020 data-v-b3fd67f8><div class="item" role="button" tabindex="0" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><h2 class="text" data-v-b3fd67f8>Contributor Guide</h2><!----></div><div class="items" data-v-b3fd67f8><!--[--><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/development-setup.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Development setup</p><!--]--></a><!----></div><!----></div><section class="VPSidebarItem level-1 collapsible collapsed is-link has-active" data-v-b3fd67f8><div class="item" tabindex="0" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/" data-v-b3fd67f8><!--[--><h3 class="text" data-v-b3fd67f8>Architecture</h3><!--]--></a><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-b3fd67f8><span class="vpi-chevron-right caret-icon" data-v-b3fd67f8></span></div></div><div class="items" data-v-b3fd67f8><!--[--><div class="VPSidebarItem level-2 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/overview.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Overview</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/worker-protocol.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Worker protocol</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/state-and-history.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>State & history intake</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/detector-contract.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Detector contract</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/impact-estimation.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Impact estimation</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/widget-rendering.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Widget rendering</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/board-widgets.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Board widgets</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/drill-down.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Drill-down</p><!--]--></a><!----></div><!----></div><!--]--></div></section><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/testing.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Testing & verification</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/contributing.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Contributing</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><!--]--><!--[--><!--]--></nav></aside><div class="VPContent has-sidebar" id="VPContent" data-v-5d98c3a5 data-v-1428d186><div class="VPDoc has-sidebar has-aside" data-v-1428d186 data-v-39a288b8><!--[--><!--]--><div class="container" data-v-39a288b8><div class="aside" data-v-39a288b8><div class="aside-curtain" data-v-39a288b8></div><div class="aside-container" data-v-39a288b8><div class="aside-content" data-v-39a288b8><div class="VPDocAside" data-v-39a288b8 data-v-3f215769><!--[--><!--]--><!--[--><!--]--><nav aria-labelledby="doc-outline-aria-label" class="VPDocAsideOutline" data-v-3f215769 data-v-a5bbad30><div class="content" data-v-a5bbad30><div class="outline-marker" data-v-a5bbad30></div><div aria-level="2" class="outline-title" id="doc-outline-aria-label" role="heading" data-v-a5bbad30>On this page</div><ul class="VPDocOutlineItem root" data-v-a5bbad30 data-v-b933a997><!--[--><!--]--></ul></div></nav><!--[--><!--]--><div class="spacer" data-v-3f215769></div><!--[--><!--]--><!----><!--[--><!--]--><!--[--><!--]--></div></div></div></div><div class="content" data-v-39a288b8><div class="content-container" data-v-39a288b8><!--[--><!--]--><main class="main" data-v-39a288b8><div style="position:relative;" class="vp-doc _docs_contributor-guide_architecture_widget-rendering" data-v-39a288b8><div><h1 id="widget-rendering" tabindex="-1">Widget rendering <a class="header-anchor" href="#widget-rendering" aria-label="Permalink to &quot;Widget rendering&quot;">​</a></h1><h2 id="widget-rendering-order" tabindex="-1">Render order <a class="header-anchor" href="#widget-rendering-order" aria-label="Permalink to &quot;Render order {#widget-rendering-order}&quot;">​</a></h2><p><code>src/view/Dashboard.tsx</code>&#39;s <code>FilteredBoard</code> renders inside a <code>&lt;main&gt;</code> that opens with <code>SampleRunNotice</code> (only while the bundled sample run is open), then <code>RunVerdict</code> (built from the unfiltered catalog), then a single <code>Scorecard</code> strip, then <code>FindingFilterBar</code> (Advanced view, or any active filter) and (only when an active filter empties both finding streams) <code>NoMatchBanner</code>, then (when the active filter doesn&#39;t empty the board) a two-tab <code>Tabs</code> (<code>src/components/ui/tabs.tsx</code>, a base-ui primitive): <strong>Findings</strong> and <strong>Full app report</strong>, the first grouped by impact band and the second always reachable. Both <code>TabsContent</code> panels pass <code>keepMounted</code>, so the inactive one is hidden (native <code>hidden</code> attribute) rather than unmounted: switching tabs keeps each widget&#39;s local state (e.g. ImpactBoard&#39;s expanded group) and scroll position.</p><p><code>region</code> on <code>RegistryEntry</code> (<code>src/view/detector-registry.tsx</code>) is read only to decide whether a widget always mounts: <code>isAlwaysMountedType()</code> flags the one <code>reference</code>-region type carved out as an always-mounted exception, <code>coreLocality</code> → <code>CoreUsageArea</code>. The other <code>reference</code>-region types stay ordinary finding-gated instead: <code>cacheUtilization</code>, <code>memoryUtilization</code>, and <code>utilization</code> are a product decision: a clean run on any of them isn&#39;t evidence worth surfacing unconditionally, so each collapses to a plain clean-check line like any other detector. The Findings tab (<code>src/view/widgets/ImpactBoard.tsx</code>) groups its content into two tiers: impact-banded rows and cards for every <code>REGISTRY</code> component and every recommendation row with at least one finding; and a collapsed &quot;Clean checks&quot; disclosure covering every remaining type with zero findings, built per detector <em>type</em> (<code>Object.keys(REGISTRY)</code>). The one always-mounted exception lives in Full app report (see &quot;ReferenceSection&quot; below), which stays structural-only.</p><p>Tags carry their own docs links; there is no separate legend widget. <code>TagBadge</code> (<code>src/view/ImpactBadge.tsx</code>) resolves its own tooltip. Its documentation anchor is the caller&#39;s <code>docAnchor</code> prop when that is a known anchor (call sites holding the finding pass <code>finding.docAnchor</code>; a widget header or grouped row passes <code>sharedDocAnchor(findings)</code>), else the type&#39;s single known anchor (the run interpretation&#39;s <code>detectors[type].docAnchor</code>, from <code>docAnchorForType</code> in <code>packages/core/src/detector-docs.ts</code>). The prop matters for <code>configAudit</code>, whose four entries carry different anchors, so the type lookup finds none. With an anchor, the pill itself links into the docs panel, and (density <code>advanced</code> only) a second icon link opens that tag&#39;s entry in <code>docs-site/user-guide/understanding-findings.md</code> (<code>findingGuideUrl</code>, <code>packages/core/src/docs-site-config.ts</code>) in the same in-app docs panel; its <code>target=&quot;_blank&quot;</code> only applies to a modifier-click or when no <code>DocsProvider</code> is mounted. When a type has no vendor-doc anchor (e.g. <code>incompleteRun</code>), the pill links straight to that same guide entry instead of rendering as inert text, and the second icon link is skipped as redundant. Either way, a linked pill gets a visible dotted underline, not just a hover tooltip. <code>TagBadge</code>&#39;s <code>plainBadge</code> prop suppresses both links together; Stage Summary&#39;s per-stage tag chips are the one caller that sets it, on a chip that sits inside its own investigate button. Findings-tab rows leave it unset, so their badge links stay real <code>&lt;a&gt;</code>s. The HTML export renders no badge links at all (<code>exportMode</code>).</p><h3 id="findings-tab" tabindex="-1">Findings tab <a class="header-anchor" href="#findings-tab" aria-label="Permalink to &quot;Findings tab&quot;">​</a></h3><p>Rendered by <code>FilteredBoard</code>&#39;s <code>TabsContent value=&quot;findings&quot;</code> (<code>src/view/widgets/ImpactBoard.tsx</code>): recommendation rows and active widget cards in one impact-ranked board. The recommendation-rollup logic lives in <code>src/view/widgets/FixTheseFirst.tsx</code> (its own file with directly-testable exports, not rendered as a standalone page section by <code>Dashboard.tsx</code>) and the active-widget logic lives in <code>src/view/widgets/Alerts.tsx</code> (same: not rendered standalone). <code>ImpactBoard</code> calls <code>FixTheseFirst.tsx</code>&#39;s exported <code>useFixTheseFirstData</code> (eligible findings, rollup groups, the top triage target) and <code>Alerts.tsx</code>&#39;s exported <code>computeActiveWidgets</code> (the ranked active-<code>REGISTRY</code> list). The rollup and its ranking are computed in core by <code>interpretRun</code> (<code>rankedRollup</code>/<code>rankFindings</code>, <code>packages/core/src/recommendation-rollup.ts</code>); <code>useFixTheseFirstData</code> reads it through <code>boardRollup</code> (<code>src/view/interpretation.ts</code>: the interpretation&#39;s carried groups when unfiltered, core <code>rankedRollup</code> recomputed over the kept findings under a filter). <code>ImpactBoard</code> then buckets rows by each group&#39;s <code>band</code> (its representative member&#39;s impact band, after core <code>rankFindings</code>, the same finding whose impact band its own badge already shows) and cards by each active widget&#39;s own <code>worstImpactBand</code>.</p><p>Eligible findings for the rollup are <code>catalog</code> ∪ <code>configFindings</code> that pass core <code>isEligible</code> (<code>packages/core/src/recommendation-rollup.ts</code>) and have a display type (<code>FINDING_DISPLAY_ORDER</code>): <code>incompleteRun</code> (a pipeline-completeness caveat, not an addressable fix) and every <code>dataUnavailable</code> evidence caveat (e.g. <code>memoryUtilization</code>&#39;s <code>memoryBand</code> variant, already covered by Evidence availability&#39;s own <code>executorMetrics</code> entry, <code>packages/core/src/evidence-availability.ts</code>) are excluded. Findings are grouped strictly by <code>finding.type</code> via <code>buildRecommendationRollup</code> (<code>packages/core/src/recommendation-rollup.ts</code>, further split within a type by impact kind and, for <code>resource</code>, unit: never merged across types); each resulting group becomes one row: a type with exactly one finding renders that finding directly, a type with more than one collapses into a summary row. Within an impact band, group order comes from <code>buildRecommendationRollup</code>&#39;s own sort: <code>time</code> groups (a real <code>wallClock</code> claim) first, ranked among themselves by their union-capped <code>recoverableMsHigh</code> descending; then <code>resource</code> groups (<code>rawWaste</code> but no <code>wallClock</code>); then <code>count</code> groups (neither), both of the latter two ranked by worst impact band, never by their incomparable raw magnitudes. A summary row&#39;s tag badge and action label come from the group&#39;s own highest-impact member (via the same three-tier comparator, core <code>rankFindings</code>); its sentence is the type-level <code>coreFindingGenericRecommendation</code>, not one member&#39;s own numbers. The trailing stat depends on the group&#39;s kind (<code>×N · &lt;time&gt; recoverable</code>, <code>×N · &lt;total&gt;</code> with the summed resource waste the CLI report also prints, or <code>×N</code> alone, adding a per-impact-band tally only when the group mixes bands). Clicking it expands straight to the group&#39;s full, impact-ranked list, with no intermediate &quot;worst-K&quot; step, paginated at 10 rows per page (<code>data-testid=&quot;fix-these-first-group-row&quot;</code>; no pager renders for a group of 10 or fewer findings; it appears once a group exceeds 10). A band&#39;s rollup rows render as a headerless three-column <code>Table</code> (<code>src/components/ui/table.tsx</code>): a single-finding row (<code>FindingRow</code>, <code>data-testid=&quot;fix-these-first-row&quot;</code>, <code>data-finding-type</code>) has three <code>TableCell</code>s: the impact dot + ALL-CAPS tag as a real <code>TagBadge</code> (not <code>plainBadge</code>: nothing wraps it, so its own docs links stay real <code>&lt;a&gt;</code>s, same as everywhere else on the board); a text block inside its own nested <code>&lt;button&gt;</code> (a short imperative action label, e.g. &quot;Reduce shuffle size&quot;, from <code>findingActionLabel</code> (<code>packages/core/src/finding-action-label.ts</code>), over the finding&#39;s own full <code>recommendation</code> sentence in smaller muted text, both wrapping rather than truncating); and a right-aligned monospace stage reference + impact figure (e.g. <code>St.49 · 20.1s</code>, via the shared <code>formatWallClockRange</code>/<code>formatRawWaste</code> in <code>packages/core/src/format-utils.ts</code>). That inner button, not the row, is the click target: it routes via <code>selectTriageTargetForFinding</code> (<code>src/view/triage-target.ts</code>), the same per-finding resolver Stage Summary Table&#39;s own control uses (see &quot;First investigation routing&quot; below); a <code>TypeGroupRow</code>&#39;s own inner button toggles its expand state instead (<code>aria-expanded</code>). An expanded group&#39;s members (<code>FindingInstanceRow</code>) omit the badge and span its cell (<code>colSpan={2}</code>): the location line, then the recommendation clamped to one line, then the figure in the third column. <code>ImpactBoard</code> owns which single group is expanded (<code>expandedGroupKey</code>); a <code>TypeGroupRow</code> owns only its page.</p><p>Each impact band (<code>ImpactBoard.tsx</code>&#39;s own <code>ImpactGroup</code>, one call per entry of <code>IMPACT_BAND_ORDER_LIST = [&#39;critical&#39;, &#39;warning&#39;, &#39;info&#39;]</code>) is a <code>&lt;section aria-labelledby&gt;</code> pointing at its <code>&lt;h2 id=&quot;impact-band-&lt;band&gt;-heading&quot; tabIndex={-1}&gt;</code> (Critical, Warning, Info; the top bar&#39;s count chip focuses it), a sibling of the panel&#39;s sr-only <code>&lt;h2&gt;Findings&lt;/h2&gt;</code>, and renders nothing (not even the heading) when it has neither a rollup row nor an active widget: a run with no critical findings has no &quot;Critical&quot; heading or section at all. Inside a band, rollup rows render first as the headerless <code>Table</code> described above, followed by that band&#39;s active <code>REGISTRY</code> widget cards (<code>computeActiveWidgets</code>&#39;s ranked list, filtered to this impact band) in their own <code>WidgetGrid</code>: every one of <code>orderedWidgets()</code>&#39;s <code>REGISTRY</code> components <em>except</em> the one always-mounted one below, with at least one finding in <code>catalog</code> ∪ <code>configFindings</code>. Within a band, active widgets keep <code>orderedWidgets()</code>&#39;s own order: the interpretation&#39;s <code>DetectorInfo</code> sorted by region (<code>action</code> first), then ascending detector <code>order</code>. In Basic view a band with both rows and cards folds its <code>WidgetGrid</code> behind one &quot;Show the evidence (N cards)&quot; disclosure, unmounted while closed; it opens itself (and stays open) when the active route target (<code>useActiveRouteTarget</code>) is one of its cards, and the card, mounting with the route still pending, opens and scrolls itself through <code>registerWidget</code>. A band with cards but no rows, and every band in Advanced view, shows its grid directly. <code>cacheUtilization</code>, <code>memoryUtilization</code>, and <code>utilization</code> are <code>reference</code>-region types but aren&#39;t always-mounted exceptions, so a Cache Storage, Memory Utilization, or Executor Utilization card with an active finding surfaces in its own impact band like any other active widget.</p><p>Below the impact bands, <code>Alerts.tsx</code>&#39;s exported <code>CleanChecks</code> renders a collapsed &quot;Clean checks&quot; disclosure of <code>CleanCheckRow</code> lines (<code>src/view/widgets/CleanCheckRow.tsx</code>: the type&#39;s tag chip and label, plus in Advanced view the threshold it was held to, the interpretation&#39;s <code>detectors[type].thresholdSummary</code> from <code>getThresholdSummary</code>, which states the detector&#39;s own numbers) built per detector <em>type</em> (every <code>REGISTRY</code> key except that one always-mounted key), under one &quot;Every check below passed.&quot; line (or &quot;No check could run on this log.&quot; when none did) and grouped by detector scope (Per-stage, App-level, SQL plan, Config checks). Types the log could not check (an <code>isEvidenceCaveat</code> finding of that type, every per-stage type when no stage finished, or the run-span types <code>RUN_SPAN_CHECK_TYPES</code> on an <code>incompleteRun</code> log, the same rule that keeps the verdict from calling the run clean) render first under <strong>Not checked on this log</strong> as <code>CleanCheckRow status=&quot;notRun&quot;</code>, drawn neutral rather than clean green, after the interpretation&#39;s <code>coverage.gaps</code> (<code>verdictGaps</code>) lines saying why and naming the setting to turn on. A clean run lands <code>cacheUtilization</code>, <code>memoryUtilization</code>, and <code>utilization</code> here too, same as any ordinary action-region type. Caching Opportunities, Config Audit, and the four Plan Advisor widgets (Redundant Plan Subtree, Excessive Small Files, Missed Broadcast Join, Oversized Broadcast Join) render through the ordinary active/clean paths above (see <a href="./board-widgets.html#board-widgets-app-and-plan-widgets">App- and plan-level widgets</a>). Core Usage by Locality (<code>coreLocality</code>, resolving to <code>CoreUsageArea</code>) is not in the Findings tab at all: it mounts unconditionally from <code>appModel</code> at the head of the Full app report&#39;s reference grid (<code>alwaysMountedWidgets()</code>/<code>isAlwaysMountedType()</code>), carrying its own impact-band indicator when a finding is active instead of collapsing to a clean-check line on a clean run. A <code>coreLocality</code> finding still lists in its impact band&#39;s recommendation rows, and its <strong>Show evidence</strong> switches to the Full app report tab.</p><h3 id="per-widget-list-sort-mode" tabindex="-1">Per-widget list sort mode <a class="header-anchor" href="#per-widget-list-sort-mode" aria-label="Permalink to &quot;Per-widget list sort mode&quot;">​</a></h3><p><code>src/view/impact-sort.ts</code> (<code>sumWallClockLow</code>, <code>hasSortableImpact</code>, <code>canToggleSort</code>, <code>byImpactDesc</code>, <code>stageIdOf</code>, <code>byStageAsc</code>), <code>src/view/useSortMode.ts</code> and <code>src/view/SortModeToggle.tsx</code> are a shared, opt-in set a Findings-tab active widget can use to let its own expanded, multi-row list default to potential-savings order (<code>wallClock.low</code> descending, the guaranteed-floor bound, not the optimistic <code>high</code>) instead of stage number. Each of the widgets below gets its <code>sortMode</code> (<code>SortMode</code>, <code>&#39;impact&#39; | &#39;stage&#39;</code>, default <code>&#39;impact&#39;</code>) from <code>useSortMode</code>, which forces impact order at Basic density, and renders a <code>SortModeToggle</code> in the <code>WidgetCard</code> <code>badges</code> slot, so it sits on the title row itself rather than floating in the body: a button alongside the widget&#39;s tag badges (a <code>WidgetCard</code> header renders <code>badges</code> beside the title heading, never inside another control). Every widget below carries the same <code>canToggleSort</code> check, so the toggle only actually renders for a type whose finding can carry a wall-clock estimate: <code>Skew.tsx</code>, <code>TinyTask.tsx</code> (<code>stageShape</code>&#39;s own rules never produce one, so <code>StageShape.tsx</code> carries the same check but it never fires), <code>ShuffleIO.tsx</code> (narrowed to <code>shuffle</code>), <code>PartitionSizing.tsx</code>, <code>Spill.tsx</code>, <code>GcPressure.tsx</code> (both its high-GC and low-GC sections, one shared toggle), <code>RetryWaste.tsx</code> (its siblings <code>StageFailed.tsx</code>/<code>TaskFailures.tsx</code> carry the same check, but <code>stageFailed</code>/<code>failures</code> are <code>estimateMethod: &#39;none&#39;</code>, so it never fires there either), <code>SlowHost.tsx</code>, <code>StageSlowness.tsx</code>, <code>Straggler.tsx</code>, <code>SpeculationWaste.tsx</code>, <code>ColdStart.tsx</code> (one widget per type, each sorting only its own flat issue list), and <code>DuplicatePlanSubtree.tsx</code>/ <code>SmallFiles.tsx</code>/<code>UnderBroadcast.tsx</code>/<code>OverBroadcast.tsx</code> (each reorders its own list by <code>stageIdOf</code>, the lowest stage id its finding touches). It is a two-segment <code>ToggleGroup</code> (Impact / Stage; optional <code>stageLabel</code>, default &quot;Stage&quot;, which no caller overrides), wrapped in <code>AdvancedOnly</code>: stage number is the one axis every one of these widgets&#39; items can be compared on. Neither mode sorts by impact band, which ranks findings by how bad they are, not by how much fixing them would save. A per-stage detector&#39;s own <code>finding.stageId</code> is the sort key directly; a sql-scope finding that spans several stages (<code>duplicatePlanSubtree</code>, <code>smallFiles</code>, <code>underBroadcast</code>, <code>overBroadcast</code>, via <code>stageIds</code>) sorts by the lowest stage id it touches (<code>stageIdOf</code>). The toggle renders only when <code>canToggleSort</code> holds: <code>hasSortableImpact</code> finds a wall-clock claim in the list, there is more than one row, and the card is open; re-sorting a list with no wall-clock claim would be a silent no-op. Both comparators return <code>0</code> when neither side has a comparable value, so those items keep their prior relative order (<code>Array.prototype.sort</code>&#39;s stability) rather than being shuffled. <code>FixTheseFirst</code>&#39;s own expanded group list (see &quot;Findings tab&quot; above) sorts by impact on its own and does not use this pattern; it has no stage-order toggle.</p><h3 id="gold-standard-row-expand-contract" tabindex="-1">Gold Standard row/expand contract <a class="header-anchor" href="#gold-standard-row-expand-contract" aria-label="Permalink to &quot;Gold Standard row/expand contract&quot;">​</a></h3><p>Every <code>REGISTRY</code> widget renders its content as N≥1 rows, using <code>Skew.tsx</code> as the reference implementation. A widget&#39;s data shape (chart, table, single app-scoped scalar) is never by itself a reason to skip this: the four named exceptions below (one histogram toggle, three partial exceptions) are the only ones. Any further exception must get its own entry here, justified in the same PR that introduces it.</p><ul><li>Tier A (universal): <code>WidgetCard</code> chrome, an impact-band left-border + dot, ALL-CAPS <code>TagBadge</code>s, a <code>SortModeToggle</code> when <code>canToggleSort</code> is true, a <code>finding-anchor</code> ref on any focusable/deep-linkable unit, and an early <code>null</code> return on an empty catalog filter (except Core Usage by Locality, the one always-mounted widget that renders unconditionally from <code>appModel</code> via <code>alwaysMountedWidgets()</code>/<code>isAlwaysMountedType()</code> rather than early-returning on an empty catalog filter).</li><li>Tier B (the row/expand pattern): a row&#39;s collapsed state shows its core metric(s), <code>ImpactEstimate</code>, any config-hint code snippet/list, and docs links, all unconditionally. The fix is stated once per card: <code>WidgetCard</code>&#39;s <code>fixFor</code> prints each distinct <code>coreFindingGenericRecommendation</code> above the body, so rows don&#39;t repeat a recommendation that restates their number. Every finding-row widget passes it, Shuffle I/O and the Skew, Stage Shape and Tiny Tasks groups (<code>StageFindingGroup</code>) included; Incomplete Run is the one that doesn&#39;t. Where the detector&#39;s sentence is a row&#39;s only measurement (Excessive Small Files, Missed Broadcast Join, Oversized Broadcast Join, Config Audit), the row shows its measured half, split off by core&#39;s <code>recommendationParts</code>. Three rows keep their own sentence because their type has no generic fix for them: Incomplete Run, and a data-unavailable Memory Utilization or Cache Storage row, which names the setting to enable. Confidence and evidence are unconditional too: <code>RowStatusCluster</code> (<code>src/view/RowStatusCluster.tsx</code>) is a single, fixed-position pill combining both. Every widget that carries one wraps it in <code>AdvancedOnly</code> (<code>src/view/AdvancedOnly.tsx</code>), so it only renders at the Advanced density tier: this is a content-visibility gate (Basic vs. Advanced), not a per-row collapse, and it&#39;s applied consistently across every adopting widget. No widget gates confidence or evidence behind a per-row click. A widget-header <code>RowStatusCluster</code> (passed as <code>WidgetCard</code>&#39;s <code>statusBadge</code> prop) carries one further, unrelated gate on top of <code>AdvancedOnly</code>: <code>open &amp;&amp; statusBadge</code> (<code>WidgetCard.tsx</code>&#39;s header) keeps it out of the collapsed header, matching the comment on <code>statusBadge</code> itself (&quot;kept separate so tag/impact badges stay visible in the collapsed summary while a <code>RowStatusCluster</code>-style ... marker doesn&#39;t&quot;). This is the card&#39;s own expand/collapse, not a separate reveal-on-click built for the cluster, and it composes with density as an AND: a header cluster needs both Advanced tier and an expanded card. A row with neither renders no cluster at all (<code>RowStatusCluster</code> returns <code>null</code>). Confidence renders as plain, non-interactive text (&quot;<code>{confidence} confidence</code>&quot;, no further suffix) with a <code>title</code> + <code>aria-describedby</code> tooltip carrying the full <code>validationRequired</code> detail: never itself a click target. Evidence renders as the cluster&#39;s one real click target: a button whose visible text is just the evidence label (e.g. &quot;SQL plan&quot;) but whose accessible name always carries the &quot;Evidence: ...&quot; prefix via an explicit <code>aria-label</code>, regardless of what else sits nearby. Clicking it calls <code>revealEvidence</code>. <code>RowStatusCluster</code>&#39;s fixed slot is the row&#39;s own stage-pill/title line (<code>justify-between</code>, cluster right-aligned), the same position in every adopting widget, not floating with the row&#39;s detail below. Lists longer than <code>VISIBLE_LIMIT</code> (6, <code>packages/core/src/format-utils.ts</code>) get page-based navigation (Previous/Next, <code>usePagedRows</code>/<code>RowPagination</code>, <code>src/view/usePagedRows.ts</code>/<code>src/view/RowPagination.tsx</code>) instead of rendering unconditionally: the one sanctioned cap mechanism across every Tier B widget. A Tier B widget whose rows are also triage-routable (wires <code>useFindingAnchor</code>) must pass its <code>routeIndex</code> (the routed finding&#39;s position in the paginated list) into <code>usePagedRows</code>, or a deep-linked route to a finding beyond the first page silently degrades to focusing the widget&#39;s disclosure title instead of the row.</li></ul><p>The following widgets carry a <code>RowStatusCluster</code>, all Advanced-only: <code>Spill.tsx</code> (confidence only), <code>ConfigAudit.tsx</code>&#39;s widget-header control (evidence only, a widget-wide constant rather than per-finding data, since the evidence key doesn&#39;t vary per row; also the control shown in the widget&#39;s empty-findings/no-data states), <code>MemoryUtilization.tsx</code>&#39;s widget header (confidence + evidence, one marker, since <code>executorMetrics</code> is widget-wide; <code>ExecutorUtilization.tsx</code>&#39;s rows carry neither), each of the four Plan Advisor widgets&#39; (<code>DuplicatePlanSubtree.tsx</code>, <code>SmallFiles.tsx</code>, <code>UnderBroadcast.tsx</code>, <code>OverBroadcast.tsx</code>) widget header (confidence + <code>sqlPlan</code> evidence, one marker per widget since each is its own finding type; no per-row control), <code>ScalingSim.tsx</code> (two call sites, evidence only), <code>CoreUsageArea.tsx</code>, <code>EfficiencyModel.tsx</code> and <code>PlanView.tsx</code> (three call sites) (these last confidence-only; all standalone rather than per-finding-row: a card-header badge, a widget-level single marker, a plan-tree node/summary-row marker, or an &quot;unavailable data&quot; message: the same component, same visual language, regardless of where it sits); and, because <code>skew</code>/<code>straggler</code>/<code>gc</code> disclose their own unvalidated noise-floor thresholds (confidence only, per-row, no <code>evidenceKey</code> passed), <code>GcPressure.tsx</code>&#39;s rows, <code>Straggler.tsx</code>&#39;s rows, <code>CachingOpportunity.tsx</code>&#39;s rows (the <code>cachingOpportunity</code> detector scales <code>confidence</code> per finding via <code>cachingReuseConfidence</code>, so the badge sits next to each row rather than as a single caveat below the table), and the shared <code>StageFindingGroup.tsx</code> row (adopted by <code>Skew.tsx</code>/<code>StageShape.tsx</code>/<code>TinyTask.tsx</code>, though only <code>skew</code> findings carry a <code>confidence</code> field).</p><p>One named exception:</p><ul><li><code>Skew.tsx</code>/<code>StageShape.tsx</code>/<code>TinyTask.tsx</code>: their per-row histogram toggle (<code>ExpandToggleButton</code> + <code>useExpandableRow</code>) gates a lazily-fetched duration histogram, unrelated to confidence/evidence. It sits alongside <code>RowStatusCluster</code> in the shared <code>StageFindingGroup.tsx</code> row; the two controls coexist per row. <code>ExpandToggleButton</code> is single-purpose (always the task-detail toggle) since these three are its only adopters.</li></ul><p>No toggle at all, unconditional content, since these widgets carry no confidence/evidence display: <code>IncompleteRun.tsx</code>, <code>ShuffleIO.tsx</code>, <code>PartitionSizing.tsx</code>, <code>StageFailed.tsx</code>, <code>TaskFailures.tsx</code>, <code>RetryWaste.tsx</code>, <code>ColdStart.tsx</code>, <code>SlowHost.tsx</code>, <code>StageSlowness.tsx</code>, <code>SpeculationWaste.tsx</code>, <code>ExecutorCountChart.tsx</code>, <code>ExecutorUtilization.tsx</code>, <code>JobFailures.tsx</code>, <code>CacheUtilization.tsx</code> and <code>AutoscalingChurn.tsx</code>.</p><p>Three named exceptions to specific pieces of the contract, not to the row wrapper or pagination, which still apply to all three:</p><ul><li><code>CoreUsageArea.tsx</code>&#39;s non-local-stage list is a derived stat breakdown (<code>computeCoreLocalityRatio</code>&#39;s <code>topStages</code>), not a <code>Finding[]</code>: there is no per-row finding or recommendation to reveal. Paginated like every other Tier B list, but with no per-row expand.</li><li><code>CacheUtilization.tsx</code>&#39;s RDD <code>&lt;Table&gt;</code> holds reference columns (name, storage level, partitions, memory, disk bytes) with no recommendation attached to any row. Paginated like every other Tier B list, but with no per-row expand. Its separate finding list below the table is full Tier B (unconditional, no toggle, per the paragraph above).</li><li><code>IncompleteRun.tsx</code> leads with no metric of its own: this app-scoped, single-finding widget has no natural lead figure distinct from its title/badge, unlike every other Gold Standard widget, which leads with a percentage, duration, or count.</li></ul><h3 id="referencesection" tabindex="-1">ReferenceSection <a class="header-anchor" href="#referencesection" aria-label="Permalink to &quot;ReferenceSection&quot;">​</a></h3><p>Rendered by <code>FilteredBoard</code>&#39;s <code>TabsContent value=&quot;full-report&quot;</code>, tab label &quot;Full app report&quot;. A plain <code>&lt;div&gt;</code>, structural-only, reading none of <code>REGISTRY</code>/<code>orderedWidgets()</code> at all. Its heading is a visually-hidden (<code>sr-only</code>) <code>&lt;h2&gt;Full app report&lt;/h2&gt;</code>, matching the tab&#39;s own label for the accessibility-tree heading outline without visually duplicating the tab text. Selecting the tab is the disclosure; the panel stays mounted but hidden while Findings is active (see &quot;Render order&quot; above). Its exact order is WallClock → Timeline → Executor Count Over Time (<code>ExecutorCountChart.tsx</code>, the executor add/remove count chart; not driven by any finding, so it isn&#39;t a <code>REGISTRY</code> entry) → StageTable → a <code>WidgetGrid</code> holding Core Usage by Locality (the one always-mounted <code>REGISTRY</code> card) → Evidence availability → ETL Phase Attribution → What-If Executor Scaling → Compute Efficiency → Core-Usage Distribution. Scorecard is not part of this section; it renders once, above the tabs themselves, in <code>FilteredBoard</code> (<code>src/view/Dashboard.tsx</code>), so it stays visible regardless of which tab is active rather than living inside either one (a three-tile run-info row: Wall-clock, Efficiency, Unused core time; see <a href="./board-widgets.html#board-widgets-app-and-plan-widgets">App- and plan-level widgets</a>). WallClock, Timeline, Executor Count Over Time and StageTable render expanded. The grid tiles beside them (Core Usage by Locality, Evidence availability and the report lenses) are <code>collapsedTile</code>s: they start collapsed at a uniform height and widen to the full row when opened; <code>revealEvidence</code> opens the Evidence availability tile. Core Usage by Locality is the only detector-driven <code>REGISTRY</code> card in this section, always mounted; Memory Utilization, Executor Utilization, and Cache Storage live in the Findings tab above and surface there only when they have an active finding.</p><p>The Evidence availability card is the persistent, non-impact-band ledger <a href="./worker-protocol.html#evidence-availability-contract">defined in the worker protocol</a>, not an alert or detector widget. An <code>Evidence: …</code> control appears only where a conclusion or unavailable report lens declares a relevant ledger dependency. <code>revealEvidence</code> (<code>src/view/EvidenceAvailabilityContext.tsx</code>) bumps a <code>revealRequest</code> counter, which <code>DashboardContent</code> (<code>src/view/Dashboard.tsx</code>) watches in a <code>useLayoutEffect</code> and translates into <code>setActiveTab(&#39;full-report&#39;)</code>. Each click is a new request, so it switches tabs again after the reader returns to Findings. The layout effect makes the tab visible before the provider&#39;s queued focus runs, since a row inside the hidden, still-mounted panel can&#39;t take focus. Mouse and keyboard activation switches to the Full app report tab, opens the ledger card, then focuses the referenced stable entry id (<code>evidence-availability-&lt;key&gt;</code>). The control explains evidence availability; it does not promise an unavailable signal would have produced a finding.</p><p><code>WidgetGrid</code> (both inside a Findings impact band and inside <code>ReferenceSection</code>): collapsed cards occupy one responsive column; opening a card expands it across the full row. Nested <code>WidgetCard</code>s are isolated from the parent grid state. A Findings-tab <code>FixTheseFirst</code> rollup row is a table row, not a <code>WidgetGrid</code> card, and carries no collapse/expand state of its own (<code>ImpactBoard</code> holds which group is expanded; a <code>TypeGroupRow</code> owns only its page). Full app report&#39;s non-<code>REGISTRY</code> tiles (ETL Phase Attribution, What-If Executor Scaling, Compute Efficiency, Core-Usage Distribution) sit in the same <code>WidgetGrid</code> as Core Usage by Locality and Evidence availability; only Core Usage by Locality&#39;s item carries a <code>widgetId</code> (see &quot;First investigation routing&quot; below), so it is the grid&#39;s one route destination.</p><p>Firm constraint: <code>orderedWidgets(detectors)</code> (in <code>src/view/detector-registry.tsx</code>) sorts the run interpretation&#39;s per-type <code>DetectorInfo</code> entries by region (<code>action</code> before <code>reference</code>), then ascending detector <code>order</code>; it does not walk the static <code>DETECTORS</code> import. Every <code>REGISTRY</code> entry maps to its own unique component, so it needs no component-identity dedup. <code>cacheUtilization</code>, <code>memoryUtilization</code>, and <code>utilization</code> (<code>reference</code>-region) can reach the active grid alongside <code>duplicatePlanSubtree</code> (<code>action</code>-region) and any other active finding, since none of them is the one always-mounted exception filtered out before that grid. <code>computeActiveWidgets</code> (<code>Alerts.tsx</code>) is its main consumer, filtered through <code>isAlwaysMountedType()</code> to exclude that one always-mounted component; the clean-check list bypasses <code>orderedWidgets()</code> entirely, iterating <code>Object.keys(REGISTRY)</code> per type instead (see &quot;Findings tab&quot; above). <code>DETECTORS</code>&#39; own array order and iteration, plus its cross-detector <code>suppressedBy</code> logic (e.g. <code>stageSlowness</code> deferring to <code>slowHost</code>, see <a href="./detector-contract.html#detector-contract">Detector contract</a>), live entirely in <code>packages/core/src/detectors.ts</code>/<code>packages/core/src/analyzer.ts</code>. Which React component each finding type resolves to is registry-owned.</p><h3 id="first-investigation-routing" tabindex="-1">First investigation routing <a class="header-anchor" href="#first-investigation-routing" aria-label="Permalink to &quot;First investigation routing&quot;">​</a></h3><p>The dashboard has a view-only per-finding route, not a single first-investigation pick: every Findings-tab recommendation row (rendered by <code>FixTheseFirst.tsx</code>&#39;s row components inside <code>ImpactBoard</code>) and the visible-finding control in Stage Summary (<code>StageTable.tsx</code>) each independently resolve and request their own target. <code>detector-registry.tsx</code> owns each routeable finding type&#39;s stable <code>widgetId</code>; its label comes from core <code>FINDING_PRESENTATION</code> (<code>findingName</code>) and its title from the widget itself, not from runtime position or raw detector type. Eligible targets have a mapped, routeable registry entry and a non-empty trimmed recommendation (<code>triageTargetFor</code>, <code>src/view/triage-target.ts</code>). The target&#39;s widget still renders every affected stage in its local order.</p><p><code>src/view/triage-target.ts</code> also exports <code>rankTriageTargets</code>, which orders <code>catalog</code> by the run interpretation&#39;s <code>savingsRank</code>. That rank comes from core&#39;s <code>rankBySavings</code> (<code>packages/core/src/run-verdict.ts</code>, shared with the CLI/MCP verdict), which orders every routeable finding by potential savings (<code>impactEstimate.wallClock.high</code>), a quantified estimate ahead of an unquantified one, then impact band, then widget display order (<code>FINDING_DISPLAY_ORDER</code>, core&#39;s copy of <code>orderedWidgets()</code>, which a <code>tests/view/detector-registry.test.tsx</code> case keeps equal), then catalog order; <code>selectTriageTarget</code> is its first entry. The occupancy-weighted impact estimator gives every finding a comparable <code>impactEstimate.wallClock</code> figure, and ranking by it keeps a &quot;start here&quot; pick off a <code>skew</code>/<code>straggler</code> finding ranked <code>critical</code> on a ratio basis whose occupancy-clipped recoverable time is near zero, when a lower-severity finding beside it has an order-of-magnitude larger recoverable-time estimate. Impact band only breaks ties. <code>RunVerdict</code>&#39;s next steps are built from this ranking (see &quot;Full render sequence&quot; below).</p><p>The route is re-derived from the current catalog before each asynchronous step. Its identity is object-reference equality against the current catalog (<code>catalog.includes(finding)</code> in <code>selectTriageTargetForFinding</code>, <code>src/view/triage-target.ts</code>) rather than a derived key: a new parse yields new finding object references, so reference presence alone detects staleness. It is not persisted and does not extend the core <code>Finding</code> contract; cross-session consumers (exports, URL-restored state) use the core <code>Finding.id</code> instead (see <a href="./worker-protocol.html#finding-identity">Finding identity</a>).</p><p><code>Dashboard</code> owns disclosure and navigation. Every routeable <code>REGISTRY</code> widget lives in the Findings tab except the always-mounted Core Usage by Locality, which lives in Full app report (see &quot;Render order&quot; above), so <code>requestRoute</code> (<code>DashboardContent</code>, <code>src/view/Dashboard.tsx</code>) starts by switching to the tab holding the target&#39;s widget (<code>isAlwaysMountedType(target.finding.type)</code> picks Full app report), whether the request came from a control already on that tab or from the other. Both panels stay mounted, but a route can still mount its target card fresh in the same commit as the route landing: a Basic-view band&#39;s &quot;Show the evidence&quot; fold mounts its cards only when opened, and opens itself when a route targets one of them (see &quot;Findings tab&quot; above). Two routing paths have to account for that: <code>reportWidgetOpen</code> does not clear a route just because the target <code>WidgetGridItem</code> hasn&#39;t registered yet in this commit (a child <code>WidgetCard</code> reports its own open state before its parent <code>WidgetGridItem</code>&#39;s registration effect runs on a fresh mount, so treating &quot;not registered yet&quot; as &quot;stale&quot; would drop the route before the parent even gets a chance to register it); and a paginated widget that must reveal a routed row passes its <code>routeIndex</code> into <code>usePagedRows</code> (<code>src/view/usePagedRows.ts</code>), which computes the page jump during render, not only in a post-commit effect, so the row exists in the DOM in time for this same commit&#39;s anchor lookup instead of one commit later.</p><p>After the target card commits open, an anchored row (<code>useFindingAnchor</code>) is scrolled to center and focused, then flashed for 2 s; without one, the card wrapper scrolls with a top margin below the fixed header and its disclosure button receives visible, temporary route focus (using instant scrolling for reduced motion). Requests use a latest-request-wins token (<code>tokenRef</code>, <code>DashboardContent</code>) that guards every step and revalidates; a stale, missing, unmounted, changed-catalog, or changed-active-file target cancels without redirecting, scrolling, or moving focus. <code>registerWidget</code>&#39;s cleanup deletes a registration only if the map still holds that same object, so StrictMode double-mounts don&#39;t drop a live one.</p><p>This route deliberately does not cover detector/parser or threshold changes, Stage Detail routing, or generic badges, tags, chips, or StagePills as controls. Config Audit findings route like any other: they resolve against <code>catalog</code> ∪ <code>configFindings</code>.</p><h3 id="widget-placement" tabindex="-1">Widget placement <a class="header-anchor" href="#widget-placement" aria-label="Permalink to &quot;Widget placement&quot;">​</a></h3><p>The Plan Explorer (<code>src/view/widgets/PlanExplorer.tsx</code>) is embedded inside a flagged stage&#39;s own row, not as a standalone widget. It has four call sites (<code>ShuffleIO.tsx</code>, <code>PartitionSizing.tsx</code>, <code>Spill.tsx</code>, and the shared <code>StageFindingGroup.tsx</code> row behind <code>Skew.tsx</code>/<code>StageShape.tsx</code>/<code>TinyTask.tsx</code>), each Advanced-density only and only when the stage&#39;s plan tree resolves. The first three have no row-expansion toggle, so the trigger renders as a direct sibling in the row body; <code>StageFindingGroup.tsx</code> renders it inside the expanded Task detail. The same stage&#39;s plan is also reachable from <code>StageDetailDialog</code>.</p><p>Plan detection logic belongs in a <code>packages/core/src/detectors.ts</code> entry (<code>scope:&#39;sql&#39;</code>) consuming <code>planTree</code>. <code>packages/core/src/plan-summary.ts</code> is display-only summarization; do not add detection heuristics there. It is best-effort: <code>summarizePlanTree</code> reads each resolved <code>planTree</code> node&#39;s <code>detail</code> with lenient regexes and silently omits a fragment it can&#39;t parse, never surfacing an error.</p><p><code>StageHeader.tsx</code> (<code>StagePill</code> plus a <code>&lt;span&gt;</code> naming the stage) vs. bare <code>StagePill</code>/<code>StagePillGroup</code> is a deliberate row-density choice, not an inconsistency: <code>StageHeader</code> is for single-stage-per-row detail widgets where naming the stage adds context (<code>SlowHost.tsx</code>, <code>StageSlowness.tsx</code>, <code>Straggler.tsx</code>, <code>SpeculationWaste.tsx</code>, <code>GcPressure.tsx</code>, <code>Skew.tsx</code>, <code>StageShape.tsx</code>, <code>TinyTask.tsx</code>, <code>PartitionSizing.tsx</code>), while bare <code>StagePill</code>/<code>StagePillGroup</code> is for compact, multi-row lists where many stages appear per widget (<code>StageFailed.tsx</code>, <code>TaskFailures.tsx</code>, <code>RetryWaste.tsx</code>, the four Plan Advisor widgets, <code>Spill.tsx</code>, and <code>StageTable.tsx</code>). <code>ShuffleIO.tsx</code> uses both in different parts of its own row, which is fine: it isn&#39;t a violation of the convention above.</p><p>Stage Summary Table (<code>src/view/widgets/StageTable.tsx</code>) defaults to the <strong>top 10 stages by duration</strong>, with a header toggle to <strong>problems only</strong> (stages carrying a finding). It renders inside the Full app report tab, not as a standalone board section.</p><h3 id="full-render-sequence" tabindex="-1">Full render sequence <a class="header-anchor" href="#full-render-sequence" aria-label="Permalink to &quot;Full render sequence&quot;">​</a></h3><p>Top to bottom, in <code>Dashboard.tsx</code>&#39;s <code>FilteredBoard</code>:</p><ol><li><code>SampleRunNotice</code> (<code>src/view/SampleRunNotice.tsx</code>), only while the bundled sample run is open (the landing&#39;s <strong>Try a sample run</strong> loads it under <code>SAMPLE_RUN_ID</code>, <code>src/view/sample-run.ts</code>) and never in the export bundle: says the board shows the sample, with <strong>Load my event log</strong> and a docs-panel link to the log-retrieval guide.</li><li><code>RunVerdict</code> (<code>src/view/widgets/RunVerdict.tsx</code>): the run&#39;s verdict title, a summary sentence, and up to three numbered next steps built by <code>buildRunVerdict</code> (<code>packages/core/src/run-verdict.ts</code>, the same code the CLI/MCP evidence report&#39;s <code>verdict</code> runs) via <code>buildNextSteps</code>. Steps group routeable eligible findings by location (one stage, one multi-stage finding type, or one app-level finding type and variant), ordered by <code>rankBySavings</code> (potential savings, then impact band, then widget order), the same ranking every other component uses; on a run with a failed job, failure steps (<code>stageFailed</code>, <code>jobFailureRate</code>) move ahead (see below), and no other type jumps the order. Each step shows its tag, action label, stage and savings, then &quot;What&#39;s happening:&quot; (the measurement) and &quot;What to try:&quot; (the fix), split from the recommendation by core <code>recommendationParts</code>; a step that merged other types at its location adds &quot;Also flagged here, likely the same cause: ...&quot;, and the summary says once that same-stage findings are grouped. Each step has <strong>Show evidence</strong>, <strong>Stage N details</strong> (when it has one stage) and <strong>Copy</strong>; &quot;N more places under Findings&quot; counts the places past the step limit. Basic view adds a collapsed &quot;New to Spark tuning?&quot; primer. An idle-capacity step (<code>utilization</code>, or <code>memoryUtilization</code>&#39;s <code>idleCores</code> variant only, never its heap variants) titles the verdict only when it ranks first; otherwise an idle share of at least <code>IDLE_NOTABLE_PCT</code> (40%) adds one summary sentence. The idle share (<code>verdictIdlePct</code>) is the figure that idle-capacity step itself reports, falling back to the Scorecard&#39;s Unused core time figure only when no step carries one, so the title and the step never disagree. Each step&#39;s savings figure is followed by what it counts (<code>savingsMeaning</code> in <code>packages/core/src/impact-format.ts</code>: run time for a wall-clock figure, otherwise the resource its <code>rawWaste</code> unit measures), and <strong>Copy next steps</strong> copies the whole verdict as a plain-text checklist (<code>planCopyText</code> in core <code>run-verdict.ts</code>). Always the unfiltered catalog: a board filter never changes the verdict. A step&#39;s <strong>Show evidence</strong> on a finding the active filter hides clears only the filter dimensions that hide it (<code>excludingDimensions</code> in <code>src/view/finding-filter.ts</code>, synced to the URL as usual) and shows a one-line notice naming what it cleared; the Topbar count chip&#39;s jump to its impact band (<code>jumpToFindings</code> in <code>Dashboard.tsx</code>) uses the same path. The clean-run message (&quot;No findings to fix right now.&quot;) lives here and shows only when no finding at all was emitted and the log lacked nothing a check needs (below); an <code>incompleteRun</code> finding always adds a sentence saying the figures cover only the captured part of the run, and titles the verdict (&quot;This log looks incomplete ...&quot;) when no other finding is eligible. Job results (<code>summarizeRunOutcome</code> in <code>packages/core/src/run-outcome.ts</code>) set the run outcome: with a failed job the title says the run failed, the verdict quotes the first line of Spark&#39;s recorded reason (a failed job&#39;s <code>stageFailed</code> value first, then any <code>stageFailed</code>, then the job exception), the run is never called clean, and <code>buildNextSteps</code> ranks <code>stageFailed</code> and <code>jobFailureRate</code> steps first (a failed job&#39;s stage leading). Evidence caveats (a finding with <code>dataUnavailable</code>, or one <code>isRealFinding</code> drops), a log with no finished stage, and an <code>incompleteRun</code> log (whose <code>RUN_SPAN_CHECK_TYPES</code> had no run length to measure) are gaps (<code>verdictGaps</code>, <code>packages/core/src/check-coverage.ts</code>): any gap keeps the run from being called clean, and a log with no finished stage and no finding gets its own title. The verdict card does not list the gaps; the Clean checks disclosure&#39;s &quot;Not checked on this log&quot; group does, each caveat by its own recommendation text, which names the setting to enable. In Advanced view each step adds an &quot;Estimate:&quot; line from <code>estimateProvenance</code> (<code>packages/core/src/impact-format.ts</code>, carried as the interpretation&#39;s per-finding <code>savings.provenance</code>: method, basis as a point figure or a floor-to-high range, ms raw waste only when the floor clipped it, a non-time raw waste as the resource measured; nothing for <code>estimateMethod: &#39;none&#39;</code> or a zero figure), the finding&#39;s <code>confidence</code> when not <code>high</code>, and the list ends with the ordering rule when it has more than one step.</li><li><code>Scorecard</code>: a three-tile run-info row (Wall-clock, Efficiency, Unused core time; Basic view captions say what each measures, and for Efficiency and Unused core time which direction is better; Advanced view shows the raw run/idle breakdown), rendered once regardless of which tab is active.</li><li><code>FindingFilterBar</code>, only in Advanced view or while a filter is active (plus <code>NoMatchBanner</code> when the active filter empties both finding streams).</li><li>A two-tab <code>Tabs</code> (skipped entirely when the active filter empties both finding streams; <code>NoMatchBanner</code> above already covers that case), tab labels <strong>Findings</strong> and <strong>Full app report</strong>: <ul><li><strong>Findings</strong> (<code>ImpactBoard</code>): one <code>&lt;section&gt;</code> per impact band in <code>Critical</code> → <code>Warning</code> → <code>Info</code> order, each rendering nothing when it has neither a recommendation row nor an active widget: a recommendation-rollup <code>Table</code> (one row per eligible-finding type, a display type, <code>incompleteRun</code> and evidence caveats excluded: a direct row for a type with one finding, an expandable, paginated summary row for a type with more than one) followed by a <code>WidgetGrid</code> of that band&#39;s active <code>REGISTRY</code> widgets (ranked by widget order: the interpretation&#39;s <code>DetectorInfo</code> by region, action-region components ahead of reference-region ones, then ascending detector order); then, below every impact band, a collapsed &quot;Clean checks&quot; disclosure of <code>CleanCheckRow</code> lines built per detector type (every remaining <code>REGISTRY</code> key with zero findings).</li><li><strong>Full app report</strong> (<code>ReferenceSection</code>): WallClock → Timeline → Executor Count Over Time → StageTable → a grid of Core Usage by Locality → Evidence availability → fixed report-lens tail (ETL Phase Attribution → What-If Executor Scaling → Compute Efficiency → Core-Usage Distribution). Structural-only, as above. Hidden (not unmounted) while Findings is active (see &quot;Render order&quot; above).</li></ul></li></ol><p>Report lenses carry no impact-band chip and self-hide when their input data is absent. Every widget self-wraps in <code>WidgetCard</code> (<code>src/view/WidgetCard.tsx</code>: an <code>&lt;h3&gt;</code> title, one level below the board&#39;s <code>&lt;h2&gt;</code> section headers) for the document outline, except Scorecard, which renders its own header band, and RunVerdict, a plain <code>&lt;section&gt;</code> with its own <code>&lt;h2&gt;</code> title.</p><p>Every card is collapsible: the title sits in a <code>CollapsibleTrigger</code> button with a chevron (<code>WidgetCard.tsx</code>), and each widget sets its own <code>defaultCollapsed</code> (most default to <code>true</code>; <code>ImpactBoard</code>&#39;s action-region grid forces <code>defaultCollapsed</code> on every <code>REGISTRY</code> widget instance it mounts, so a marginal finding doesn&#39;t default open). Route navigation (&quot;jump to this finding&quot;) goes through the grid coordinator instead of a fixed <code>tabIndex</code>: <code>WidgetGrid.tsx</code> tracks each card&#39;s disclosure button and bumps <code>openRequestGeneration</code> to force a collapsed card open and focus its trigger when the target finding has no anchored row of its own. How much a card shows beyond that is a board-wide choice, set by the Basic/Advanced density tier in the topbar.</p></div></div></main><footer class="VPDocFooter" data-v-39a288b8 data-v-e257564d><!--[--><!--]--><!----><nav class="prev-next" aria-labelledby="doc-footer-aria-label" data-v-e257564d><span class="visually-hidden" id="doc-footer-aria-label" data-v-e257564d>Pager</span><div class="pager" data-v-e257564d><a class="VPLink link pager-link prev" href="/docs/contributor-guide/architecture/impact-estimation.html" data-v-e257564d><!--[--><span class="desc" data-v-e257564d>Previous page</span><span class="title" data-v-e257564d>Impact estimation</span><!--]--></a></div><div class="pager" data-v-e257564d><a class="VPLink link pager-link next" href="/docs/contributor-guide/architecture/board-widgets.html" data-v-e257564d><!--[--><span class="desc" data-v-e257564d>Next page</span><span class="title" data-v-e257564d>Board widgets</span><!--]--></a></div></nav></footer><!--[--><!--]--></div></div></div><!--[--><!--]--></div></div><!----><!--[--><!--]--></div></div>
27
- <script>window.__VP_HASH_MAP__=JSON.parse("{\"contributor-guide_architecture_board-widgets.md\":\"D0Kkk_r5\",\"contributor-guide_architecture_detector-contract.md\":\"caA46z-u\",\"contributor-guide_architecture_drill-down.md\":\"CdtlKHl-\",\"contributor-guide_architecture_impact-estimation.md\":\"BNp14LtL\",\"contributor-guide_architecture_index.md\":\"B9yZieYL\",\"contributor-guide_architecture_overview.md\":\"DBGo-bwn\",\"contributor-guide_architecture_state-and-history.md\":\"w4lwybdh\",\"contributor-guide_architecture_widget-rendering.md\":\"CcvqxnZI\",\"contributor-guide_architecture_worker-protocol.md\":\"CKeFttKj\",\"contributor-guide_contributing.md\":\"CQMvGwFt\",\"contributor-guide_development-setup.md\":\"D9bu4j_c\",\"contributor-guide_testing.md\":\"By6XGR9a\",\"index.md\":\"CHJVslga\",\"tuning-reference_anti-patterns.md\":\"D08Uaod7\",\"tuning-reference_aqe.md\":\"C1Ae6A8u\",\"tuning-reference_bottleneck-broadcast-sizing.md\":\"6wjKuJNE\",\"tuning-reference_bottleneck-cold-start.md\":\"BVQbsuHP\",\"tuning-reference_bottleneck-duplicate-plan-subtree.md\":\"DtaYnaiF\",\"tuning-reference_bottleneck-failures.md\":\"Z1Tdmdd3\",\"tuning-reference_bottleneck-gc.md\":\"BCnXLf1n\",\"tuning-reference_bottleneck-job-failure-rate.md\":\"CcvQj2g9\",\"tuning-reference_bottleneck-memory-utilization.md\":\"B-C0-8h-\",\"tuning-reference_bottleneck-retry-waste.md\":\"DCnhWiqj\",\"tuning-reference_bottleneck-shuffle.md\":\"B5OMJCd3\",\"tuning-reference_bottleneck-skew.md\":\"Cvo6NQm_\",\"tuning-reference_bottleneck-slow-host.md\":\"ja5qr3wq\",\"tuning-reference_bottleneck-small-files.md\":\"Crjtb0i2\",\"tuning-reference_bottleneck-spill.md\":\"Bl8KtSRg\",\"tuning-reference_bottleneck-straggler.md\":\"B0VN8reT\",\"tuning-reference_bottleneck-tiny-tasks.md\":\"DQCftGhu\",\"tuning-reference_bottleneck-utilization.md\":\"BXDl30f9\",\"tuning-reference_caching.md\":\"CcaiMn_7\",\"tuning-reference_cluster-config.md\":\"DbWG1yZq\",\"tuning-reference_config.md\":\"YUqSpwFB\",\"tuning-reference_data-formats.md\":\"C5DX5xL4\",\"tuning-reference_index.md\":\"ZVIpQ6Dk\",\"tuning-reference_intro.md\":\"BSSaDlqx\",\"tuning-reference_joins.md\":\"BVhNCDnE\",\"tuning-reference_memory-model.md\":\"CfjFdgbl\",\"tuning-reference_metrics.md\":\"C64VNEL4\",\"tuning-reference_partitioning.md\":\"CtLK0xsS\",\"tuning-reference_pyspark.md\":\"D-92XgF8\",\"tuning-reference_shuffle.md\":\"Iq2v1oHJ\",\"tuning-reference_spark-architecture.md\":\"_qwnE_Sc\",\"tuning-reference_table-formats.md\":\"BSYEK-eN\",\"user-guide_alternative-log-retrieval.md\":\"z-8npxNk\",\"user-guide_getting-started.md\":\"BJW5oVhe\",\"user-guide_mcp-tools.md\":\"DcLiOa9t\",\"user-guide_run-comparison.md\":\"qin0a4IX\",\"user-guide_understanding-findings.md\":\"DzReyCXb\"}");window.__VP_SITE_DATA__=JSON.parse("{\"lang\":\"en-US\",\"dir\":\"ltr\",\"title\":\"SparkForensics\",\"description\":\"Docs for using and contributing to SparkForensics\",\"base\":\"/docs/\",\"head\":[],\"router\":{\"prefetchLinks\":true},\"appearance\":true,\"themeConfig\":{\"logo\":\"/favicon.svg\",\"nav\":[{\"text\":\"User Guide\",\"link\":\"/user-guide/getting-started\"},{\"text\":\"Contributor Guide\",\"link\":\"/contributor-guide/development-setup\"},{\"text\":\"Tuning Reference\",\"link\":\"/tuning-reference/\"}],\"sidebar\":{\"/tuning-reference/\":[{\"text\":\"Getting Started\",\"items\":[{\"text\":\"Introduction\",\"link\":\"/tuning-reference/intro\"}]},{\"text\":\"Optimization Guide\",\"items\":[{\"text\":\"Spark Execution Model\",\"link\":\"/tuning-reference/spark-architecture\"},{\"text\":\"Memory Management\",\"link\":\"/tuning-reference/memory-model\"},{\"text\":\"Partitioning\",\"link\":\"/tuning-reference/partitioning\"},{\"text\":\"Join Optimization\",\"link\":\"/tuning-reference/joins\"},{\"text\":\"Shuffle\",\"link\":\"/tuning-reference/shuffle\"},{\"text\":\"Data Formats\",\"link\":\"/tuning-reference/data-formats\"},{\"text\":\"Table Formats\",\"link\":\"/tuning-reference/table-formats\"},{\"text\":\"Caching & Persistence\",\"link\":\"/tuning-reference/caching\"},{\"text\":\"PySpark Specifics\",\"link\":\"/tuning-reference/pyspark\"},{\"text\":\"Adaptive Query Execution\",\"link\":\"/tuning-reference/aqe\"},{\"text\":\"Cluster Tuning\",\"link\":\"/tuning-reference/cluster-config\"},{\"text\":\"Anti-Patterns\",\"link\":\"/tuning-reference/anti-patterns\"}]},{\"text\":\"Detector Catalog\",\"items\":[{\"text\":\"Task Skew\",\"link\":\"/tuning-reference/bottleneck-skew\"},{\"text\":\"Shuffle I/O\",\"link\":\"/tuning-reference/bottleneck-shuffle\"},{\"text\":\"Memory / Disk Spill\",\"link\":\"/tuning-reference/bottleneck-spill\"},{\"text\":\"GC Pressure\",\"link\":\"/tuning-reference/bottleneck-gc\"},{\"text\":\"Cold Start\",\"link\":\"/tuning-reference/bottleneck-cold-start\"},{\"text\":\"Executor Utilization\",\"link\":\"/tuning-reference/bottleneck-utilization\"},{\"text\":\"Slow Host\",\"link\":\"/tuning-reference/bottleneck-slow-host\"},{\"text\":\"Task Failures\",\"link\":\"/tuning-reference/bottleneck-failures\"},{\"text\":\"Stragglers\",\"link\":\"/tuning-reference/bottleneck-straggler\"},{\"text\":\"Retry Waste\",\"link\":\"/tuning-reference/bottleneck-retry-waste\"},{\"text\":\"Tiny Tasks\",\"link\":\"/tuning-reference/bottleneck-tiny-tasks\"},{\"text\":\"Job Failure Rate\",\"link\":\"/tuning-reference/bottleneck-job-failure-rate\"},{\"text\":\"Memory Utilization\",\"link\":\"/tuning-reference/bottleneck-memory-utilization\"},{\"text\":\"Duplicate Plan Subtree\",\"link\":\"/tuning-reference/bottleneck-duplicate-plan-subtree\"},{\"text\":\"Small Files\",\"link\":\"/tuning-reference/bottleneck-small-files\"},{\"text\":\"Broadcast Sizing\",\"link\":\"/tuning-reference/bottleneck-broadcast-sizing\"}]},{\"text\":\"Reference\",\"items\":[{\"text\":\"Metrics Glossary\",\"link\":\"/tuning-reference/metrics\"},{\"text\":\"Spark Config Quick-Reference\",\"link\":\"/tuning-reference/config\"}]}],\"/user-guide/\":[{\"text\":\"User Guide\",\"items\":[{\"text\":\"Getting started\",\"link\":\"/user-guide/getting-started\"},{\"text\":\"Understanding findings\",\"link\":\"/user-guide/understanding-findings\"},{\"text\":\"Run comparison mode\",\"link\":\"/user-guide/run-comparison\"},{\"text\":\"MCP tools reference\",\"link\":\"/user-guide/mcp-tools\"},{\"text\":\"Finding your event log\",\"link\":\"/user-guide/alternative-log-retrieval\"}]}],\"/contributor-guide/\":[{\"text\":\"Contributor Guide\",\"items\":[{\"text\":\"Development setup\",\"link\":\"/contributor-guide/development-setup\"},{\"text\":\"Architecture\",\"link\":\"/contributor-guide/architecture/\",\"collapsed\":true,\"items\":[{\"text\":\"Overview\",\"link\":\"/contributor-guide/architecture/overview\"},{\"text\":\"Worker protocol\",\"link\":\"/contributor-guide/architecture/worker-protocol\"},{\"text\":\"State & history intake\",\"link\":\"/contributor-guide/architecture/state-and-history\"},{\"text\":\"Detector contract\",\"link\":\"/contributor-guide/architecture/detector-contract\"},{\"text\":\"Impact estimation\",\"link\":\"/contributor-guide/architecture/impact-estimation\"},{\"text\":\"Widget rendering\",\"link\":\"/contributor-guide/architecture/widget-rendering\"},{\"text\":\"Board widgets\",\"link\":\"/contributor-guide/architecture/board-widgets\"},{\"text\":\"Drill-down\",\"link\":\"/contributor-guide/architecture/drill-down\"}]},{\"text\":\"Testing & verification\",\"link\":\"/contributor-guide/testing\"},{\"text\":\"Contributing\",\"link\":\"/contributor-guide/contributing\"}]}]},\"search\":{\"provider\":\"local\"}},\"locales\":{},\"scrollOffset\":{\"selector\":\"[data-sf-anchor-offset]\",\"padding\":0},\"cleanUrls\":false}");</script>
26
+ <div id="app"><div class="Layout" data-v-5d98c3a5><!--[--><!--]--><!--[--><span tabindex="-1" data-v-0b0ada53></span><a href="#VPContent" class="VPSkipLink visually-hidden" data-v-0b0ada53>Skip to content</a><!--]--><!----><header class="VPNav" data-v-5d98c3a5 data-v-ae24b3ad><div class="VPNavBar" data-v-ae24b3ad data-v-6aa21345><div class="wrapper" data-v-6aa21345><div class="container" data-v-6aa21345><div class="title" data-v-6aa21345><div class="VPNavBarTitle has-sidebar" data-v-6aa21345 data-v-1168a8e4><a class="title" href="/docs/" data-v-1168a8e4><!--[--><!--]--><!--[--><img class="VPImage logo" src="/docs/favicon.svg" alt data-v-8426fc1a><!--]--><span data-v-1168a8e4>SparkForensics</span><!--[--><!--]--></a></div></div><div class="content" data-v-6aa21345><div class="content-body" data-v-6aa21345><!--[--><!--]--><div class="VPNavBarSearch search" data-v-6aa21345><!--[--><!----><div id="local-search"><button type="button" class="DocSearch DocSearch-Button" aria-label="Search"><span class="DocSearch-Button-Container"><span class="vp-icon DocSearch-Search-Icon"></span><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"><kbd class="DocSearch-Button-Key"></kbd><kbd class="DocSearch-Button-Key">K</kbd></span></button></div><!--]--></div><nav aria-labelledby="main-nav-aria-label" class="VPNavBarMenu menu" data-v-6aa21345 data-v-dc692963><span id="main-nav-aria-label" class="visually-hidden" data-v-dc692963> Main Navigation </span><!--[--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/docs/user-guide/getting-started.html" tabindex="0" data-v-dc692963 data-v-e56f3d57><!--[--><span data-v-e56f3d57>User Guide</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/docs/contributor-guide/development-setup.html" tabindex="0" data-v-dc692963 data-v-e56f3d57><!--[--><span data-v-e56f3d57>Contributor Guide</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/docs/tuning-reference/" tabindex="0" data-v-dc692963 data-v-e56f3d57><!--[--><span data-v-e56f3d57>Tuning Reference</span><!--]--></a><!--]--><!--]--></nav><!----><div class="VPNavBarAppearance appearance" data-v-6aa21345 data-v-6c893767><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title aria-checked="false" data-v-6c893767 data-v-5337faa4 data-v-1d5665e3><span class="check" data-v-1d5665e3><span class="icon" data-v-1d5665e3><!--[--><span class="vpi-sun sun" data-v-5337faa4></span><span class="vpi-moon moon" data-v-5337faa4></span><!--]--></span></span></button></div><!----><div class="VPFlyout VPNavBarExtra extra" data-v-6aa21345 data-v-bb2aa2f0 data-v-cf11d7a2><button type="button" class="button" aria-haspopup="true" aria-expanded="false" aria-label="extra navigation" data-v-cf11d7a2><span class="vpi-more-horizontal icon" data-v-cf11d7a2></span></button><div class="menu" data-v-cf11d7a2><div class="VPMenu" data-v-cf11d7a2 data-v-b98bc113><!----><!--[--><!--[--><!----><div class="group" data-v-bb2aa2f0><div class="item appearance" data-v-bb2aa2f0><p class="label" data-v-bb2aa2f0>Appearance</p><div class="appearance-action" data-v-bb2aa2f0><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title aria-checked="false" data-v-bb2aa2f0 data-v-5337faa4 data-v-1d5665e3><span class="check" data-v-1d5665e3><span class="icon" data-v-1d5665e3><!--[--><span class="vpi-sun sun" data-v-5337faa4></span><span class="vpi-moon moon" data-v-5337faa4></span><!--]--></span></span></button></div></div></div><!----><!--]--><!--]--></div></div></div><!--[--><!--]--><button type="button" class="VPNavBarHamburger hamburger" aria-label="mobile navigation" aria-expanded="false" aria-controls="VPNavScreen" data-v-6aa21345 data-v-e5dd9c1c><span class="container" data-v-e5dd9c1c><span class="top" data-v-e5dd9c1c></span><span class="middle" data-v-e5dd9c1c></span><span class="bottom" data-v-e5dd9c1c></span></span></button></div></div></div></div><div class="divider" data-v-6aa21345><div class="divider-line" data-v-6aa21345></div></div></div><!----></header><div class="VPLocalNav has-sidebar empty" data-v-5d98c3a5 data-v-a6f0e41e><div class="container" data-v-a6f0e41e><button class="menu" aria-expanded="false" aria-controls="VPSidebarNav" data-v-a6f0e41e><span class="vpi-align-left menu-icon" data-v-a6f0e41e></span><span class="menu-text" data-v-a6f0e41e>Menu</span></button><div class="VPLocalNavOutlineDropdown" style="--vp-vh:0px;" data-v-a6f0e41e data-v-8a42e2b4><button data-v-8a42e2b4>Return to top</button><!----></div></div></div><aside class="VPSidebar" data-v-5d98c3a5 data-v-319d5ca6><div class="curtain" data-v-319d5ca6></div><nav class="nav" id="VPSidebarNav" aria-labelledby="sidebar-aria-label" tabindex="-1" data-v-319d5ca6><span class="visually-hidden" id="sidebar-aria-label" data-v-319d5ca6> Sidebar Navigation </span><!--[--><!--]--><!--[--><div class="no-transition group" data-v-c40bc020><section class="VPSidebarItem level-0 has-active" data-v-c40bc020 data-v-b3fd67f8><div class="item" role="button" tabindex="0" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><h2 class="text" data-v-b3fd67f8>Contributor Guide</h2><!----></div><div class="items" data-v-b3fd67f8><!--[--><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/development-setup.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Development setup</p><!--]--></a><!----></div><!----></div><section class="VPSidebarItem level-1 collapsible collapsed is-link has-active" data-v-b3fd67f8><div class="item" tabindex="0" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/" data-v-b3fd67f8><!--[--><h3 class="text" data-v-b3fd67f8>Architecture</h3><!--]--></a><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-b3fd67f8><span class="vpi-chevron-right caret-icon" data-v-b3fd67f8></span></div></div><div class="items" data-v-b3fd67f8><!--[--><div class="VPSidebarItem level-2 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/overview.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Overview</p><!--]--></a><!----></div><!----></div><section class="VPSidebarItem level-2 collapsible collapsed is-link" data-v-b3fd67f8><div class="item" tabindex="0" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/worker-protocol.html" data-v-b3fd67f8><!--[--><h4 class="text" data-v-b3fd67f8>Worker protocol</h4><!--]--></a><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-b3fd67f8><span class="vpi-chevron-right caret-icon" data-v-b3fd67f8></span></div></div><div class="items" data-v-b3fd67f8><!--[--><div class="VPSidebarItem level-3 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/worker-protocol/evidence-availability.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Evidence availability</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-3 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/worker-protocol/evidence-report.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Evidence report and finding identity</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-3 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/worker-protocol/cli-and-mcp.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>CLI and MCP server</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-3 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/worker-protocol/core-and-event-validation.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Core and event validation</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-2 collapsible collapsed is-link" data-v-b3fd67f8><div class="item" tabindex="0" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/state-and-history.html" data-v-b3fd67f8><!--[--><h4 class="text" data-v-b3fd67f8>State & history intake</h4><!--]--></a><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-b3fd67f8><span class="vpi-chevron-right caret-icon" data-v-b3fd67f8></span></div></div><div class="items" data-v-b3fd67f8><!--[--><div class="VPSidebarItem level-3 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/state-and-history/run-comparison-internals.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Run comparison internals</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-3 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/state-and-history/history-server-intake.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>History Server intake</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-2 collapsible collapsed is-link" data-v-b3fd67f8><div class="item" tabindex="0" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/detector-contract.html" data-v-b3fd67f8><!--[--><h4 class="text" data-v-b3fd67f8>Detector contract</h4><!--]--></a><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-b3fd67f8><span class="vpi-chevron-right caret-icon" data-v-b3fd67f8></span></div></div><div class="items" data-v-b3fd67f8><!--[--><div class="VPSidebarItem level-3 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/detector-contract/plan-attribution.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Plan attribution</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-3 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/detector-contract/threshold-tables.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Threshold tables</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-2 collapsible collapsed is-link" data-v-b3fd67f8><div class="item" tabindex="0" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/impact-estimation.html" data-v-b3fd67f8><!--[--><h4 class="text" data-v-b3fd67f8>Impact estimation</h4><!--]--></a><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-b3fd67f8><span class="vpi-chevron-right caret-icon" data-v-b3fd67f8></span></div></div><div class="items" data-v-b3fd67f8><!--[--><div class="VPSidebarItem level-3 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/impact-estimation/stage-union-rollup.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Stage union rollup</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-3 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/impact-estimation/caveats-tuning-and-coverage.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Caveats, tuning and coverage</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-2 collapsible collapsed is-link" data-v-b3fd67f8><div class="item" tabindex="0" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/widget-rendering.html" data-v-b3fd67f8><!--[--><h4 class="text" data-v-b3fd67f8>Widget rendering</h4><!--]--></a><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-b3fd67f8><span class="vpi-chevron-right caret-icon" data-v-b3fd67f8></span></div></div><div class="items" data-v-b3fd67f8><!--[--><div class="VPSidebarItem level-3 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/widget-rendering/findings-tab.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Findings tab</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-3 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/widget-rendering/list-sort-mode.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>List sort mode</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-3 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/widget-rendering/row-expand-contract.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Row/expand contract</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-3 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/widget-rendering/reference-section.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>ReferenceSection</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-3 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/widget-rendering/investigation-routing.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Investigation routing</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-2 collapsible collapsed is-link" data-v-b3fd67f8><div class="item" tabindex="0" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/board-widgets.html" data-v-b3fd67f8><!--[--><h4 class="text" data-v-b3fd67f8>Board widgets</h4><!--]--></a><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-b3fd67f8><span class="vpi-chevron-right caret-icon" data-v-b3fd67f8></span></div></div><div class="items" data-v-b3fd67f8><!--[--><div class="VPSidebarItem level-3 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/board-widgets/plan-advisor.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Plan Advisor</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-3 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/board-widgets/caching-and-memory.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Caching and memory widgets</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-3 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/board-widgets/visual-system-and-templating.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Visual system and templating</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-2 collapsible collapsed is-link" data-v-b3fd67f8><div class="item" tabindex="0" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/drill-down.html" data-v-b3fd67f8><!--[--><h4 class="text" data-v-b3fd67f8>Drill-down</h4><!--]--></a><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-b3fd67f8><span class="vpi-chevron-right caret-icon" data-v-b3fd67f8></span></div></div><div class="items" data-v-b3fd67f8><!--[--><div class="VPSidebarItem level-3 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/architecture/drill-down/plan-graph.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Plan graph</p><!--]--></a><!----></div><!----></div><!--]--></div></section><!--]--></div></section><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/testing.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Testing & verification</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/contributor-guide/contributing.html" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Contributing</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><!--]--><!--[--><!--]--></nav></aside><div class="VPContent has-sidebar" id="VPContent" data-v-5d98c3a5 data-v-1428d186><div class="VPDoc has-sidebar has-aside" data-v-1428d186 data-v-39a288b8><!--[--><!--]--><div class="container" data-v-39a288b8><div class="aside" data-v-39a288b8><div class="aside-curtain" data-v-39a288b8></div><div class="aside-container" data-v-39a288b8><div class="aside-content" data-v-39a288b8><div class="VPDocAside" data-v-39a288b8 data-v-3f215769><!--[--><!--]--><!--[--><!--]--><nav aria-labelledby="doc-outline-aria-label" class="VPDocAsideOutline" data-v-3f215769 data-v-a5bbad30><div class="content" data-v-a5bbad30><div class="outline-marker" data-v-a5bbad30></div><div aria-level="2" class="outline-title" id="doc-outline-aria-label" role="heading" data-v-a5bbad30>On this page</div><ul class="VPDocOutlineItem root" data-v-a5bbad30 data-v-b933a997><!--[--><!--]--></ul></div></nav><!--[--><!--]--><div class="spacer" data-v-3f215769></div><!--[--><!--]--><!----><!--[--><!--]--><!--[--><!--]--></div></div></div></div><div class="content" data-v-39a288b8><div class="content-container" data-v-39a288b8><!--[--><!--]--><main class="main" data-v-39a288b8><div style="position:relative;" class="vp-doc _docs_contributor-guide_architecture_widget-rendering" data-v-39a288b8><div><h1 id="widget-rendering" tabindex="-1">Widget rendering <a class="header-anchor" href="#widget-rendering" aria-label="Permalink to &quot;Widget rendering&quot;">​</a></h1><h2 id="widget-rendering-order" tabindex="-1">Render order <a class="header-anchor" href="#widget-rendering-order" aria-label="Permalink to &quot;Render order {#widget-rendering-order}&quot;">​</a></h2><p><code>src/view/Dashboard.tsx</code>&#39;s <code>FilteredBoard</code> renders inside a <code>&lt;main&gt;</code> that opens with <code>SampleRunNotice</code> (only while the bundled sample run is open), then <code>RunVerdict</code> (built from the unfiltered catalog), then a single <code>Scorecard</code> strip, then <code>FindingFilterBar</code> (Advanced view, or any active filter) and (only when an active filter empties both finding streams) <code>NoMatchBanner</code>, then (when the active filter doesn&#39;t empty the board) a two-tab <code>Tabs</code> (<code>src/components/ui/tabs.tsx</code>, a base-ui primitive): <strong>Findings</strong> and <strong>Full app report</strong>, the first grouped by impact band and the second always reachable. Both <code>TabsContent</code> panels pass <code>keepMounted</code>, so the inactive one is hidden (native <code>hidden</code> attribute) rather than unmounted: switching tabs keeps each widget&#39;s local state (e.g. ImpactBoard&#39;s expanded group) and scroll position.</p><p><code>region</code> on <code>RegistryEntry</code> (<code>src/view/detector-registry.tsx</code>) is read only to decide whether a widget always mounts: <code>isAlwaysMountedType()</code> flags the one <code>reference</code>-region type carved out as an always-mounted exception, <code>coreLocality</code> → <code>CoreUsageArea</code>. The other <code>reference</code>-region types stay ordinary finding-gated instead: <code>cacheUtilization</code>, <code>memoryUtilization</code>, and <code>utilization</code> are a product decision: a clean run on any of them isn&#39;t evidence worth surfacing unconditionally, so each collapses to a plain clean-check line like any other detector. The Findings tab (<code>src/view/widgets/ImpactBoard.tsx</code>) groups its content into two tiers: impact-banded rows and cards for every <code>REGISTRY</code> component and every recommendation row with at least one finding; and a collapsed &quot;Clean checks&quot; disclosure covering every remaining type with zero findings, built per detector <em>type</em> (<code>Object.keys(REGISTRY)</code>). The one always-mounted exception lives in Full app report (see &quot;ReferenceSection&quot; below), which stays structural-only.</p><p>Tags carry their own docs links; there is no separate legend widget. <code>TagBadge</code> (<code>src/view/ImpactBadge.tsx</code>) resolves its own tooltip. Its documentation anchor is the caller&#39;s <code>docAnchor</code> prop when that is a known anchor (call sites holding the finding pass <code>finding.docAnchor</code>; a widget header or grouped row passes <code>sharedDocAnchor(findings)</code>), else the type&#39;s single known anchor (the run interpretation&#39;s <code>detectors[type].docAnchor</code>, from <code>docAnchorForType</code> in <code>packages/core/src/detector-docs.ts</code>). The prop matters for <code>configAudit</code>, whose four entries carry different anchors, so the type lookup finds none. With an anchor, the pill itself links into the docs panel, and (density <code>advanced</code> only) a second icon link opens that tag&#39;s entry in <code>docs-site/user-guide/understanding-findings.md</code> (<code>findingGuideUrl</code>, <code>packages/core/src/docs-site-config.ts</code>) in the same in-app docs panel; its <code>target=&quot;_blank&quot;</code> only applies to a modifier-click or when no <code>DocsProvider</code> is mounted. When a type has no vendor-doc anchor (e.g. <code>incompleteRun</code>), the pill links straight to that same guide entry instead of rendering as inert text, and the second icon link is skipped as redundant. Either way, a linked pill gets a visible dotted underline, not just a hover tooltip. <code>TagBadge</code>&#39;s <code>plainBadge</code> prop suppresses both links together; Stage Summary&#39;s per-stage tag chips are the one caller that sets it, on a chip that sits inside its own investigate button. Findings-tab rows leave it unset, so their badge links stay real <code>&lt;a&gt;</code>s. The HTML export renders no badge links at all (<code>exportMode</code>).</p><h3 id="widget-placement" tabindex="-1">Widget placement <a class="header-anchor" href="#widget-placement" aria-label="Permalink to &quot;Widget placement&quot;">​</a></h3><p>The Plan Explorer (<code>src/view/widgets/PlanExplorer.tsx</code>) is embedded inside a flagged stage&#39;s own row, not as a standalone widget. It has four call sites (<code>ShuffleIO.tsx</code>, <code>PartitionSizing.tsx</code>, <code>Spill.tsx</code>, and the shared <code>StageFindingGroup.tsx</code> row behind <code>Skew.tsx</code>/<code>StageShape.tsx</code>/<code>TinyTask.tsx</code>), each Advanced-density only and only when the stage&#39;s plan tree resolves. The first three have no row-expansion toggle, so the trigger renders as a direct sibling in the row body; <code>StageFindingGroup.tsx</code> renders it inside the expanded Task detail. The same stage&#39;s plan is also reachable from <code>StageDetailDialog</code>.</p><p>Plan detection logic belongs in a <code>packages/core/src/detectors.ts</code> entry (<code>scope:&#39;sql&#39;</code>) consuming <code>planTree</code>. <code>packages/core/src/plan-summary.ts</code> is display-only summarization; do not add detection heuristics there. It is best-effort: <code>summarizePlanTree</code> reads each resolved <code>planTree</code> node&#39;s <code>detail</code> with lenient regexes and silently omits a fragment it can&#39;t parse, never surfacing an error.</p><p><code>StageHeader.tsx</code> (<code>StagePill</code> plus a <code>&lt;span&gt;</code> naming the stage) vs. bare <code>StagePill</code>/<code>StagePillGroup</code> is a deliberate row-density choice, not an inconsistency: <code>StageHeader</code> is for single-stage-per-row detail widgets where naming the stage adds context (<code>SlowHost.tsx</code>, <code>StageSlowness.tsx</code>, <code>Straggler.tsx</code>, <code>SpeculationWaste.tsx</code>, <code>GcPressure.tsx</code>, <code>Skew.tsx</code>, <code>StageShape.tsx</code>, <code>TinyTask.tsx</code>, <code>PartitionSizing.tsx</code>), while bare <code>StagePill</code>/<code>StagePillGroup</code> is for compact, multi-row lists where many stages appear per widget (<code>StageFailed.tsx</code>, <code>TaskFailures.tsx</code>, <code>RetryWaste.tsx</code>, the four Plan Advisor widgets, <code>Spill.tsx</code>, and <code>StageTable.tsx</code>). <code>ShuffleIO.tsx</code> uses both in different parts of its own row, which is fine: it isn&#39;t a violation of the convention above.</p><p>Stage Summary Table (<code>src/view/widgets/StageTable.tsx</code>) defaults to the <strong>top 10 stages by duration</strong>, with a header toggle to <strong>problems only</strong> (stages carrying a finding). It renders inside the Full app report tab, not as a standalone board section.</p><h3 id="full-render-sequence" tabindex="-1">Full render sequence <a class="header-anchor" href="#full-render-sequence" aria-label="Permalink to &quot;Full render sequence&quot;">​</a></h3><p>Top to bottom, in <code>Dashboard.tsx</code>&#39;s <code>FilteredBoard</code>:</p><ol><li><code>SampleRunNotice</code> (<code>src/view/SampleRunNotice.tsx</code>), only while the bundled sample run is open (the landing&#39;s <strong>Try a sample run</strong> loads it under <code>SAMPLE_RUN_ID</code>, <code>src/view/sample-run.ts</code>) and never in the export bundle: says the board shows the sample, with <strong>Load my event log</strong> and a docs-panel link to the log-retrieval guide.</li><li><code>RunVerdict</code> (<code>src/view/widgets/RunVerdict.tsx</code>): the run&#39;s verdict title, a summary sentence, and up to three numbered next steps built by <code>buildRunVerdict</code> (<code>packages/core/src/run-verdict.ts</code>, the same code the CLI/MCP evidence report&#39;s <code>verdict</code> runs) via <code>buildNextSteps</code>. Steps group routeable eligible findings by location (one stage, one multi-stage finding type, or one app-level finding type and variant), ordered by <code>rankBySavings</code> (potential savings, then impact band, then widget order), the same ranking every other component uses; on a run with a failed job, failure steps (<code>stageFailed</code>, <code>jobFailureRate</code>) move ahead (see below), and no other type jumps the order. Each step shows its tag, action label, stage and savings, then &quot;What&#39;s happening:&quot; (the measurement) and &quot;What to try:&quot; (the fix), split from the recommendation by core <code>recommendationParts</code>; a step that merged other types at its location adds &quot;Also flagged here, likely the same cause: ...&quot;, and the summary says once that same-stage findings are grouped. Each step has <strong>Show evidence</strong>, <strong>Stage N details</strong> (when it has one stage) and <strong>Copy</strong>; &quot;N more places under Findings&quot; counts the places past the step limit. Basic view adds a collapsed &quot;New to Spark tuning?&quot; primer. An idle-capacity step (<code>utilization</code>, or <code>memoryUtilization</code>&#39;s <code>idleCores</code> variant only, never its heap variants) titles the verdict only when it ranks first; otherwise an idle share of at least <code>IDLE_NOTABLE_PCT</code> (40%) adds one summary sentence. The idle share (<code>verdictIdlePct</code>) is the figure that idle-capacity step itself reports, falling back to the Scorecard&#39;s Unused core time figure only when no step carries one, so the title and the step never disagree. Each step&#39;s savings figure is followed by what it counts (<code>savingsMeaning</code> in <code>packages/core/src/impact-format.ts</code>: run time for a wall-clock figure, otherwise the resource its <code>rawWaste</code> unit measures), and <strong>Copy next steps</strong> copies the whole verdict as a plain-text checklist (<code>planCopyText</code> in core <code>run-verdict.ts</code>). Always the unfiltered catalog: a board filter never changes the verdict. A step&#39;s <strong>Show evidence</strong> on a finding the active filter hides clears only the filter dimensions that hide it (<code>excludingDimensions</code> in <code>src/view/finding-filter.ts</code>, synced to the URL as usual) and shows a one-line notice naming what it cleared; the Topbar count chip&#39;s jump to its impact band (<code>jumpToFindings</code> in <code>Dashboard.tsx</code>) uses the same path. The clean-run message (&quot;No findings to fix right now.&quot;) lives here and shows only when no finding at all was emitted and the log lacked nothing a check needs (below); an <code>incompleteRun</code> finding always adds a sentence saying the figures cover only the captured part of the run, and titles the verdict (&quot;This log looks incomplete ...&quot;) when no other finding is eligible. Job results (<code>summarizeRunOutcome</code> in <code>packages/core/src/run-outcome.ts</code>) set the run outcome: with a failed job the title says the run failed, the verdict quotes the first line of Spark&#39;s recorded reason (a failed job&#39;s <code>stageFailed</code> value first, then any <code>stageFailed</code>, then the job exception), the run is never called clean, and <code>buildNextSteps</code> ranks <code>stageFailed</code> and <code>jobFailureRate</code> steps first (a failed job&#39;s stage leading). Evidence caveats (a finding with <code>dataUnavailable</code>, or one <code>isRealFinding</code> drops), a log with no finished stage, and an <code>incompleteRun</code> log (whose <code>RUN_SPAN_CHECK_TYPES</code> had no run length to measure) are gaps (<code>verdictGaps</code>, <code>packages/core/src/check-coverage.ts</code>): any gap keeps the run from being called clean, and a log with no finished stage and no finding gets its own title. The verdict card does not list the gaps; the Clean checks disclosure&#39;s &quot;Not checked on this log&quot; group does, each caveat by its own recommendation text, which names the setting to enable. In Advanced view each step adds an &quot;Estimate:&quot; line from <code>estimateProvenance</code> (<code>packages/core/src/impact-format.ts</code>, carried as the interpretation&#39;s per-finding <code>savings.provenance</code>: method, basis as a point figure or a floor-to-high range, ms raw waste only when the floor clipped it, a non-time raw waste as the resource measured; nothing for <code>estimateMethod: &#39;none&#39;</code> or a zero figure), the finding&#39;s <code>confidence</code> when not <code>high</code>, and the list ends with the ordering rule when it has more than one step.</li><li><code>Scorecard</code>: a three-tile run-info row (Wall-clock, Efficiency, Unused core time; Basic view captions say what each measures, and for Efficiency and Unused core time which direction is better; Advanced view shows the raw run/idle breakdown), rendered once regardless of which tab is active.</li><li><code>FindingFilterBar</code>, only in Advanced view or while a filter is active (plus <code>NoMatchBanner</code> when the active filter empties both finding streams).</li><li>A two-tab <code>Tabs</code> (skipped entirely when the active filter empties both finding streams; <code>NoMatchBanner</code> above already covers that case), tab labels <strong>Findings</strong> and <strong>Full app report</strong>: <ul><li><strong>Findings</strong> (<code>ImpactBoard</code>): one <code>&lt;section&gt;</code> per impact band in <code>Critical</code> → <code>Warning</code> → <code>Info</code> order, each rendering nothing when it has neither a recommendation row nor an active widget: a recommendation-rollup <code>Table</code> (one row per eligible-finding type, a display type, <code>incompleteRun</code> and evidence caveats excluded: a direct row for a type with one finding, an expandable, paginated summary row for a type with more than one) followed by a <code>WidgetGrid</code> of that band&#39;s active <code>REGISTRY</code> widgets (ranked by widget order: the interpretation&#39;s <code>DetectorInfo</code> by region, action-region components ahead of reference-region ones, then ascending detector order); then, below every impact band, a collapsed &quot;Clean checks&quot; disclosure of <code>CleanCheckRow</code> lines built per detector type (every remaining <code>REGISTRY</code> key with zero findings).</li><li><strong>Full app report</strong> (<code>ReferenceSection</code>): WallClock → Timeline → Executor Count Over Time → StageTable → a grid of Core Usage by Locality → Evidence availability → fixed report-lens tail (ETL Phase Attribution → What-If Executor Scaling → Compute Efficiency → Core-Usage Distribution). Structural-only, as above. Hidden (not unmounted) while Findings is active (see &quot;Render order&quot; above).</li></ul></li></ol><p>Report lenses carry no impact-band chip and self-hide when their input data is absent. Every widget self-wraps in <code>WidgetCard</code> (<code>src/view/WidgetCard.tsx</code>: an <code>&lt;h3&gt;</code> title, one level below the board&#39;s <code>&lt;h2&gt;</code> section headers) for the document outline, except Scorecard, which renders its own header band, and RunVerdict, a plain <code>&lt;section&gt;</code> with its own <code>&lt;h2&gt;</code> title.</p><p>Every card is collapsible: the title sits in a <code>CollapsibleTrigger</code> button with a chevron (<code>WidgetCard.tsx</code>), and each widget sets its own <code>defaultCollapsed</code> (most default to <code>true</code>; <code>ImpactBoard</code>&#39;s action-region grid forces <code>defaultCollapsed</code> on every <code>REGISTRY</code> widget instance it mounts, so a marginal finding doesn&#39;t default open). Route navigation (&quot;jump to this finding&quot;) goes through the grid coordinator instead of a fixed <code>tabIndex</code>: <code>WidgetGrid.tsx</code> tracks each card&#39;s disclosure button and bumps <code>openRequestGeneration</code> to force a collapsed card open and focus its trigger when the target finding has no anchored row of its own. How much a card shows beyond that is a board-wide choice, set by the Basic/Advanced density tier in the topbar.</p></div></div></main><footer class="VPDocFooter" data-v-39a288b8 data-v-e257564d><!--[--><!--]--><!----><nav class="prev-next" aria-labelledby="doc-footer-aria-label" data-v-e257564d><span class="visually-hidden" id="doc-footer-aria-label" data-v-e257564d>Pager</span><div class="pager" data-v-e257564d><a class="VPLink link pager-link prev" href="/docs/contributor-guide/architecture/impact-estimation/caveats-tuning-and-coverage.html" data-v-e257564d><!--[--><span class="desc" data-v-e257564d>Previous page</span><span class="title" data-v-e257564d>Caveats, tuning and coverage</span><!--]--></a></div><div class="pager" data-v-e257564d><a class="VPLink link pager-link next" href="/docs/contributor-guide/architecture/widget-rendering/findings-tab.html" data-v-e257564d><!--[--><span class="desc" data-v-e257564d>Next page</span><span class="title" data-v-e257564d>Findings tab</span><!--]--></a></div></nav></footer><!--[--><!--]--></div></div></div><!--[--><!--]--></div></div><!----><!--[--><!--]--></div></div>
27
+ <script>window.__VP_HASH_MAP__=JSON.parse("{\"contributor-guide_architecture_board-widgets.md\":\"YQcj5EI4\",\"contributor-guide_architecture_board-widgets_caching-and-memory.md\":\"8bH1HnZp\",\"contributor-guide_architecture_board-widgets_plan-advisor.md\":\"RlzvUmNm\",\"contributor-guide_architecture_board-widgets_visual-system-and-templating.md\":\"DYgUpIJB\",\"contributor-guide_architecture_detector-contract.md\":\"wj6Et6UD\",\"contributor-guide_architecture_detector-contract_plan-attribution.md\":\"yNex4bys\",\"contributor-guide_architecture_detector-contract_threshold-tables.md\":\"CKjWG56Y\",\"contributor-guide_architecture_drill-down.md\":\"CqZPrdo9\",\"contributor-guide_architecture_drill-down_plan-graph.md\":\"DwOozIEs\",\"contributor-guide_architecture_impact-estimation.md\":\"CZv_ZGOp\",\"contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md\":\"BUucRc0A\",\"contributor-guide_architecture_impact-estimation_stage-union-rollup.md\":\"DdCGtAGu\",\"contributor-guide_architecture_index.md\":\"tymxf1Fh\",\"contributor-guide_architecture_overview.md\":\"C1sSvm9i\",\"contributor-guide_architecture_state-and-history.md\":\"DyCBdjuM\",\"contributor-guide_architecture_state-and-history_history-server-intake.md\":\"C298hrHZ\",\"contributor-guide_architecture_state-and-history_run-comparison-internals.md\":\"CZA0JLtV\",\"contributor-guide_architecture_widget-rendering.md\":\"BOZ1azXG\",\"contributor-guide_architecture_widget-rendering_findings-tab.md\":\"BQXycK6G\",\"contributor-guide_architecture_widget-rendering_investigation-routing.md\":\"BGdiyElu\",\"contributor-guide_architecture_widget-rendering_list-sort-mode.md\":\"BuHKgH0C\",\"contributor-guide_architecture_widget-rendering_reference-section.md\":\"DWqC80bN\",\"contributor-guide_architecture_widget-rendering_row-expand-contract.md\":\"ZT89u9f1\",\"contributor-guide_architecture_worker-protocol.md\":\"BAnhi2-d\",\"contributor-guide_architecture_worker-protocol_cli-and-mcp.md\":\"DC1YqXBp\",\"contributor-guide_architecture_worker-protocol_core-and-event-validation.md\":\"DSVFPpWb\",\"contributor-guide_architecture_worker-protocol_evidence-availability.md\":\"CjimIDBY\",\"contributor-guide_architecture_worker-protocol_evidence-report.md\":\"DVGZOWvW\",\"contributor-guide_contributing.md\":\"CQMvGwFt\",\"contributor-guide_development-setup.md\":\"D9bu4j_c\",\"contributor-guide_testing.md\":\"By6XGR9a\",\"index.md\":\"CHJVslga\",\"tuning-reference_anti-patterns.md\":\"D08Uaod7\",\"tuning-reference_aqe.md\":\"C1Ae6A8u\",\"tuning-reference_bottleneck-broadcast-sizing.md\":\"6wjKuJNE\",\"tuning-reference_bottleneck-cold-start.md\":\"BVQbsuHP\",\"tuning-reference_bottleneck-duplicate-plan-subtree.md\":\"DtaYnaiF\",\"tuning-reference_bottleneck-failures.md\":\"Z1Tdmdd3\",\"tuning-reference_bottleneck-gc.md\":\"BCnXLf1n\",\"tuning-reference_bottleneck-job-failure-rate.md\":\"CcvQj2g9\",\"tuning-reference_bottleneck-memory-utilization.md\":\"B-C0-8h-\",\"tuning-reference_bottleneck-retry-waste.md\":\"DCnhWiqj\",\"tuning-reference_bottleneck-shuffle.md\":\"B5OMJCd3\",\"tuning-reference_bottleneck-skew.md\":\"Cvo6NQm_\",\"tuning-reference_bottleneck-slow-host.md\":\"ja5qr3wq\",\"tuning-reference_bottleneck-small-files.md\":\"Crjtb0i2\",\"tuning-reference_bottleneck-spill.md\":\"Bl8KtSRg\",\"tuning-reference_bottleneck-straggler.md\":\"B0VN8reT\",\"tuning-reference_bottleneck-tiny-tasks.md\":\"DQCftGhu\",\"tuning-reference_bottleneck-utilization.md\":\"BXDl30f9\",\"tuning-reference_caching.md\":\"CcaiMn_7\",\"tuning-reference_cluster-config.md\":\"DbWG1yZq\",\"tuning-reference_config.md\":\"YUqSpwFB\",\"tuning-reference_data-formats.md\":\"C5DX5xL4\",\"tuning-reference_index.md\":\"ZVIpQ6Dk\",\"tuning-reference_intro.md\":\"BSSaDlqx\",\"tuning-reference_joins.md\":\"BVhNCDnE\",\"tuning-reference_memory-model.md\":\"CfjFdgbl\",\"tuning-reference_metrics.md\":\"C64VNEL4\",\"tuning-reference_partitioning.md\":\"CtLK0xsS\",\"tuning-reference_pyspark.md\":\"D-92XgF8\",\"tuning-reference_shuffle.md\":\"Iq2v1oHJ\",\"tuning-reference_spark-architecture.md\":\"_qwnE_Sc\",\"tuning-reference_table-formats.md\":\"BSYEK-eN\",\"user-guide_alternative-log-retrieval.md\":\"CJpYvHGo\",\"user-guide_alternative-log-retrieval_managed-platforms.md\":\"DvDwHNtC\",\"user-guide_alternative-log-retrieval_ssh-bastion.md\":\"Gszzlplv\",\"user-guide_getting-started.md\":\"DYHHHkQR\",\"user-guide_getting-started_ci-and-automation.md\":\"OUQqw3mI\",\"user-guide_getting-started_reading-the-dashboard.md\":\"Obf3W3__\",\"user-guide_getting-started_regression-metric-keys.md\":\"B_gzr2Mw\",\"user-guide_getting-started_tuning-thresholds.md\":\"B_MyjyZm\",\"user-guide_mcp-tools.md\":\"C65SjWUL\",\"user-guide_mcp-tools_compare-runs.md\":\"Cc_gLoix\",\"user-guide_mcp-tools_diagnose-run.md\":\"BP1vnHA5\",\"user-guide_mcp-tools_evaluate-budgets.md\":\"BB0nDY3Z\",\"user-guide_mcp-tools_get-finding-documentation.md\":\"BtTTHNV6\",\"user-guide_mcp-tools_get-finding-evidence.md\":\"rKyu4wts\",\"user-guide_mcp-tools_get-reference-doc.md\":\"Yac4tH3u\",\"user-guide_mcp-tools_get-run-summary.md\":\"DpxZ4ZNp\",\"user-guide_mcp-tools_list-runs.md\":\"DpQPeCM3\",\"user-guide_run-comparison.md\":\"BUALkohi\",\"user-guide_run-comparison_how-stages-are-matched.md\":\"DSXGu2tA\",\"user-guide_understanding-findings.md\":\"C9YRruMT\"}");window.__VP_SITE_DATA__=JSON.parse("{\"lang\":\"en-US\",\"dir\":\"ltr\",\"title\":\"SparkForensics\",\"description\":\"Docs for using and contributing to SparkForensics\",\"base\":\"/docs/\",\"head\":[],\"router\":{\"prefetchLinks\":true},\"appearance\":true,\"themeConfig\":{\"logo\":\"/favicon.svg\",\"nav\":[{\"text\":\"User Guide\",\"link\":\"/user-guide/getting-started\"},{\"text\":\"Contributor Guide\",\"link\":\"/contributor-guide/development-setup\"},{\"text\":\"Tuning Reference\",\"link\":\"/tuning-reference/\"}],\"sidebar\":{\"/tuning-reference/\":[{\"text\":\"Getting Started\",\"items\":[{\"text\":\"Introduction\",\"link\":\"/tuning-reference/intro\"}]},{\"text\":\"Optimization Guide\",\"items\":[{\"text\":\"Spark Execution Model\",\"link\":\"/tuning-reference/spark-architecture\"},{\"text\":\"Memory Management\",\"link\":\"/tuning-reference/memory-model\"},{\"text\":\"Partitioning\",\"link\":\"/tuning-reference/partitioning\"},{\"text\":\"Join Optimization\",\"link\":\"/tuning-reference/joins\"},{\"text\":\"Shuffle\",\"link\":\"/tuning-reference/shuffle\"},{\"text\":\"Data Formats\",\"link\":\"/tuning-reference/data-formats\"},{\"text\":\"Table Formats\",\"link\":\"/tuning-reference/table-formats\"},{\"text\":\"Caching & Persistence\",\"link\":\"/tuning-reference/caching\"},{\"text\":\"PySpark Specifics\",\"link\":\"/tuning-reference/pyspark\"},{\"text\":\"Adaptive Query Execution\",\"link\":\"/tuning-reference/aqe\"},{\"text\":\"Cluster Tuning\",\"link\":\"/tuning-reference/cluster-config\"},{\"text\":\"Anti-Patterns\",\"link\":\"/tuning-reference/anti-patterns\"}]},{\"text\":\"Detector Catalog\",\"items\":[{\"text\":\"Task Skew\",\"link\":\"/tuning-reference/bottleneck-skew\"},{\"text\":\"Shuffle I/O\",\"link\":\"/tuning-reference/bottleneck-shuffle\"},{\"text\":\"Memory / Disk Spill\",\"link\":\"/tuning-reference/bottleneck-spill\"},{\"text\":\"GC Pressure\",\"link\":\"/tuning-reference/bottleneck-gc\"},{\"text\":\"Cold Start\",\"link\":\"/tuning-reference/bottleneck-cold-start\"},{\"text\":\"Executor Utilization\",\"link\":\"/tuning-reference/bottleneck-utilization\"},{\"text\":\"Slow Host\",\"link\":\"/tuning-reference/bottleneck-slow-host\"},{\"text\":\"Task Failures\",\"link\":\"/tuning-reference/bottleneck-failures\"},{\"text\":\"Stragglers\",\"link\":\"/tuning-reference/bottleneck-straggler\"},{\"text\":\"Retry Waste\",\"link\":\"/tuning-reference/bottleneck-retry-waste\"},{\"text\":\"Tiny Tasks\",\"link\":\"/tuning-reference/bottleneck-tiny-tasks\"},{\"text\":\"Job Failure Rate\",\"link\":\"/tuning-reference/bottleneck-job-failure-rate\"},{\"text\":\"Memory Utilization\",\"link\":\"/tuning-reference/bottleneck-memory-utilization\"},{\"text\":\"Duplicate Plan Subtree\",\"link\":\"/tuning-reference/bottleneck-duplicate-plan-subtree\"},{\"text\":\"Small Files\",\"link\":\"/tuning-reference/bottleneck-small-files\"},{\"text\":\"Broadcast Sizing\",\"link\":\"/tuning-reference/bottleneck-broadcast-sizing\"}]},{\"text\":\"Reference\",\"items\":[{\"text\":\"Metrics Glossary\",\"link\":\"/tuning-reference/metrics\"},{\"text\":\"Spark Config Quick-Reference\",\"link\":\"/tuning-reference/config\"}]}],\"/user-guide/\":[{\"text\":\"User Guide\",\"items\":[{\"text\":\"Getting started\",\"link\":\"/user-guide/getting-started\",\"collapsed\":true,\"items\":[{\"text\":\"Reading the dashboard\",\"link\":\"/user-guide/getting-started/reading-the-dashboard\"},{\"text\":\"CI and automation\",\"link\":\"/user-guide/getting-started/ci-and-automation\"},{\"text\":\"Tuning detector thresholds\",\"link\":\"/user-guide/getting-started/tuning-thresholds\"},{\"text\":\"Regression metric keys\",\"link\":\"/user-guide/getting-started/regression-metric-keys\"}]},{\"text\":\"Understanding findings\",\"link\":\"/user-guide/understanding-findings\"},{\"text\":\"Run comparison mode\",\"link\":\"/user-guide/run-comparison\",\"collapsed\":true,\"items\":[{\"text\":\"How stages are matched\",\"link\":\"/user-guide/run-comparison/how-stages-are-matched\"}]},{\"text\":\"MCP tools reference\",\"link\":\"/user-guide/mcp-tools\",\"collapsed\":true,\"items\":[{\"text\":\"diagnose_run\",\"link\":\"/user-guide/mcp-tools/diagnose-run\"},{\"text\":\"get_run_summary\",\"link\":\"/user-guide/mcp-tools/get-run-summary\"},{\"text\":\"evaluate_budgets\",\"link\":\"/user-guide/mcp-tools/evaluate-budgets\"},{\"text\":\"compare_runs\",\"link\":\"/user-guide/mcp-tools/compare-runs\"},{\"text\":\"get_finding_evidence\",\"link\":\"/user-guide/mcp-tools/get-finding-evidence\"},{\"text\":\"get_finding_documentation\",\"link\":\"/user-guide/mcp-tools/get-finding-documentation\"},{\"text\":\"get_reference_doc\",\"link\":\"/user-guide/mcp-tools/get-reference-doc\"},{\"text\":\"list_runs\",\"link\":\"/user-guide/mcp-tools/list-runs\"}]},{\"text\":\"Finding your event log\",\"link\":\"/user-guide/alternative-log-retrieval\",\"collapsed\":true,\"items\":[{\"text\":\"Managed platforms\",\"link\":\"/user-guide/alternative-log-retrieval/managed-platforms\"},{\"text\":\"Behind an SSH bastion\",\"link\":\"/user-guide/alternative-log-retrieval/ssh-bastion\"}]}]}],\"/contributor-guide/\":[{\"text\":\"Contributor Guide\",\"items\":[{\"text\":\"Development setup\",\"link\":\"/contributor-guide/development-setup\"},{\"text\":\"Architecture\",\"link\":\"/contributor-guide/architecture/\",\"collapsed\":true,\"items\":[{\"text\":\"Overview\",\"link\":\"/contributor-guide/architecture/overview\"},{\"text\":\"Worker protocol\",\"link\":\"/contributor-guide/architecture/worker-protocol\",\"collapsed\":true,\"items\":[{\"text\":\"Evidence availability\",\"link\":\"/contributor-guide/architecture/worker-protocol/evidence-availability\"},{\"text\":\"Evidence report and finding identity\",\"link\":\"/contributor-guide/architecture/worker-protocol/evidence-report\"},{\"text\":\"CLI and MCP server\",\"link\":\"/contributor-guide/architecture/worker-protocol/cli-and-mcp\"},{\"text\":\"Core and event validation\",\"link\":\"/contributor-guide/architecture/worker-protocol/core-and-event-validation\"}]},{\"text\":\"State & history intake\",\"link\":\"/contributor-guide/architecture/state-and-history\",\"collapsed\":true,\"items\":[{\"text\":\"Run comparison internals\",\"link\":\"/contributor-guide/architecture/state-and-history/run-comparison-internals\"},{\"text\":\"History Server intake\",\"link\":\"/contributor-guide/architecture/state-and-history/history-server-intake\"}]},{\"text\":\"Detector contract\",\"link\":\"/contributor-guide/architecture/detector-contract\",\"collapsed\":true,\"items\":[{\"text\":\"Plan attribution\",\"link\":\"/contributor-guide/architecture/detector-contract/plan-attribution\"},{\"text\":\"Threshold tables\",\"link\":\"/contributor-guide/architecture/detector-contract/threshold-tables\"}]},{\"text\":\"Impact estimation\",\"link\":\"/contributor-guide/architecture/impact-estimation\",\"collapsed\":true,\"items\":[{\"text\":\"Stage union rollup\",\"link\":\"/contributor-guide/architecture/impact-estimation/stage-union-rollup\"},{\"text\":\"Caveats, tuning and coverage\",\"link\":\"/contributor-guide/architecture/impact-estimation/caveats-tuning-and-coverage\"}]},{\"text\":\"Widget rendering\",\"link\":\"/contributor-guide/architecture/widget-rendering\",\"collapsed\":true,\"items\":[{\"text\":\"Findings tab\",\"link\":\"/contributor-guide/architecture/widget-rendering/findings-tab\"},{\"text\":\"List sort mode\",\"link\":\"/contributor-guide/architecture/widget-rendering/list-sort-mode\"},{\"text\":\"Row/expand contract\",\"link\":\"/contributor-guide/architecture/widget-rendering/row-expand-contract\"},{\"text\":\"ReferenceSection\",\"link\":\"/contributor-guide/architecture/widget-rendering/reference-section\"},{\"text\":\"Investigation routing\",\"link\":\"/contributor-guide/architecture/widget-rendering/investigation-routing\"}]},{\"text\":\"Board widgets\",\"link\":\"/contributor-guide/architecture/board-widgets\",\"collapsed\":true,\"items\":[{\"text\":\"Plan Advisor\",\"link\":\"/contributor-guide/architecture/board-widgets/plan-advisor\"},{\"text\":\"Caching and memory widgets\",\"link\":\"/contributor-guide/architecture/board-widgets/caching-and-memory\"},{\"text\":\"Visual system and templating\",\"link\":\"/contributor-guide/architecture/board-widgets/visual-system-and-templating\"}]},{\"text\":\"Drill-down\",\"link\":\"/contributor-guide/architecture/drill-down\",\"collapsed\":true,\"items\":[{\"text\":\"Plan graph\",\"link\":\"/contributor-guide/architecture/drill-down/plan-graph\"}]}]},{\"text\":\"Testing & verification\",\"link\":\"/contributor-guide/testing\"},{\"text\":\"Contributing\",\"link\":\"/contributor-guide/contributing\"}]}]},\"search\":{\"provider\":\"local\"}},\"locales\":{},\"scrollOffset\":{\"selector\":\"[data-sf-anchor-offset]\",\"padding\":0},\"cleanUrls\":false}");</script>
28
28
 
29
29
  </body>
30
30
  </html>