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
package/public/index.html CHANGED
@@ -23,14 +23,14 @@
23
23
  document.documentElement.setAttribute('data-theme', 'light');
24
24
  } catch (_) {}
25
25
  </script>
26
- <script type="module" crossorigin src="./assets/index-DaC8A2FS.js"></script>
26
+ <script type="module" crossorigin src="./assets/index-B-03mG7K.js"></script>
27
27
  <link rel="modulepreload" crossorigin href="./assets/rolldown-runtime-hePW80VL.js">
28
28
  <link rel="modulepreload" crossorigin href="./assets/charts-vendor-0VMaLVLN.js">
29
- <link rel="modulepreload" crossorigin href="./assets/plan-graph-vendor-B0sEms9Z.js">
30
- <link rel="modulepreload" crossorigin href="./assets/react-vendor-BAKt3vny.js">
31
- <link rel="modulepreload" crossorigin href="./assets/DisclosureContext-Ds0hcZFn.js">
29
+ <link rel="modulepreload" crossorigin href="./assets/plan-graph-vendor-BYl-AzN5.js">
30
+ <link rel="modulepreload" crossorigin href="./assets/react-vendor-DEdfrRTJ.js">
31
+ <link rel="modulepreload" crossorigin href="./assets/DisclosureContext-ZpE6CkHx.js">
32
32
  <link rel="stylesheet" crossorigin href="./assets/plan-graph-vendor-DLioOiRN.css">
33
- <link rel="stylesheet" crossorigin href="./assets/index-BcqTLKFp.css">
33
+ <link rel="stylesheet" crossorigin href="./assets/index-2h_TgUAb.css">
34
34
  </head>
35
35
  <body>
36
36
  <div id="root"></div>
@@ -90,7 +90,7 @@ export function findingId(f ) {
90
90
 
91
91
  // skew (either branch) and straggler both claim the stage's replayed tail recovery
92
92
  // (tailReplayRecoveryMs via tailRecoveryMs): the same slow-task tail reported by two detectors
93
- // (see "Overlap caveat: skew / straggler" in impact-estimation.md). skew's branch only changes
93
+ // (see "Overlap caveat: skew / straggler" in impact-estimation/caveats-tuning-and-coverage.md). skew's branch only changes
94
94
  // the fallback single-task delta on a stage without the replay, so every skew + straggler pair
95
95
  // on a stage is flagged. Flags both sides via validationRequired (rather than suppressing
96
96
  // either) so neither finding's own diagnostic value is lost; the flag rides the same
@@ -0,0 +1,80 @@
1
+ // DeltaTable API merges run no command node: each internal query is its own root execution, tied
2
+ // together only by a "MERGE operation" description and adjacent ids. One DeltaMerge write is
3
+ // reported per run of such executions, with a path only when no other merge can be mixed into it.
4
+ import { soleLogPath } from './delta-log-paths.js';
5
+
6
+
7
+
8
+
9
+
10
+ const API_MERGE_COMMAND = 'DeltaMerge';
11
+
12
+ // Delta labels its MERGE sub-queries "MERGE operation - <phase>", prefixed "Delta: " in some.
13
+ const MERGE_DESCRIPTION = /^(?:Delta: )?MERGE operation\b/;
14
+
15
+ // The sub-queries that write: "writing new files ...", "Rewriting N files". A group without one
16
+ // only scanned, and a scan may read the merge's source rather than its target.
17
+ const MERGE_WRITE_PHASE = /\b(?:re)?writing\b/i;
18
+
19
+
20
+
21
+ // Selected executions have a MERGE description. A SQL MERGE's own sub-queries carry the same
22
+ // description but belong to the MergeIntoCommand execution they share a root with, which is
23
+ // attributed through its own node (resolveDeltaCommandTarget), so they are left out. A sub-query
24
+ // of an API merge may have another of its executions as its root, so a root alone does not exclude.
25
+ function selectMergeExecutions(sql ) {
26
+ const isSqlMergeRoot = (id ) =>
27
+ id !== undefined && /^Execute\s+MergeIntoCommand\b/.test(sql.get(id)?.planTree?.name ?? '');
28
+ return [...sql.values()]
29
+ .filter((exec) => MERGE_DESCRIPTION.test(exec.description ?? '') && !isSqlMergeRoot(exec.rootExecutionId))
30
+ .sort((a, b) => a.id - b.id);
31
+ }
32
+
33
+ // A run of consecutive execution ids. Nothing in the log ties the members of one merge together,
34
+ // so adjacency is a guess, and a wrong one fuses or splits merges. Each way it can go wrong is
35
+ // marked `unclean` instead of attributed:
36
+ // - the ids of two merges that ran concurrently interleave, so their start-time spans overlap;
37
+ // - a start time is missing, so the span is unknown.
38
+ // - no member writes, so the one path it names may be a source's.
39
+ // Merges that ran back to back with no other execution between them still fuse into one run; the
40
+ // run then names two paths and gets no target.
41
+ function groupMerges(selected ) {
42
+ const groups = [];
43
+ for (const exec of selected) {
44
+ const last = groups[groups.length - 1];
45
+ if (last && last.members[last.members.length - 1].id + 1 === exec.id) last.members.push(exec);
46
+ else groups.push({ members: [exec], unclean: null });
47
+ }
48
+ const spans = groups.map(({ members }) => {
49
+ const starts = members.map((m) => m.startTime);
50
+ return starts.every((t) => typeof t === 'number') ? [Math.min(...starts), Math.max(...starts)] : null;
51
+ });
52
+ groups.forEach((group, i) => {
53
+ const mine = spans[i];
54
+ if (mine === null) { group.unclean = 'start time missing'; return; }
55
+ const overlaps = spans.some((other, j) => j !== i && other !== null && other[0] <= mine[1] && mine[0] <= other[1]);
56
+ if (overlaps) group.unclean = 'overlaps another merge in time';
57
+ else if (!group.members.some((m) => MERGE_WRITE_PHASE.test(m.description ?? ''))) group.unclean = 'no write phase';
58
+ });
59
+ return groups;
60
+ }
61
+
62
+ /** One DeltaMerge write per run of API merge executions; null target unless one path is certain. */
63
+ export function apiMergeWrites(sql ) {
64
+ return groupMerges(selectMergeExecutions(sql)).map((group) => {
65
+ const first = group.members[0].id;
66
+ const last = group.members[group.members.length - 1].id;
67
+ const resolved = group.unclean === null ? soleLogPath(group.members) : null;
68
+ const span = first === last ? `${first}` : `${first}-${last}`;
69
+ return {
70
+ sqlExecutionId: first,
71
+ nodeId: null,
72
+ command: API_MERGE_COMMAND,
73
+ recognized: true,
74
+ kind: resolved?.kind ?? null,
75
+ target: resolved?.target ?? null,
76
+ outputRows: null,
77
+ raw: `Delta MERGE operation, executions ${span}${group.unclean ? ` (${group.unclean})` : ''}`,
78
+ };
79
+ });
80
+ }
@@ -3,6 +3,7 @@ import { computeSkewRatio, ENTRY_BY_TYPE, } from '../det
3
3
  import { effectiveThresholds } from '../threshold-overrides.js';
4
4
  import { IMPACT_BAND_ORDER } from '../format-utils.js';
5
5
 
6
+ import { PAIR_DELTA_METRICS, } from '../stage-alignment.js';
6
7
 
7
8
 
8
9
 
@@ -14,13 +15,23 @@ const IMPACT_BANDS = Object.keys(IMPACT_BAND_ORDER) ;
14
15
 
15
16
 
16
17
 
18
+
19
+
20
+
21
+
22
+
17
23
 
24
+
25
+ export const STAGE_QUALITIES = ['exact', 'structural', 'aligned'];
26
+ export const DEFAULT_STAGE_BUDGET_QUALITIES = ['exact', 'structural'];
27
+ /** Metrics with a regression direction (higher is worse). Input and output volume are workload, not performance. */
28
+ export const NEUTRAL_PAIR_METRICS = new Set(['inputBytes', 'outputBytes']);
18
29
 
19
30
 
20
-
31
+
21
32
 
22
33
 
23
-
34
+
24
35
 
25
36
 
26
37
 
@@ -146,6 +157,50 @@ function checkRegression(comparison , maxRegressionPct
146
157
  : { name: 'max-regression', metric: regressionMetric, status: 'pass', detail: `Metric "${regressionMetric}" ${pctLabel}, within budget ${maxRegressionPct}%.` };
147
158
  }
148
159
 
160
+ function checkStageRegression(
161
+ comparison , { metric, maxPct } , qualities ,
162
+ ) {
163
+ const name = 'max-stage-regression' ;
164
+ if (!(PAIR_DELTA_METRICS ).includes(metric)) {
165
+ return { name, metric, status: 'inconclusive', detail: `Metric "${metric}" is not a paired-stage metric (expected one of: ${PAIR_DELTA_METRICS.join(', ')}).` };
166
+ }
167
+ if (NEUTRAL_PAIR_METRICS.has(metric)) {
168
+ return { name, metric, status: 'inconclusive', detail: `Metric "${metric}" measures workload volume, not performance: it has no regression direction to check.` };
169
+ }
170
+ const eligible = comparison.stagePairs.filter((p) => qualities.includes(p.quality));
171
+ if (eligible.length === 0) {
172
+ return { name, metric, status: 'inconclusive', detail: comparison.stagePairs.length === 0
173
+ ? 'No stages were paired between the two runs.'
174
+ : `None of the ${comparison.stagePairs.length} paired stage(s) has quality ${qualities.join(' or ')}.` };
175
+ }
176
+ const measured = eligible
177
+ .map((pair) => ({ pair, delta: pair.deltas[metric ] }))
178
+ .filter(({ delta }) => delta.baseline != null && delta.delta != null);
179
+ if (measured.length === 0) {
180
+ return { name, metric, status: 'inconclusive', detail: `Metric "${metric}" is unavailable for every eligible paired stage.` };
181
+ }
182
+ const regressions = measured
183
+ .filter(({ delta }) => delta.delta > 0)
184
+ .map(({ pair, delta }) => ({ pair, delta, pct: delta.baseline === 0 ? Infinity : (delta.delta / delta.baseline ) * 100 }))
185
+ .sort((a, b) => b.pct - a.pct || a.pair.pairId.localeCompare(b.pair.pairId));
186
+ const over = regressions.filter((r) => r.pct > maxPct);
187
+ const scope = `${measured.length} paired stage(s)`;
188
+ if (over.length === 0) {
189
+ const worst = regressions[0];
190
+ return { name, metric, status: 'pass', detail: worst
191
+ ? `No paired stage regressed "${metric}" beyond ${maxPct}% across ${scope}; the largest was ${formatPct(worst.pct)} (pair ${worst.pair.pairId}).`
192
+ : `No paired stage regressed "${metric}" across ${scope}.` };
193
+ }
194
+ const listed = over.slice(0, 3).map((r) => `pair ${r.pair.pairId} (${r.pair.quality}) ${formatPct(r.pct)}`).join(', ');
195
+ const more = over.length > 3 ? `, and ${over.length - 3} more` : '';
196
+ return { name, metric, status: 'violation',
197
+ detail: `${over.length} of ${scope} regressed "${metric}" beyond ${maxPct}%: ${listed}${more}.` };
198
+ }
199
+
200
+ function formatPct(pct ) {
201
+ return pct === Infinity ? 'from 0' : `+${pct.toFixed(1)}%`;
202
+ }
203
+
149
204
  function checkFailOnIntroduced(comparison , band ) {
150
205
  if (band !== 'all' && !IMPACT_BANDS.includes(band )) {
151
206
  return { name: 'fail-on-introduced', status: 'inconclusive', detail: `Impact band "${band}" is not recognized (expected "all" or one of ${IMPACT_BANDS.join(', ')}).` };
@@ -198,6 +253,11 @@ export function evaluateBudgets({ appModel, catalog, budgets, comparison, thresh
198
253
  for (const { metric, maxPct } of budgets.regressionBudgets ?? []) {
199
254
  pushComparisonBudget(results, comparison, 'max-regression', (c) => checkRegression(c, maxPct, metric), metric);
200
255
  }
256
+ const stageQualities = budgets.stageQualities ?? DEFAULT_STAGE_BUDGET_QUALITIES;
257
+ for (const budget of budgets.stageRegressionBudgets ?? []) {
258
+ pushComparisonBudget(results, comparison, 'max-stage-regression',
259
+ (c) => checkStageRegression(c, budget, stageQualities), budget.metric);
260
+ }
201
261
  if (budgets.failOnIntroduced !== undefined) {
202
262
  pushComparisonBudget(results, comparison, 'fail-on-introduced',
203
263
  (c) => checkFailOnIntroduced(c, budgets.failOnIntroduced ));
@@ -4,6 +4,9 @@
4
4
  import { readFileSync } from 'node:fs';
5
5
  import { resolve } from 'node:path';
6
6
  import { COMPARISON_METRIC_KEYS } from '../run-comparison.js';
7
+ import { PAIR_DELTA_METRICS } from '../stage-alignment.js';
8
+ import { NEUTRAL_PAIR_METRICS, STAGE_QUALITIES } from './budgets.js';
9
+
7
10
 
8
11
 
9
12
 
@@ -81,3 +84,35 @@ export function combineRegressionBudgets(sources )
81
84
  }
82
85
  return sources.map((s) => s.budget);
83
86
  }
87
+
88
+ /** Refuses a stage budget metric that is not a paired-stage metric or has no regression direction. */
89
+ export function assertStageBudgetMetric(metric , origin ) {
90
+ if (!(PAIR_DELTA_METRICS ).includes(metric)) {
91
+ throw new Error(`${origin}: unknown paired-stage metric "${metric}" (expected one of: ${PAIR_DELTA_METRICS.filter((m) => !NEUTRAL_PAIR_METRICS.has(m)).join(', ')}).`);
92
+ }
93
+ if (NEUTRAL_PAIR_METRICS.has(metric)) {
94
+ throw new Error(`${origin}: "${metric}" measures workload volume, not performance, so it has no regression direction to budget.`);
95
+ }
96
+ }
97
+
98
+ /** One `--stage-regression-budget` value, `<metric>:<pct>`, over the paired-stage metrics. */
99
+ export function parseStageRegressionBudgetFlag(spec ) {
100
+ const origin = `--stage-regression-budget "${spec}"`;
101
+ const colon = spec.indexOf(':');
102
+ if (colon === -1) throw new Error(`${origin}: expected <metric>:<pct>.`);
103
+ const metric = spec.slice(0, colon);
104
+ const pct = spec.slice(colon + 1);
105
+ assertStageBudgetMetric(metric, origin);
106
+ if (!PCT_PATTERN.test(pct)) throw new Error(`${origin}: "${pct}" is not a non-negative percentage.`);
107
+ return { metric, maxPct: Number(pct) };
108
+ }
109
+
110
+ /** The pair qualities a stage budget reads, from a comma list such as `exact,structural`. */
111
+ export function parseStageQualities(list , origin ) {
112
+ const qualities = list.split(',').map((q) => q.trim()).filter(Boolean);
113
+ const bad = qualities.find((q) => !(STAGE_QUALITIES ).includes(q));
114
+ if (qualities.length === 0 || bad !== undefined) {
115
+ throw new Error(`${origin}: ${bad === undefined ? 'no quality given' : `unknown quality "${bad}"`} (expected a list from: ${STAGE_QUALITIES.join(', ')}).`);
116
+ }
117
+ return qualities ;
118
+ }
@@ -0,0 +1,50 @@
1
+ import { buildComparison, } from './run-comparison.js';
2
+ import { comparisonVerdict, } from './comparison-verdict.js';
3
+ import { redactComparison } from './redact.js';
4
+
5
+
6
+
7
+
8
+ // The comparison a surface reports: the CLI's `comparison` object and the shared part of MCP
9
+ // compare_runs. A field added here reaches every surface.
10
+ // `stagePairs` is the one large field (a row per paired stage), so the `summary` view leaves it out.
11
+
12
+
13
+
14
+
15
+
16
+
17
+ /** `full` carries every field (the CLI); `summary` leaves out `stagePairs` (MCP, which returns them
18
+ * only when asked). */
19
+
20
+
21
+ /** The reported projection of a comparison. Key order is the CLI's JSON order. */
22
+ export function comparisonOutput(comparison , view = 'full') {
23
+ return {
24
+ verdict: comparisonVerdict(comparison),
25
+ confidence: comparison.confidence,
26
+ reason: comparison.reason,
27
+ matchedCoverage: comparison.matchedCoverage,
28
+ runtimeCoverage: comparison.runtimeCoverage,
29
+ metrics: comparison.metrics,
30
+ findings: comparison.findings,
31
+ comparisonSchemaVersion: comparison.comparisonSchemaVersion,
32
+ ...(view === 'full' ? { stagePairs: comparison.stagePairs } : {}),
33
+ unmatched: comparison.unmatched,
34
+ replanned: comparison.replanned,
35
+ bookkeepingStageIds: comparison.bookkeepingStageIds,
36
+ executionAlignment: comparison.executionAlignment,
37
+ };
38
+ }
39
+
40
+ /** Diffs the two runs and applies `redact` here, so no surface can skip it (stage names carry raw
41
+ * Spark text that can embed a host or IP). `comparison` is the (redacted) raw result for budgets
42
+ * and Markdown; `output` is the projection to serialize. */
43
+ export function buildComparisonOutput(
44
+ baseline , candidate ,
45
+ { redact, normalizePath, view } = {},
46
+ ) {
47
+ const built = buildComparison(baseline, candidate, { normalizePath });
48
+ const comparison = redact ? redactComparison(built) : built;
49
+ return { comparison, output: comparisonOutput(comparison, view) };
50
+ }
@@ -1 +1 @@
1
- ab36c5fdcdc1a32a4e6206459e843e8d2267a90e6f1de29671827315d81bc374
1
+ 7aaca3af812e0e5a4f4632ad7577fc438b589520a27422ce8c986b156de82260
@@ -0,0 +1,13 @@
1
+ // Delta write commands whose root node is a bare name in sparkPlanInfo on real logs. The parser keeps
2
+ // their `Arguments:` line (event-handlers.ts) and delta-targets.ts reads their target from it; the
3
+ // handler table there is typed by this list, so a command added here must get a handler.
4
+ export const DELTA_COMMANDS = [
5
+ 'MergeIntoCommand', 'UpdateCommand', 'DeleteCommand', 'WriteIntoDelta', 'WriteIntoDeltaCommand',
6
+ 'SaveIntoDataSourceCommand',
7
+ ] ;
8
+
9
+
10
+
11
+ export function isDeltaCommand(name ) {
12
+ return (DELTA_COMMANDS ).includes(name);
13
+ }
@@ -0,0 +1,42 @@
1
+ // The `<table path>/_delta_log` paths that plans name: the only trace of which Delta table a command
2
+ // or API merge touched when its plan carries no target. A scan may read a source too, so callers
3
+ // take a path only when exactly one is named.
4
+ import { walkPlanTree } from './plan-tree-walk.js';
5
+ import { isCut, } from './write-target-args.js';
6
+
7
+
8
+ // A URI or absolute path before `/_delta_log`, as printed in `... State #0 - <path>/_delta_log[cols]`
9
+ // scans and `DeltaLogFileIndex [<path>/_delta_log/<file>, ...]`.
10
+ const DELTA_LOG_PATH = /((?:[A-Za-z][A-Za-z0-9+.-]*:\/{1,3}|\/)[^\s,[\]()#]+?)\/_delta_log(?=[/[\],\s)]|$)/g;
11
+
12
+
13
+
14
+ // Every distinct table path named in the plans; `unreliable` when a plan is missing or a path cut.
15
+ function deltaLogPaths(executions ) {
16
+ const paths = new Set ();
17
+ let unreliable = false;
18
+ for (const exec of executions) {
19
+ if (!exec.planTree) { unreliable = true; continue; }
20
+ walkPlanTree(exec.planTree, (node) => {
21
+ for (const text of [node.name, node.detail ?? '']) {
22
+ const mentions = text.split('/_delta_log').length - 1;
23
+ if (mentions === 0) continue;
24
+ const matches = [...text.matchAll(DELTA_LOG_PATH)];
25
+ // A `/_delta_log` that is not read as a path is a table this set cannot name.
26
+ if (matches.length < mentions) unreliable = true;
27
+ for (const m of matches) {
28
+ if (isCut(m[1])) unreliable = true;
29
+ else paths.add(m[1].replace(/\/+$/, ''));
30
+ }
31
+ }
32
+ }, { dedupe: true });
33
+ }
34
+ return { paths, unreliable };
35
+ }
36
+
37
+ /** The one table path the plans of `executions` name, or null for none, several or an unreadable plan. */
38
+ export function soleLogPath(executions ) {
39
+ const { paths, unreliable } = deltaLogPaths(executions);
40
+ const [path] = paths;
41
+ return !unreliable && paths.size === 1 ? { kind: 'path', target: path } : null;
42
+ }
@@ -0,0 +1,127 @@
1
+ // Delta write targets that the plan node's simpleString does not carry. A Delta command node is a
2
+ // bare name in sparkPlanInfo on real logs; its target is read from the `Arguments:` line the parser
3
+ // keeps (SqlExecution.commandArguments, one reader per command in COMMANDS), and for a command that
4
+ // always scans its target (MERGE, UPDATE, DELETE) from the single `_delta_log` path of the executions
5
+ // sharing its root. A write command may read another Delta table as its source, so it never takes
6
+ // that step. Otherwise the target is null: a rule that cannot name exactly one target names none.
7
+ import { isDeltaCommand, } from './delta-commands.js';
8
+ import { soleLogPath } from './delta-log-paths.js';
9
+ import { isCut, splitArgs, tableName, } from './write-target-args.js';
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+ /** Executions grouped by rootExecutionId, each group excluding the root itself. */
18
+ export function indexByRoot(sql ) {
19
+ const byRoot = new Map ();
20
+ for (const exec of sql.values()) {
21
+ if (exec.rootExecutionId === undefined || exec.rootExecutionId === exec.id) continue;
22
+ const list = byRoot.get(exec.rootExecutionId);
23
+ if (list) list.push(exec);
24
+ else byRoot.set(exec.rootExecutionId, [exec]);
25
+ }
26
+ return byRoot;
27
+ }
28
+
29
+ // The text after `<Command>\nArguments: `, or null when the execution kept none for this command.
30
+ function argumentsOf(exec , command ) {
31
+ const kept = exec.commandArguments;
32
+ const head = `${command}\nArguments: `;
33
+ return kept !== undefined && kept.startsWith(head) ? kept.slice(head.length) : null;
34
+ }
35
+
36
+ // A catalog table is printed as its quoted identifier, `catalog`.`db`.`t`, as one top-level
37
+ // argument. Two or more such arguments (or none) leave the table unnamed. The session catalog is
38
+ // implied by a Hive or V1 table name, so it is dropped: `spark_catalog`.`db`.`t` is db.t.
39
+ const QUOTED_TABLE = /^`[^`]+`(?:\.`[^`]+`){1,2}$/;
40
+
41
+ function tableFromArguments(args ) {
42
+ const candidates = splitArgs(args).filter((arg) => QUOTED_TABLE.test(arg.text));
43
+ if (candidates.length !== 1 || !candidates[0].terminated) return null;
44
+ const name = tableName(candidates[0].text);
45
+ if (!name || isCut(name)) return null;
46
+ const parts = name.split('.');
47
+ return { kind: 'table', target: parts.length === 3 && parts[0] === 'spark_catalog' ? parts.slice(1).join('.') : name };
48
+ }
49
+
50
+ // SaveIntoDataSourceCommand prints `<provider>@<hash>, [k=v, k2=v2], <mode>`. The target is the
51
+ // one `path` option of a Delta provider.
52
+ function pathFromSaveOptions(args ) {
53
+ const options = splitArgs(args)[1];
54
+ if (!options?.terminated || !/^\[[\s\S]*\]$/.test(options.text)) return null;
55
+ const body = options.text.slice(1, -1);
56
+ const keys = [...body.matchAll(/(?:^|, )([A-Za-z0-9_.-]+)=/g)];
57
+ const paths = keys.flatMap((key, i) => {
58
+ if (key[1].toLowerCase() !== 'path') return [];
59
+ return [body.slice(key.index + key[0].length, i + 1 < keys.length ? keys[i + 1].index : body.length)];
60
+ });
61
+ if (paths.length !== 1 || isCut(paths[0]) || /\*{5,}\(redacted\)/.test(paths[0])) return null;
62
+ return { kind: 'path', target: paths[0] };
63
+ }
64
+
65
+
66
+
67
+
68
+
69
+
70
+
71
+
72
+ const tableCommand = { fromArguments: tableFromArguments, scansTarget: false };
73
+ const scanningTableCommand = { ...tableCommand, scansTarget: true };
74
+
75
+ // One entry per Delta command: a new command is added here (and to DELTA_COMMANDS), not in the resolver.
76
+ const COMMANDS = {
77
+ MergeIntoCommand: scanningTableCommand,
78
+ UpdateCommand: scanningTableCommand,
79
+ DeleteCommand: scanningTableCommand,
80
+ WriteIntoDelta: tableCommand,
81
+ WriteIntoDeltaCommand: tableCommand,
82
+ // Only the arguments say a save is a Delta one; a JDBC save with a Delta table in its source
83
+ // would otherwise take that table as its target.
84
+ SaveIntoDataSourceCommand: {
85
+ fromArguments: (args) => (args.includes('DeltaDataSource') ? pathFromSaveOptions(args) : null),
86
+ scansTarget: false,
87
+ },
88
+ };
89
+
90
+ /** Target of a Delta command whose simpleString names none; `node` must be the root of `exec`'s plan. */
91
+ export function resolveDeltaCommandTarget(
92
+ command , exec , node , byRoot ,
93
+ ) {
94
+ if (!isDeltaCommand(command) || node !== exec.planTree) return null;
95
+ const handler = COMMANDS[command];
96
+ const args = argumentsOf(exec, command);
97
+ const found = args === null ? null : handler.fromArguments(args);
98
+ if (found || !handler.scansTarget) return found;
99
+ // Child executions are the root's own only when it is the root: a command run under another
100
+ // execution shares that execution's children, which may belong to unrelated work.
101
+ return exec.rootExecutionId === exec.id ? soleLogPath(byRoot.get(exec.id) ?? []) : null;
102
+ }
103
+
104
+ // An append or overwrite of an existing Delta table (saveAsTable, INSERT INTO or OVERWRITE) is an
105
+ // `AppendDataExecV1` or `OverwriteByExpressionExecV1` whose first argument is the table object:
106
+ // `DeltaTableV2(<session>,<path>,Some(CatalogTable(\nCatalog: c\nDatabase: d\nTable: t\n...)),...)`.
107
+ // Its CatalogTable block names the table; a path-based table has `None` there and only the path.
108
+ const DELTA_TABLE_V2 = /^\w+ DeltaTableV2\([^,\s]*,([^,\n]*),(?:Some\(CatalogTable\(\n(?:Catalog: (\S+)\n)?Database: (\S+)\nTable: (\S+)\n|None,)/;
109
+ const SIMPLE_NAME = /^[A-Za-z0-9_$]+$/;
110
+
111
+ /** Target of a V1-fallback write on a DeltaTableV2 table, or null when the detail has another shape. */
112
+ export function parseDeltaTableV2(detail ) {
113
+ const m = DELTA_TABLE_V2.exec(detail);
114
+ if (!m) return null;
115
+ const [, path, catalog, database, table] = m;
116
+ if (table === undefined) return path && !isCut(path) ? { kind: 'path', target: path } : null;
117
+ if (![database, table, catalog ?? 'spark_catalog'].every((part) => SIMPLE_NAME.test(part))) return null;
118
+ // The session catalog is implied by a V1 name, so it is dropped (as for a Delta command).
119
+ const name = `${database}.${table}`;
120
+ return { kind: 'table', target: catalog === undefined || catalog === 'spark_catalog' ? name : `${catalog}.${name}` };
121
+ }
122
+
123
+ /** The append a Delta CTAS or RTAS runs under its own root: its staged table has no name. */
124
+ export function isStagedDeltaWrite(write ) {
125
+ return (write.command === 'AppendDataExecV1' || write.command === 'OverwriteByExpressionExecV1')
126
+ && /^\w+ \S*DeltaCatalog\$StagedDeltaTableV2@/.test(write.raw);
127
+ }