@herbertgao/pi-extensions 2026.10.0 → 2026.10.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 (769) hide show
  1. package/README.md +16 -12
  2. package/THIRD_PARTY_NOTICES.md +3 -3
  3. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-audit.md +2 -2
  4. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-debt.md +2 -2
  5. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-gain.md +1 -1
  6. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-help.md +1 -1
  7. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-review.md +2 -2
  8. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail.md +1 -1
  9. package/node_modules/@dietrichgebert/ponytail/.opencode/plugins/index.js +4 -0
  10. package/node_modules/@dietrichgebert/ponytail/.opencode/plugins/ponytail-frontmatter.cjs +43 -3
  11. package/node_modules/@dietrichgebert/ponytail/.opencode/plugins/ponytail.mjs +142 -59
  12. package/node_modules/@dietrichgebert/ponytail/.qoder/rules/ponytail.md +21 -21
  13. package/node_modules/@dietrichgebert/ponytail/.qoder-plugin/plugin.json +1 -1
  14. package/node_modules/@dietrichgebert/ponytail/AGENTS.md +21 -21
  15. package/node_modules/@dietrichgebert/ponytail/README.md +52 -242
  16. package/node_modules/@dietrichgebert/ponytail/hooks/claude-codex-hooks.json +3 -3
  17. package/node_modules/@dietrichgebert/ponytail/hooks/copilot-hooks.json +4 -4
  18. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-activate.js +70 -16
  19. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-config.js +8 -7
  20. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-instructions.js +30 -30
  21. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-map.js +103 -0
  22. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-mode-tracker.js +46 -23
  23. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-runtime.js +39 -7
  24. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-subagent.js +20 -3
  25. package/node_modules/@dietrichgebert/ponytail/hooks/qoder-hooks.json +3 -3
  26. package/node_modules/@dietrichgebert/ponytail/package.json +3 -3
  27. package/node_modules/@dietrichgebert/ponytail/pi-extension/index.js +47 -17
  28. package/node_modules/@dietrichgebert/ponytail/scripts/uninstall.js +21 -7
  29. package/node_modules/@dietrichgebert/ponytail/skills/plugin.json +5 -0
  30. package/node_modules/@dietrichgebert/ponytail/skills/ponytail/SKILL.md +33 -101
  31. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-audit/SKILL.md +89 -27
  32. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-debt/SKILL.md +13 -12
  33. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-gain/SKILL.md +21 -18
  34. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-help/SKILL.md +12 -13
  35. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-review/SKILL.md +96 -52
  36. package/node_modules/@herbertgao/pi-agency/LICENSE +21 -0
  37. package/node_modules/@herbertgao/pi-agency/README.md +40 -0
  38. package/node_modules/@herbertgao/pi-agency/package.json +53 -0
  39. package/node_modules/@herbertgao/pi-agency/src/index.ts +441 -0
  40. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +12 -12
  41. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +30 -6
  42. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +15 -9
  43. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +27 -9
  44. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/codemode.ts +4 -1
  45. package/node_modules/@herbertgao/pi-cc-extensions/package.json +2 -2
  46. package/node_modules/@narumitw/pi-caffeinate/package.json +4 -4
  47. package/node_modules/@tifan/pi-mermaid-open/package.json +5 -1
  48. package/node_modules/@tifan/pi-mermaid-open/src/index.ts +26 -9
  49. package/node_modules/@tifan/pi-recap/package.json +1 -1
  50. package/node_modules/@tifan/pi-recap/src/index.ts +104 -20
  51. package/node_modules/@tifan/pi-recap/src/models.ts +46 -4
  52. package/node_modules/pi-antigravity/README.md +23 -2
  53. package/node_modules/pi-antigravity/package.json +5 -3
  54. package/node_modules/pi-antigravity/src/diagnostics/diagnostics.ts +3 -2
  55. package/node_modules/pi-antigravity/src/features/generate-image.ts +72 -0
  56. package/node_modules/pi-antigravity/src/features/google-search.ts +71 -0
  57. package/node_modules/pi-antigravity/src/image/image.ts +86 -47
  58. package/node_modules/pi-antigravity/src/image/native.ts +69 -0
  59. package/node_modules/pi-antigravity/src/index.ts +52 -137
  60. package/node_modules/pi-antigravity/src/search/search.ts +130 -31
  61. package/node_modules/pi-antigravity/src/stream/stream.ts +162 -113
  62. package/node_modules/pi-antigravity/src/types/enums.ts +1 -0
  63. package/node_modules/pi-antigravity/src/types/types.ts +1 -1
  64. package/node_modules/pi-antigravity/src/utils/security.ts +47 -1
  65. package/node_modules/pi-antigravity/src/utils/util.ts +38 -6
  66. package/node_modules/pi-lens/CHANGELOG.md +1064 -0
  67. package/node_modules/pi-lens/README.md +115 -25
  68. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +6 -2
  69. package/node_modules/pi-lens/dist/chunk-22AVIGXS.js +560 -0
  70. package/node_modules/pi-lens/dist/chunk-27BBHSZC.js +106 -0
  71. package/node_modules/pi-lens/dist/chunk-2BEMIWEW.js +51 -0
  72. package/node_modules/pi-lens/dist/chunk-2EECBNC5.js +89 -0
  73. package/node_modules/pi-lens/dist/chunk-34U7PLGE.js +23 -0
  74. package/node_modules/pi-lens/dist/chunk-3I5QFT3N.js +181 -0
  75. package/node_modules/pi-lens/dist/chunk-3MED75HT.js +37 -0
  76. package/node_modules/pi-lens/dist/chunk-47D2BO4S.js +87 -0
  77. package/node_modules/pi-lens/dist/chunk-4RERDEXR.js +348 -0
  78. package/node_modules/pi-lens/dist/chunk-4W2PLC4J.js +27 -0
  79. package/node_modules/pi-lens/dist/chunk-52H5B5TC.js +202 -0
  80. package/node_modules/pi-lens/dist/chunk-54B42YRX.js +27 -0
  81. package/node_modules/pi-lens/dist/chunk-5RXBNSW2.js +266 -0
  82. package/node_modules/pi-lens/dist/chunk-5THXD6X5.js +201 -0
  83. package/node_modules/pi-lens/dist/chunk-5W3R6LFT.js +24 -0
  84. package/node_modules/pi-lens/dist/chunk-5ZEJHV35.js +341 -0
  85. package/node_modules/pi-lens/dist/chunk-6P62QVEA.js +118 -0
  86. package/node_modules/pi-lens/dist/chunk-6QIUX4EP.js +459 -0
  87. package/node_modules/pi-lens/dist/chunk-6VCD4VKZ.js +78 -0
  88. package/node_modules/pi-lens/dist/chunk-7F72PXY2.js +326 -0
  89. package/node_modules/pi-lens/dist/chunk-7FBF7NPR.js +998 -0
  90. package/node_modules/pi-lens/dist/chunk-7U2IDNXN.js +37 -0
  91. package/node_modules/pi-lens/dist/chunk-ABBOA7UD.js +157 -0
  92. package/node_modules/pi-lens/dist/chunk-AHDW3GPE.js +50 -0
  93. package/node_modules/pi-lens/dist/chunk-AJ3QXTRQ.js +756 -0
  94. package/node_modules/pi-lens/dist/chunk-AJVLWNFN.js +1610 -0
  95. package/node_modules/pi-lens/dist/chunk-AJXODZY6.js +112 -0
  96. package/node_modules/pi-lens/dist/chunk-ARFCXG7S.js +84 -0
  97. package/node_modules/pi-lens/dist/chunk-AS44CFQ4.js +708 -0
  98. package/node_modules/pi-lens/dist/chunk-AWE5TMPO.js +38 -0
  99. package/node_modules/pi-lens/dist/chunk-B3I2UUT4.js +4692 -0
  100. package/node_modules/pi-lens/dist/chunk-BRJKOBZP.js +331 -0
  101. package/node_modules/pi-lens/dist/chunk-BW5KFIQC.js +59 -0
  102. package/node_modules/pi-lens/dist/chunk-C2AUYXQH.js +149 -0
  103. package/node_modules/pi-lens/dist/chunk-CGGAIP7J.js +29 -0
  104. package/node_modules/pi-lens/dist/chunk-D6QS3U6M.js +30 -0
  105. package/node_modules/pi-lens/dist/chunk-DFVMIKMQ.js +349 -0
  106. package/node_modules/pi-lens/dist/chunk-DIULIWKU.js +169 -0
  107. package/node_modules/pi-lens/dist/chunk-DQ2NZ7C4.js +2317 -0
  108. package/node_modules/pi-lens/dist/chunk-E2WFBRHM.js +213 -0
  109. package/node_modules/pi-lens/dist/chunk-E4MAEDPM.js +50 -0
  110. package/node_modules/pi-lens/dist/chunk-EOKRQ3SA.js +30 -0
  111. package/node_modules/pi-lens/dist/chunk-F3AU2TMM.js +2626 -0
  112. package/node_modules/pi-lens/dist/chunk-F3P7VQ7H.js +170 -0
  113. package/node_modules/pi-lens/dist/chunk-FC4RP4I2.js +12 -0
  114. package/node_modules/pi-lens/dist/chunk-FJNKANOS.js +32 -0
  115. package/node_modules/pi-lens/dist/chunk-GUOGVTCQ.js +146 -0
  116. package/node_modules/pi-lens/dist/chunk-GVXA6CCE.js +18 -0
  117. package/node_modules/pi-lens/dist/chunk-HHYAELLJ.js +2334 -0
  118. package/node_modules/pi-lens/dist/chunk-HMFXILPV.js +83 -0
  119. package/node_modules/pi-lens/dist/chunk-HVUZXSWK.js +421 -0
  120. package/node_modules/pi-lens/dist/chunk-HWBZIGMJ.js +62 -0
  121. package/node_modules/pi-lens/dist/chunk-I3R7GKDR.js +17 -0
  122. package/node_modules/pi-lens/dist/chunk-I7SPGOWI.js +55 -0
  123. package/node_modules/pi-lens/dist/chunk-IIXOAGHR.js +38 -0
  124. package/node_modules/pi-lens/dist/chunk-IVP5KAB7.js +2250 -0
  125. package/node_modules/pi-lens/dist/chunk-IXEBJSUE.js +22 -0
  126. package/node_modules/pi-lens/dist/chunk-JOLITGCC.js +27 -0
  127. package/node_modules/pi-lens/dist/chunk-K6VYLB2U.js +145 -0
  128. package/node_modules/pi-lens/dist/chunk-LJDODBBZ.js +78 -0
  129. package/node_modules/pi-lens/dist/chunk-LNAKEO63.js +78 -0
  130. package/node_modules/pi-lens/dist/chunk-LSMJ6KBE.js +4139 -0
  131. package/node_modules/pi-lens/dist/chunk-LW6XKDZQ.js +5565 -0
  132. package/node_modules/pi-lens/dist/chunk-N3YQJI6O.js +899 -0
  133. package/node_modules/pi-lens/dist/chunk-NABLC7W3.js +148 -0
  134. package/node_modules/pi-lens/dist/chunk-NKOUFPQF.js +659 -0
  135. package/node_modules/pi-lens/dist/chunk-NL5GLMFI.js +211 -0
  136. package/node_modules/pi-lens/dist/chunk-NMABWBZN.js +327 -0
  137. package/node_modules/pi-lens/dist/chunk-NTHH2J74.js +27 -0
  138. package/node_modules/pi-lens/dist/chunk-NTUJEGDZ.js +59 -0
  139. package/node_modules/pi-lens/dist/chunk-NXL4FFQQ.js +47 -0
  140. package/node_modules/pi-lens/dist/chunk-NXPS2L6U.js +466 -0
  141. package/node_modules/pi-lens/dist/chunk-O5WILGMI.js +42 -0
  142. package/node_modules/pi-lens/dist/chunk-O6TQT6RI.js +48 -0
  143. package/node_modules/pi-lens/dist/chunk-OAXNDZJ7.js +323 -0
  144. package/node_modules/pi-lens/dist/chunk-ODEEOXPH.js +44 -0
  145. package/node_modules/pi-lens/dist/chunk-OQGFCZJ4.js +4533 -0
  146. package/node_modules/pi-lens/dist/chunk-ORTMY4Q4.js +282 -0
  147. package/node_modules/pi-lens/dist/chunk-OS32RNR2.js +442 -0
  148. package/node_modules/pi-lens/dist/chunk-PAK6IDME.js +189 -0
  149. package/node_modules/pi-lens/dist/chunk-PWFHBTSF.js +406 -0
  150. package/node_modules/pi-lens/dist/chunk-Q5U6FMDI.js +2352 -0
  151. package/node_modules/pi-lens/dist/chunk-QOZXJ4NA.js +3364 -0
  152. package/node_modules/pi-lens/dist/chunk-QYF5S6P6.js +45 -0
  153. package/node_modules/pi-lens/dist/chunk-RIISMHKN.js +36 -0
  154. package/node_modules/pi-lens/dist/chunk-RPD4ZSB7.js +25 -0
  155. package/node_modules/pi-lens/dist/chunk-RPD5K5NC.js +79 -0
  156. package/node_modules/pi-lens/dist/chunk-RUOG5D2C.js +262 -0
  157. package/node_modules/pi-lens/dist/chunk-T4ERT73Y.js +599 -0
  158. package/node_modules/pi-lens/dist/chunk-T6D4Q4AQ.js +54 -0
  159. package/node_modules/pi-lens/dist/chunk-TGPXLY2J.js +391 -0
  160. package/node_modules/pi-lens/dist/chunk-TLGFBM3F.js +12116 -0
  161. package/node_modules/pi-lens/dist/chunk-TO6WL3GM.js +88 -0
  162. package/node_modules/pi-lens/dist/chunk-TT22N74J.js +517 -0
  163. package/node_modules/pi-lens/dist/chunk-TVN5N6GY.js +21216 -0
  164. package/node_modules/pi-lens/dist/chunk-U3VGR4VU.js +308 -0
  165. package/node_modules/pi-lens/dist/chunk-UK3CMEAL.js +1250 -0
  166. package/node_modules/pi-lens/dist/chunk-UOPVJQ4N.js +103 -0
  167. package/node_modules/pi-lens/dist/chunk-UXVNL3D7.js +23 -0
  168. package/node_modules/pi-lens/dist/chunk-UY752QSN.js +45 -0
  169. package/node_modules/pi-lens/dist/chunk-V5PXFSZK.js +188 -0
  170. package/node_modules/pi-lens/dist/chunk-V7R425XG.js +398 -0
  171. package/node_modules/pi-lens/dist/chunk-VCSRSYLF.js +81 -0
  172. package/node_modules/pi-lens/dist/chunk-VD4JQCQC.js +162 -0
  173. package/node_modules/pi-lens/dist/chunk-VDBPVIKD.js +62 -0
  174. package/node_modules/pi-lens/dist/chunk-VHQLPIRJ.js +114 -0
  175. package/node_modules/pi-lens/dist/chunk-VN2AXLEX.js +5414 -0
  176. package/node_modules/pi-lens/dist/chunk-VNMT7C64.js +478 -0
  177. package/node_modules/pi-lens/dist/chunk-WONERJTP.js +309 -0
  178. package/node_modules/pi-lens/dist/chunk-XECLWQZN.js +85 -0
  179. package/node_modules/pi-lens/dist/chunk-XIMYZ3JA.js +27 -0
  180. package/node_modules/pi-lens/dist/chunk-XK4CIILI.js +144 -0
  181. package/node_modules/pi-lens/dist/chunk-Y4T27COV.js +562 -0
  182. package/node_modules/pi-lens/dist/chunk-YGVYKNSN.js +1860 -0
  183. package/node_modules/pi-lens/dist/chunk-YLF3PPT5.js +308 -0
  184. package/node_modules/pi-lens/dist/chunk-YSNFMJKN.js +14 -0
  185. package/node_modules/pi-lens/dist/chunk-Z3CCVDUB.js +76 -0
  186. package/node_modules/pi-lens/dist/chunk-Z54ZSOBZ.js +61 -0
  187. package/node_modules/pi-lens/dist/chunk-ZAWW3AF6.js +97 -0
  188. package/node_modules/pi-lens/dist/chunk-ZMOL6RQC.js +167 -0
  189. package/node_modules/pi-lens/dist/index.js +16456 -8335
  190. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +186 -232
  191. package/node_modules/pi-lens/dist/mcp/cli.js +137 -98
  192. package/node_modules/pi-lens/dist/mcp/server.js +22589 -1573
  193. package/node_modules/pi-lens/dist/mcp/worker.js +97 -38
  194. package/node_modules/pi-lens/dist/probes/installer.js +135 -0
  195. package/node_modules/pi-lens/dist/workers/project-snapshot-persist-worker.js +22 -0
  196. package/node_modules/pi-lens/dist/workers/review-graph-persist-worker.js +15 -0
  197. package/node_modules/pi-lens/docs/agent-guide.md +13 -4
  198. package/node_modules/pi-lens/docs/agent-tools.md +42 -0
  199. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +3 -2
  200. package/node_modules/pi-lens/docs/configuration.md +20 -1
  201. package/node_modules/pi-lens/docs/environment-variables.md +52 -2
  202. package/node_modules/pi-lens/docs/features.md +8 -4
  203. package/node_modules/pi-lens/docs/globalconfig.md +1 -0
  204. package/node_modules/pi-lens/docs/io-bridge-v2-migration.md +94 -0
  205. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +36 -4
  206. package/node_modules/pi-lens/docs/lsp-idle-eviction.md +124 -0
  207. package/node_modules/pi-lens/docs/mcp.md +21 -5
  208. package/node_modules/pi-lens/docs/merge-queue-rollout.md +77 -0
  209. package/node_modules/pi-lens/docs/pi-lens-fixer.md +156 -88
  210. package/node_modules/pi-lens/docs/pi-lens-investigator.md +74 -26
  211. package/node_modules/pi-lens/docs/pi-lens-merge-policy.md +694 -0
  212. package/node_modules/pi-lens/docs/pi-lens-monitor.md +73 -7
  213. package/node_modules/pi-lens/docs/pi-lens-retro.md +6 -4
  214. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +188 -48
  215. package/node_modules/pi-lens/docs/pi-lens-subagent.md +246 -45
  216. package/node_modules/pi-lens/docs/pi-lens-warden.md +18 -7
  217. package/node_modules/pi-lens/docs/public-api-stability.md +55 -0
  218. package/node_modules/pi-lens/docs/release-qa-baseline.md +84 -17
  219. package/node_modules/pi-lens/docs/rfcs/3654-unified-io-bridge-v2.md +316 -0
  220. package/node_modules/pi-lens/docs/servercapabilities.md +53 -49
  221. package/node_modules/pi-lens/docs/settings.md +1 -0
  222. package/node_modules/pi-lens/docs/usage.md +5 -1
  223. package/node_modules/pi-lens/grammars/tree-sitter-bash.wasm +0 -0
  224. package/node_modules/pi-lens/grammars/tree-sitter-bash.wasm.json +3 -3
  225. package/node_modules/pi-lens/package.json +18 -6
  226. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/advisory-registry-subset-test.yml +41 -0
  227. package/node_modules/pi-lens/rules/ast-grep-rules/rules/advisory-registry-subset.yml +50 -0
  228. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +1275 -62
  229. package/node_modules/pi-lens/scripts/download-grammars.js +5 -0
  230. package/node_modules/pi-lens/scripts/grammars.lock.json +6 -1
  231. package/node_modules/pi-lens/scripts/install-selftest.mjs +33 -34
  232. package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +13 -4
  233. package/node_modules/pi-lens/scripts/lib/web-tree-sitter-dir.mjs +18 -0
  234. package/node_modules/pi-typesafe/README.md +1 -1
  235. package/node_modules/pi-typesafe/dist/backends.d.ts +15 -5
  236. package/node_modules/pi-typesafe/dist/backends.js +18 -0
  237. package/node_modules/pi-typesafe/dist/client.d.ts +4 -3
  238. package/node_modules/pi-typesafe/dist/client.js +22 -7
  239. package/node_modules/pi-typesafe/dist/index.d.ts +2 -2
  240. package/node_modules/pi-typesafe/dist/index.js +1 -1
  241. package/node_modules/pi-typesafe/dist/usage.d.ts +14 -4
  242. package/node_modules/pi-typesafe/dist/usage.js +147 -42
  243. package/node_modules/pi-typesafe/package.json +1 -1
  244. package/node_modules/pi-web-access/CHANGELOG.md +48 -0
  245. package/node_modules/pi-web-access/README.md +57 -11
  246. package/node_modules/pi-web-access/brave-rate-limit.ts +8 -2
  247. package/node_modules/pi-web-access/curator-page.ts +5 -1
  248. package/node_modules/pi-web-access/curator-server.ts +2 -0
  249. package/node_modules/pi-web-access/declared-web-links.ts +2 -2
  250. package/node_modules/pi-web-access/degoog.ts +323 -0
  251. package/node_modules/pi-web-access/dist/index.js +3483 -2745
  252. package/node_modules/pi-web-access/dist/mcp-cli.js +27443 -0
  253. package/node_modules/pi-web-access/exa.ts +5 -1
  254. package/node_modules/pi-web-access/extract.ts +41 -3
  255. package/node_modules/pi-web-access/firecrawl.ts +35 -4
  256. package/node_modules/pi-web-access/gemini-search.ts +34 -11
  257. package/node_modules/pi-web-access/gemini-web.ts +14 -9
  258. package/node_modules/pi-web-access/index.ts +153 -1202
  259. package/node_modules/pi-web-access/keenable.ts +221 -0
  260. package/node_modules/pi-web-access/mcp-cli.ts +40 -0
  261. package/node_modules/pi-web-access/mcp-server.ts +81 -0
  262. package/node_modules/pi-web-access/mcp-tool-definitions.ts +117 -0
  263. package/node_modules/pi-web-access/openai-search.ts +34 -1
  264. package/node_modules/pi-web-access/package.json +23 -5
  265. package/node_modules/pi-web-access/perplexity.ts +64 -56
  266. package/node_modules/pi-web-access/tool-arguments.ts +20 -0
  267. package/node_modules/pi-web-access/video-extract.ts +1 -1
  268. package/node_modules/pi-web-access/web-tool-contract.ts +86 -0
  269. package/node_modules/pi-web-access/web-tool-core.ts +1502 -0
  270. package/node_modules/pi-web-access/youtube-extract.ts +2 -5
  271. package/package.json +17 -12
  272. package/node_modules/@dietrichgebert/ponytail/README.es.md +0 -329
  273. package/node_modules/@dietrichgebert/ponytail/README.ko.md +0 -346
  274. package/node_modules/@dietrichgebert/ponytail/assets/benchmark-3model.svg +0 -21
  275. package/node_modules/@dietrichgebert/ponytail/assets/benchmark-agentic.svg +0 -62
  276. package/node_modules/@dietrichgebert/ponytail/assets/logo-dark.png +0 -0
  277. package/node_modules/@dietrichgebert/ponytail/assets/logo-dark.svg +0 -115
  278. package/node_modules/@dietrichgebert/ponytail/assets/logo-greenpt-dark.svg +0 -27
  279. package/node_modules/@dietrichgebert/ponytail/assets/logo-greenpt.svg +0 -27
  280. package/node_modules/@dietrichgebert/ponytail/assets/logo.png +0 -0
  281. package/node_modules/@dietrichgebert/ponytail/assets/retriever-icon.png +0 -0
  282. package/node_modules/@dietrichgebert/ponytail/assets/retriever-logo-dark.svg +0 -4
  283. package/node_modules/@dietrichgebert/ponytail/assets/retriever-logo-light.svg +0 -4
  284. package/node_modules/@dietrichgebert/ponytail/assets/social-preview.png +0 -0
  285. package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner-es.png +0 -0
  286. package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner-ko.png +0 -0
  287. package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner.png +0 -0
  288. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +0 -35
  289. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +0 -1610
  290. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +0 -593
  291. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +0 -147
  292. package/node_modules/pi-lens/dist/clients/agent-nudge.js +0 -325
  293. package/node_modules/pi-lens/dist/clients/analysed-root.js +0 -1
  294. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +0 -731
  295. package/node_modules/pi-lens/dist/clients/ast-grep-rule-manager.js +0 -146
  296. package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +0 -118
  297. package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -10
  298. package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +0 -154
  299. package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +0 -75
  300. package/node_modules/pi-lens/dist/clients/atomic-write.js +0 -189
  301. package/node_modules/pi-lens/dist/clients/availability-probe-flight.js +0 -12
  302. package/node_modules/pi-lens/dist/clients/bash-file-access.js +0 -1056
  303. package/node_modules/pi-lens/dist/clients/biome-client.js +0 -357
  304. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +0 -837
  305. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +0 -129
  306. package/node_modules/pi-lens/dist/clients/bootstrap.js +0 -569
  307. package/node_modules/pi-lens/dist/clients/bounded-cache.js +0 -190
  308. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +0 -202
  309. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +0 -295
  310. package/node_modules/pi-lens/dist/clients/build-identity.js +0 -95
  311. package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +0 -113
  312. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +0 -105
  313. package/node_modules/pi-lens/dist/clients/bus-publish.js +0 -171
  314. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +0 -190
  315. package/node_modules/pi-lens/dist/clients/cache-manager.js +0 -524
  316. package/node_modules/pi-lens/dist/clients/cache-observability.js +0 -1313
  317. package/node_modules/pi-lens/dist/clients/call-graph.js +0 -852
  318. package/node_modules/pi-lens/dist/clients/cargo-manifest.js +0 -422
  319. package/node_modules/pi-lens/dist/clients/cascade-budget.js +0 -196
  320. package/node_modules/pi-lens/dist/clients/cascade-format.js +0 -315
  321. package/node_modules/pi-lens/dist/clients/cascade-logger.js +0 -31
  322. package/node_modules/pi-lens/dist/clients/cascade-types.js +0 -9
  323. package/node_modules/pi-lens/dist/clients/child-unref.js +0 -124
  324. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +0 -286
  325. package/node_modules/pi-lens/dist/clients/codebase-model.js +0 -173
  326. package/node_modules/pi-lens/dist/clients/collateral-test-role.js +0 -40
  327. package/node_modules/pi-lens/dist/clients/complexity-client.js +0 -462
  328. package/node_modules/pi-lens/dist/clients/config-core/deny.js +0 -221
  329. package/node_modules/pi-lens/dist/clients/config-core/index.js +0 -50
  330. package/node_modules/pi-lens/dist/clients/config-core/merge.js +0 -357
  331. package/node_modules/pi-lens/dist/clients/config-core/normalize.js +0 -340
  332. package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +0 -248
  333. package/node_modules/pi-lens/dist/clients/config-core/provenance.js +0 -164
  334. package/node_modules/pi-lens/dist/clients/config-core/records.js +0 -218
  335. package/node_modules/pi-lens/dist/clients/config-core/resolve.js +0 -125
  336. package/node_modules/pi-lens/dist/clients/config-core/safe-object.js +0 -78
  337. package/node_modules/pi-lens/dist/clients/config-core/schema.js +0 -165
  338. package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +0 -285
  339. package/node_modules/pi-lens/dist/clients/config-locations.js +0 -388
  340. package/node_modules/pi-lens/dist/clients/config-resolve.js +0 -798
  341. package/node_modules/pi-lens/dist/clients/config-schema.js +0 -251
  342. package/node_modules/pi-lens/dist/clients/config-warn.js +0 -407
  343. package/node_modules/pi-lens/dist/clients/console-guard-install.js +0 -26
  344. package/node_modules/pi-lens/dist/clients/cooperative-budget.js +0 -42
  345. package/node_modules/pi-lens/dist/clients/dead-code-client.js +0 -510
  346. package/node_modules/pi-lens/dist/clients/dead-code-logger.js +0 -30
  347. package/node_modules/pi-lens/dist/clients/deadline-utils.js +0 -280
  348. package/node_modules/pi-lens/dist/clients/debug-handles.js +0 -211
  349. package/node_modules/pi-lens/dist/clients/debug-heap.js +0 -174
  350. package/node_modules/pi-lens/dist/clients/deferred-lsp-work.js +0 -106
  351. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +0 -426
  352. package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +0 -169
  353. package/node_modules/pi-lens/dist/clients/dependency-checker.js +0 -885
  354. package/node_modules/pi-lens/dist/clients/deps/ast-grep-napi.js +0 -22
  355. package/node_modules/pi-lens/dist/clients/deps/js-yaml.js +0 -9
  356. package/node_modules/pi-lens/dist/clients/deps/minimatch.js +0 -4
  357. package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +0 -14
  358. package/node_modules/pi-lens/dist/clients/deps/typebox.js +0 -14
  359. package/node_modules/pi-lens/dist/clients/deps/vscode-jsonrpc.js +0 -1
  360. package/node_modules/pi-lens/dist/clients/deps/web-tree-sitter.js +0 -24
  361. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +0 -630
  362. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +0 -305
  363. package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +0 -73
  364. package/node_modules/pi-lens/dist/clients/diagnostic-tracker.js +0 -105
  365. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +0 -230
  366. package/node_modules/pi-lens/dist/clients/dispatch/ast-grep-catalog.js +0 -65
  367. package/node_modules/pi-lens/dist/clients/dispatch/auxiliary-lsp.js +0 -343
  368. package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +0 -35
  369. package/node_modules/pi-lens/dist/clients/dispatch/diagnostic-taxonomy.js +0 -66
  370. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +0 -1269
  371. package/node_modules/pi-lens/dist/clients/dispatch/fact-provider-types.js +0 -1
  372. package/node_modules/pi-lens/dist/clients/dispatch/fact-rule-runner.js +0 -17
  373. package/node_modules/pi-lens/dist/clients/dispatch/fact-runner.js +0 -30
  374. package/node_modules/pi-lens/dist/clients/dispatch/fact-scheduler.js +0 -68
  375. package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +0 -377
  376. package/node_modules/pi-lens/dist/clients/dispatch/facts/comment-facts.js +0 -22
  377. package/node_modules/pi-lens/dist/clients/dispatch/facts/file-content.js +0 -19
  378. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +0 -348
  379. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +0 -204
  380. package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +0 -34
  381. package/node_modules/pi-lens/dist/clients/dispatch/facts/try-catch-facts.js +0 -119
  382. package/node_modules/pi-lens/dist/clients/dispatch/finding-policy.js +0 -339
  383. package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +0 -417
  384. package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +0 -89
  385. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +0 -2377
  386. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +0 -11
  387. package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +0 -87
  388. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +0 -278
  389. package/node_modules/pi-lens/dist/clients/dispatch/priorities.js +0 -20
  390. package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +0 -50
  391. package/node_modules/pi-lens/dist/clients/dispatch/rule-ignores.js +0 -69
  392. package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +0 -135
  393. package/node_modules/pi-lens/dist/clients/dispatch/rules/async-noise.js +0 -40
  394. package/node_modules/pi-lens/dist/clients/dispatch/rules/async-unnecessary-wrapper.js +0 -27
  395. package/node_modules/pi-lens/dist/clients/dispatch/rules/cors-wildcard.js +0 -54
  396. package/node_modules/pi-lens/dist/clients/dispatch/rules/error-obscuring.js +0 -32
  397. package/node_modules/pi-lens/dist/clients/dispatch/rules/error-swallowing.js +0 -31
  398. package/node_modules/pi-lens/dist/clients/dispatch/rules/framework-call-noise.js +0 -67
  399. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-complexity.js +0 -72
  400. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-fan-out.js +0 -74
  401. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-import-coupling.js +0 -37
  402. package/node_modules/pi-lens/dist/clients/dispatch/rules/missing-error-propagation.js +0 -62
  403. package/node_modules/pi-lens/dist/clients/dispatch/rules/no-commented-credentials.js +0 -52
  404. package/node_modules/pi-lens/dist/clients/dispatch/rules/pass-through-wrappers.js +0 -42
  405. package/node_modules/pi-lens/dist/clients/dispatch/rules/placeholder-comments.js +0 -40
  406. package/node_modules/pi-lens/dist/clients/dispatch/rules/unsafe-boundary.js +0 -126
  407. package/node_modules/pi-lens/dist/clients/dispatch/runner-context.js +0 -52
  408. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +0 -111
  409. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +0 -1196
  410. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +0 -296
  411. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +0 -219
  412. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +0 -82
  413. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +0 -416
  414. package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -183
  415. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +0 -176
  416. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -164
  417. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +0 -195
  418. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +0 -137
  419. package/node_modules/pi-lens/dist/clients/dispatch/runners/fact-rules.js +0 -29
  420. package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +0 -69
  421. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +0 -167
  422. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +0 -104
  423. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -135
  424. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +0 -91
  425. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +0 -171
  426. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +0 -940
  427. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +0 -117
  428. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +0 -108
  429. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +0 -99
  430. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +0 -110
  431. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +0 -379
  432. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +0 -167
  433. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +0 -102
  434. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +0 -414
  435. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +0 -85
  436. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +0 -120
  437. package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -62
  438. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -445
  439. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +0 -169
  440. package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +0 -99
  441. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +0 -88
  442. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +0 -256
  443. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +0 -173
  444. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -102
  445. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +0 -128
  446. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +0 -259
  447. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -183
  448. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +0 -166
  449. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +0 -163
  450. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +0 -174
  451. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +0 -168
  452. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +0 -125
  453. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -643
  454. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +0 -253
  455. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +0 -496
  456. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +0 -90
  457. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -95
  458. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +0 -274
  459. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +0 -1701
  460. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +0 -101
  461. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +0 -235
  462. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +0 -169
  463. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils.js +0 -15
  464. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -128
  465. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +0 -242
  466. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +0 -95
  467. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -79
  468. package/node_modules/pi-lens/dist/clients/dispatch/suppress-writer.js +0 -79
  469. package/node_modules/pi-lens/dist/clients/dispatch/tool-profile.js +0 -30
  470. package/node_modules/pi-lens/dist/clients/dispatch/types.js +0 -21
  471. package/node_modules/pi-lens/dist/clients/dispatch/utils/format-utils.js +0 -45
  472. package/node_modules/pi-lens/dist/clients/dispatch/utils/lsp-diagnostics.js +0 -35
  473. package/node_modules/pi-lens/dist/clients/disposition-logger.js +0 -32
  474. package/node_modules/pi-lens/dist/clients/disposition-publish.js +0 -124
  475. package/node_modules/pi-lens/dist/clients/durable-store.js +0 -97
  476. package/node_modules/pi-lens/dist/clients/effective-config.js +0 -415
  477. package/node_modules/pi-lens/dist/clients/env-utils.js +0 -83
  478. package/node_modules/pi-lens/dist/clients/error-class.js +0 -23
  479. package/node_modules/pi-lens/dist/clients/eval-timestamp.js +0 -17
  480. package/node_modules/pi-lens/dist/clients/event-loop-hold.js +0 -274
  481. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +0 -250
  482. package/node_modules/pi-lens/dist/clients/extension-log.js +0 -504
  483. package/node_modules/pi-lens/dist/clients/extension-mode.js +0 -73
  484. package/node_modules/pi-lens/dist/clients/feature-hints.js +0 -103
  485. package/node_modules/pi-lens/dist/clients/file-kinds.js +0 -412
  486. package/node_modules/pi-lens/dist/clients/file-role.js +0 -134
  487. package/node_modules/pi-lens/dist/clients/file-time.js +0 -160
  488. package/node_modules/pi-lens/dist/clients/file-utils.js +0 -1032
  489. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +0 -494
  490. package/node_modules/pi-lens/dist/clients/finding-identity.js +0 -88
  491. package/node_modules/pi-lens/dist/clients/fix-worklog.js +0 -103
  492. package/node_modules/pi-lens/dist/clients/format-events-publish.js +0 -343
  493. package/node_modules/pi-lens/dist/clients/format-service.js +0 -222
  494. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +0 -9
  495. package/node_modules/pi-lens/dist/clients/formatters.js +0 -2032
  496. package/node_modules/pi-lens/dist/clients/freshness-cadence.js +0 -17
  497. package/node_modules/pi-lens/dist/clients/freshness.js +0 -38
  498. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +0 -394
  499. package/node_modules/pi-lens/dist/clients/generation-guard.js +0 -215
  500. package/node_modules/pi-lens/dist/clients/git-guard.js +0 -1136
  501. package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +0 -243
  502. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +0 -509
  503. package/node_modules/pi-lens/dist/clients/go-client.js +0 -104
  504. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +0 -605
  505. package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +0 -348
  506. package/node_modules/pi-lens/dist/clients/grammar-source.js +0 -465
  507. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +0 -135
  508. package/node_modules/pi-lens/dist/clients/hashline-anchor.js +0 -424
  509. package/node_modules/pi-lens/dist/clients/hook-budgets.js +0 -76
  510. package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +0 -140
  511. package/node_modules/pi-lens/dist/clients/host-ports.js +0 -39
  512. package/node_modules/pi-lens/dist/clients/indent-retarget.js +0 -154
  513. package/node_modules/pi-lens/dist/clients/inline-blocker-dispositions.js +0 -186
  514. package/node_modules/pi-lens/dist/clients/install-diagnostics.js +0 -156
  515. package/node_modules/pi-lens/dist/clients/installer/index.js +0 -5416
  516. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +0 -58
  517. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +0 -783
  518. package/node_modules/pi-lens/dist/clients/instance-reaper-state.js +0 -7
  519. package/node_modules/pi-lens/dist/clients/instance-reaper.js +0 -1137
  520. package/node_modules/pi-lens/dist/clients/instance-registry-lock.js +0 -145
  521. package/node_modules/pi-lens/dist/clients/instance-registry.js +0 -773
  522. package/node_modules/pi-lens/dist/clients/jscpd-client.js +0 -378
  523. package/node_modules/pi-lens/dist/clients/json-cache-read.js +0 -65
  524. package/node_modules/pi-lens/dist/clients/knip-client.js +0 -788
  525. package/node_modules/pi-lens/dist/clients/language-policy.js +0 -252
  526. package/node_modules/pi-lens/dist/clients/language-profile.js +0 -361
  527. package/node_modules/pi-lens/dist/clients/language-registry.js +0 -603
  528. package/node_modules/pi-lens/dist/clients/latency-logger.js +0 -727
  529. package/node_modules/pi-lens/dist/clients/lazy-import.js +0 -21
  530. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +0 -54
  531. package/node_modules/pi-lens/dist/clients/lens-config.js +0 -404
  532. package/node_modules/pi-lens/dist/clients/lens-engine.js +0 -358
  533. package/node_modules/pi-lens/dist/clients/lens-events.js +0 -125
  534. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +0 -467
  535. package/node_modules/pi-lens/dist/clients/lens-map.js +0 -1156
  536. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +0 -74
  537. package/node_modules/pi-lens/dist/clients/log-cleanup.js +0 -222
  538. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +0 -178
  539. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +0 -421
  540. package/node_modules/pi-lens/dist/clients/lsp/client.js +0 -4593
  541. package/node_modules/pi-lens/dist/clients/lsp/config.js +0 -691
  542. package/node_modules/pi-lens/dist/clients/lsp/content-limits.js +0 -41
  543. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +0 -225
  544. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +0 -467
  545. package/node_modules/pi-lens/dist/clients/lsp/edits.js +0 -1219
  546. package/node_modules/pi-lens/dist/clients/lsp/index.js +0 -8305
  547. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +0 -216
  548. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +0 -298
  549. package/node_modules/pi-lens/dist/clients/lsp/language.js +0 -63
  550. package/node_modules/pi-lens/dist/clients/lsp/launch.js +0 -746
  551. package/node_modules/pi-lens/dist/clients/lsp/lombok.js +0 -158
  552. package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +0 -5
  553. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +0 -251
  554. package/node_modules/pi-lens/dist/clients/lsp/position-encoding.js +0 -61
  555. package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +0 -83
  556. package/node_modules/pi-lens/dist/clients/lsp/server.js +0 -3290
  557. package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +0 -192
  558. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +0 -17
  559. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +0 -56
  560. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +0 -332
  561. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/capability-snapshot.js +0 -1
  562. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +0 -85
  563. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/index.js +0 -10
  564. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +0 -392
  565. package/node_modules/pi-lens/dist/clients/lsp/watch-queue.js +0 -61
  566. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +0 -639
  567. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +0 -30
  568. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +0 -184
  569. package/node_modules/pi-lens/dist/clients/lsp-budget.js +0 -186
  570. package/node_modules/pi-lens/dist/clients/lsp-document-symbols.js +0 -110
  571. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +0 -9
  572. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +0 -396
  573. package/node_modules/pi-lens/dist/clients/lsp-status.js +0 -59
  574. package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +0 -40
  575. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +0 -420
  576. package/node_modules/pi-lens/dist/clients/mcp/host-shim.js +0 -31
  577. package/node_modules/pi-lens/dist/clients/mcp/ipc.js +0 -523
  578. package/node_modules/pi-lens/dist/clients/mcp/review.js +0 -197
  579. package/node_modules/pi-lens/dist/clients/mcp/session.js +0 -412
  580. package/node_modules/pi-lens/dist/clients/memory-sampler.js +0 -208
  581. package/node_modules/pi-lens/dist/clients/message-end-attribution.js +0 -30
  582. package/node_modules/pi-lens/dist/clients/metrics-client.js +0 -107
  583. package/node_modules/pi-lens/dist/clients/metrics-history.js +0 -327
  584. package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +0 -263
  585. package/node_modules/pi-lens/dist/clients/model-provider.js +0 -53
  586. package/node_modules/pi-lens/dist/clients/module-report-lsp.js +0 -189
  587. package/node_modules/pi-lens/dist/clients/module-report.js +0 -2150
  588. package/node_modules/pi-lens/dist/clients/mutating-tool.js +0 -651
  589. package/node_modules/pi-lens/dist/clients/mutation-attribution.js +0 -368
  590. package/node_modules/pi-lens/dist/clients/mutation-bridge.js +0 -240
  591. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +0 -734
  592. package/node_modules/pi-lens/dist/clients/observed-mutation-sources.js +0 -101
  593. package/node_modules/pi-lens/dist/clients/observed-mutation.js +0 -1253
  594. package/node_modules/pi-lens/dist/clients/oldtext-autopatch.js +0 -128
  595. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +0 -496
  596. package/node_modules/pi-lens/dist/clients/opengrep-client.js +0 -333
  597. package/node_modules/pi-lens/dist/clients/opengrep-config.js +0 -76
  598. package/node_modules/pi-lens/dist/clients/package-manager.js +0 -607
  599. package/node_modules/pi-lens/dist/clients/package-root.js +0 -38
  600. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +0 -439
  601. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +0 -43
  602. package/node_modules/pi-lens/dist/clients/path-keyed-map.js +0 -108
  603. package/node_modules/pi-lens/dist/clients/path-utils.js +0 -1244
  604. package/node_modules/pi-lens/dist/clients/performance-report.js +0 -331
  605. package/node_modules/pi-lens/dist/clients/persist-debounce.js +0 -70
  606. package/node_modules/pi-lens/dist/clients/persistent-reverify.js +0 -271
  607. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +0 -119
  608. package/node_modules/pi-lens/dist/clients/pipeline.js +0 -1480
  609. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +0 -37
  610. package/node_modules/pi-lens/dist/clients/print-mode.js +0 -21
  611. package/node_modules/pi-lens/dist/clients/probe-home-state.js +0 -228
  612. package/node_modules/pi-lens/dist/clients/process-bridge.js +0 -66
  613. package/node_modules/pi-lens/dist/clients/process-singletons.js +0 -210
  614. package/node_modules/pi-lens/dist/clients/process-snapshot.js +0 -68
  615. package/node_modules/pi-lens/dist/clients/project-changes.js +0 -181
  616. package/node_modules/pi-lens/dist/clients/project-conventions.js +0 -171
  617. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +0 -137
  618. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +0 -109
  619. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +0 -668
  620. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +0 -118
  621. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/dead-code.js +0 -47
  622. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +0 -40
  623. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/govulncheck.js +0 -34
  624. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/jscpd.js +0 -38
  625. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +0 -47
  626. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +0 -48
  627. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +0 -40
  628. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +0 -127
  629. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +0 -81
  630. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +0 -520
  631. package/node_modules/pi-lens/dist/clients/project-diagnostics/types.js +0 -1
  632. package/node_modules/pi-lens/dist/clients/project-lens-config.js +0 -807
  633. package/node_modules/pi-lens/dist/clients/project-report.js +0 -701
  634. package/node_modules/pi-lens/dist/clients/project-scale.js +0 -237
  635. package/node_modules/pi-lens/dist/clients/project-scan-policy.js +0 -11
  636. package/node_modules/pi-lens/dist/clients/project-snapshot-fingerprint.js +0 -48
  637. package/node_modules/pi-lens/dist/clients/project-snapshot-persist-worker.js +0 -20
  638. package/node_modules/pi-lens/dist/clients/project-snapshot.js +0 -1653
  639. package/node_modules/pi-lens/dist/clients/project-trust.js +0 -181
  640. package/node_modules/pi-lens/dist/clients/python-environment.js +0 -217
  641. package/node_modules/pi-lens/dist/clients/python-provenance.js +0 -639
  642. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +0 -47
  643. package/node_modules/pi-lens/dist/clients/quiet-window.js +0 -236
  644. package/node_modules/pi-lens/dist/clients/read-bridge.js +0 -139
  645. package/node_modules/pi-lens/dist/clients/read-expansion.js +0 -266
  646. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +0 -251
  647. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +0 -1306
  648. package/node_modules/pi-lens/dist/clients/read-guard.js +0 -1561
  649. package/node_modules/pi-lens/dist/clients/recent-touches.js +0 -240
  650. package/node_modules/pi-lens/dist/clients/redact/secrets.js +0 -449
  651. package/node_modules/pi-lens/dist/clients/resource-sampler.js +0 -628
  652. package/node_modules/pi-lens/dist/clients/reverse-deps.js +0 -270
  653. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +0 -5126
  654. package/node_modules/pi-lens/dist/clients/review-graph/format.js +0 -33
  655. package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +0 -163
  656. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +0 -481
  657. package/node_modules/pi-lens/dist/clients/review-graph/persist-worker.js +0 -10
  658. package/node_modules/pi-lens/dist/clients/review-graph/query.js +0 -244
  659. package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +0 -21
  660. package/node_modules/pi-lens/dist/clients/review-graph/service.js +0 -96
  661. package/node_modules/pi-lens/dist/clients/review-graph/shared-extraction-ir.js +0 -82
  662. package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +0 -103
  663. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +0 -299
  664. package/node_modules/pi-lens/dist/clients/review-graph/types.js +0 -1
  665. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +0 -449
  666. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +0 -81
  667. package/node_modules/pi-lens/dist/clients/ruff-client.js +0 -179
  668. package/node_modules/pi-lens/dist/clients/rules-scanner.js +0 -131
  669. package/node_modules/pi-lens/dist/clients/run-duration.js +0 -55
  670. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +0 -746
  671. package/node_modules/pi-lens/dist/clients/runtime-config.js +0 -77
  672. package/node_modules/pi-lens/dist/clients/runtime-context.js +0 -211
  673. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +0 -1324
  674. package/node_modules/pi-lens/dist/clients/runtime-session.js +0 -2485
  675. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +0 -1295
  676. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +0 -2049
  677. package/node_modules/pi-lens/dist/clients/runtime-turn.js +0 -3767
  678. package/node_modules/pi-lens/dist/clients/rust-client.js +0 -97
  679. package/node_modules/pi-lens/dist/clients/safe-spawn.js +0 -2357
  680. package/node_modules/pi-lens/dist/clients/sanitize.js +0 -286
  681. package/node_modules/pi-lens/dist/clients/scan-utils.js +0 -25
  682. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +0 -165
  683. package/node_modules/pi-lens/dist/clients/search-read-registration.js +0 -94
  684. package/node_modules/pi-lens/dist/clients/secret-findings.js +0 -114
  685. package/node_modules/pi-lens/dist/clients/security-scan-client.js +0 -352
  686. package/node_modules/pi-lens/dist/clients/session-event-guard.js +0 -297
  687. package/node_modules/pi-lens/dist/clients/session-lifecycle.js +0 -484
  688. package/node_modules/pi-lens/dist/clients/session-start-observability.js +0 -94
  689. package/node_modules/pi-lens/dist/clients/session-state-store.js +0 -101
  690. package/node_modules/pi-lens/dist/clients/session-summary.js +0 -15
  691. package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +0 -27
  692. package/node_modules/pi-lens/dist/clients/sg-runner.js +0 -876
  693. package/node_modules/pi-lens/dist/clients/sgconfig.js +0 -347
  694. package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +0 -338
  695. package/node_modules/pi-lens/dist/clients/single-flight.js +0 -158
  696. package/node_modules/pi-lens/dist/clients/situational-tool-telemetry.js +0 -108
  697. package/node_modules/pi-lens/dist/clients/skills-resolver.js +0 -141
  698. package/node_modules/pi-lens/dist/clients/slow-fs.js +0 -138
  699. package/node_modules/pi-lens/dist/clients/smells-rollup.js +0 -231
  700. package/node_modules/pi-lens/dist/clients/source-filter.js +0 -596
  701. package/node_modules/pi-lens/dist/clients/source-walker.js +0 -211
  702. package/node_modules/pi-lens/dist/clients/spawn-output-cap.js +0 -120
  703. package/node_modules/pi-lens/dist/clients/spawn-timeout-cooldown.js +0 -110
  704. package/node_modules/pi-lens/dist/clients/stale-marker.js +0 -17
  705. package/node_modules/pi-lens/dist/clients/startup-scan.js +0 -400
  706. package/node_modules/pi-lens/dist/clients/startup-timing.js +0 -130
  707. package/node_modules/pi-lens/dist/clients/string-utils.js +0 -33
  708. package/node_modules/pi-lens/dist/clients/subagent-mode.js +0 -111
  709. package/node_modules/pi-lens/dist/clients/symbol-containment.js +0 -24
  710. package/node_modules/pi-lens/dist/clients/symbol-types.js +0 -5
  711. package/node_modules/pi-lens/dist/clients/test-runner-client.js +0 -2507
  712. package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +0 -287
  713. package/node_modules/pi-lens/dist/clients/todo-scanner.js +0 -229
  714. package/node_modules/pi-lens/dist/clients/tool-agreement.js +0 -423
  715. package/node_modules/pi-lens/dist/clients/tool-config.js +0 -249
  716. package/node_modules/pi-lens/dist/clients/tool-cwd.js +0 -270
  717. package/node_modules/pi-lens/dist/clients/tool-definition.js +0 -47
  718. package/node_modules/pi-lens/dist/clients/tool-event.js +0 -105
  719. package/node_modules/pi-lens/dist/clients/tool-policy.js +0 -2755
  720. package/node_modules/pi-lens/dist/clients/tool-probe.js +0 -53
  721. package/node_modules/pi-lens/dist/clients/tool-render.js +0 -156
  722. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +0 -118
  723. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +0 -364
  724. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +0 -4195
  725. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +0 -100
  726. package/node_modules/pi-lens/dist/clients/tree-sitter-navigator.js +0 -269
  727. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +0 -569
  728. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +0 -158
  729. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +0 -1265
  730. package/node_modules/pi-lens/dist/clients/trivy-client.js +0 -467
  731. package/node_modules/pi-lens/dist/clients/tui-fit.js +0 -50
  732. package/node_modules/pi-lens/dist/clients/turn-context.js +0 -52
  733. package/node_modules/pi-lens/dist/clients/turn-end/lane.js +0 -51
  734. package/node_modules/pi-lens/dist/clients/turn-end/lanes/govulncheck.js +0 -112
  735. package/node_modules/pi-lens/dist/clients/turn-end/lanes/secrets.js +0 -204
  736. package/node_modules/pi-lens/dist/clients/turn-summary-render.js +0 -72
  737. package/node_modules/pi-lens/dist/clients/turn-summary.js +0 -132
  738. package/node_modules/pi-lens/dist/clients/typos-config.js +0 -29
  739. package/node_modules/pi-lens/dist/clients/user-notify.js +0 -51
  740. package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +0 -57
  741. package/node_modules/pi-lens/dist/clients/warm-attach.js +0 -305
  742. package/node_modules/pi-lens/dist/clients/widget-state.js +0 -1808
  743. package/node_modules/pi-lens/dist/clients/word-index-logger.js +0 -49
  744. package/node_modules/pi-lens/dist/clients/word-index-store.js +0 -459
  745. package/node_modules/pi-lens/dist/clients/word-index.js +0 -2080
  746. package/node_modules/pi-lens/dist/clients/workspace-topology.js +0 -443
  747. package/node_modules/pi-lens/dist/clients/write-ordering-guard.js +0 -65
  748. package/node_modules/pi-lens/dist/clients/zizmor-config.js +0 -340
  749. package/node_modules/pi-lens/dist/i18n.js +0 -69
  750. package/node_modules/pi-lens/dist/mcp/build-staleness.js +0 -124
  751. package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +0 -583
  752. package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +0 -129
  753. package/node_modules/pi-lens/dist/scripts/lib/web-tree-sitter-dir.mjs +0 -123
  754. package/node_modules/pi-lens/dist/tools/activate-tools.js +0 -87
  755. package/node_modules/pi-lens/dist/tools/ast-grep-outline.js +0 -137
  756. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +0 -244
  757. package/node_modules/pi-lens/dist/tools/ast-grep-search.js +0 -762
  758. package/node_modules/pi-lens/dist/tools/effective-config.js +0 -90
  759. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +0 -375
  760. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +0 -2571
  761. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +0 -1745
  762. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +0 -1367
  763. package/node_modules/pi-lens/dist/tools/lsp-structured-output.js +0 -294
  764. package/node_modules/pi-lens/dist/tools/module-report.js +0 -382
  765. package/node_modules/pi-lens/dist/tools/project-report.js +0 -86
  766. package/node_modules/pi-lens/dist/tools/render-compact.js +0 -374
  767. package/node_modules/pi-lens/dist/tools/scan-progress.js +0 -53
  768. package/node_modules/pi-lens/dist/tools/shared.js +0 -29
  769. package/node_modules/pi-lens/dist/tools/symbol-search.js +0 -155
@@ -79,6 +79,7 @@ async function main() {
79
79
  analyzeActionableWarnings(files.actionableWarnings, state),
80
80
  analyzeAstGrepTools(files.astGrepTools, state),
81
81
  analyzeWorklog(files.worklog, state),
82
+ analyzeExtension(files.extension, state),
82
83
  ]);
83
84
 
84
85
  const report = buildReport(state);
@@ -165,18 +166,34 @@ function discoverLogFiles(logRoot, archived) {
165
166
  });
166
167
 
167
168
  return {
168
- latency: byPrefix("latency"),
169
- sessionStart: byPrefix("sessionstart"),
170
- cascade: byPrefix("cascade"),
171
- readGuard: byPrefix("read-guard"),
172
- treeSitter: byPrefix("tree-sitter"),
173
- actionableWarnings: byPrefix("actionable-warnings"),
174
- astGrepTools: byPrefix("ast-grep-tools"),
169
+ latency: chronologicalFiles(byPrefix("latency")),
170
+ sessionStart: chronologicalFiles(byPrefix("sessionstart")),
171
+ cascade: chronologicalFiles(byPrefix("cascade")),
172
+ readGuard: chronologicalFiles(byPrefix("read-guard")),
173
+ treeSitter: chronologicalFiles(byPrefix("tree-sitter")),
174
+ actionableWarnings: chronologicalFiles(byPrefix("actionable-warnings")),
175
+ astGrepTools: chronologicalFiles(byPrefix("ast-grep-tools")),
176
+ extension: chronologicalFiles(byPrefix("extension")),
175
177
  diagnostics: dailyLogs,
176
178
  worklog: worklogs,
177
179
  };
178
180
  }
179
181
 
182
+ /**
183
+ * Rotated siblings (`x.log.1`) carry older rows than the active `x.log`. The
184
+ * run-splitting detectors (D3/D4/D5) and the read-guard evidence walk (D10)
185
+ * only read correctly in chronological order, so order every multi-file stream
186
+ * oldest-first with the active file last.
187
+ */
188
+ function chronologicalFiles(files) {
189
+ return [...files].sort((a, b) => {
190
+ const activeA = path.basename(a).endsWith(".log");
191
+ const activeB = path.basename(b).endsWith(".log");
192
+ if (activeA !== activeB) return activeA ? 1 : -1;
193
+ return a.localeCompare(b);
194
+ });
195
+ }
196
+
180
197
  function safeReaddir(dir) {
181
198
  try {
182
199
  return fs.readdirSync(dir);
@@ -205,6 +222,45 @@ function createState(files) {
205
222
  toolResults: counter(),
206
223
  phaseCounts: counter(),
207
224
  phaseTimeouts: counter(),
225
+ // D1: every latency row timestamp (ms) in window, for the coverage-gap
226
+ // scan. Sorted after all files are read.
227
+ rowTs: [],
228
+ cascadeTs: [],
229
+ // D2: real-log pollution — rows whose filePath points at a test
230
+ // home, grouped by pid.
231
+ scratchRows: new Map(),
232
+ // D5: test_runner_delivery outcomes per session, joined with the
233
+ // sessionstart firing/stale text into one delivery-health verdict.
234
+ testRunnerDelivery: new Map(),
235
+ // D6: `phase: knip` executed rows per pid, for the drift and cost rules.
236
+ knip: new Map(),
237
+ // D7: hook-await-exceeded overruns plus the per-pid degradation census.
238
+ hookAwaitExceeded: [],
239
+ degradationKinds: new Map(),
240
+ // D8: turn_end `tool_result` summaries per pid, and the retained-state text.
241
+ turnEndTools: new Map(),
242
+ retainingNewerTurn: [],
243
+ // D9: empty-candidate LSP waits, edit touches that saw no client, and
244
+ // warm-reuse selections, joined into the wait/contradiction verdict.
245
+ lspWaitEmpty: new Map(),
246
+ lspTouchEdit: new Map(),
247
+ lspTouchNoClients: [],
248
+ lspClientSelectedWarm: [],
249
+ // D11: read-set carry outcomes at a restart boundary.
250
+ carryRestart: [],
251
+ // D12: nudges and scope transitions, joined by pid + wall clock.
252
+ agentNudges: [],
253
+ sessionScopeTransitions: [],
254
+ // D13: deferred runner failures joined to their delivery row by (pid, turnId).
255
+ deferredRunnerFailed: [],
256
+ lateRunnerFindings: [],
257
+ // D14: auxiliary stuck pair (pid, filePath, serverId) recurrence.
258
+ auxStuck: new Map(),
259
+ // D15: advisory provenance decisions with unknown provenance.
260
+ advisoryProvenance: [],
261
+ // D8/D15: first latency row timestamp per pid (lifetime base).
262
+ pidFirstTs: new Map(),
263
+ pidLastTs: new Map(),
208
264
  workspace: {
209
265
  started: 0,
210
266
  completed: 0,
@@ -280,6 +336,24 @@ function createState(files) {
280
336
  staleRanges: [],
281
337
  zeroReads: [],
282
338
  unavailableSnapshots: [],
339
+ // E2: the block/warn split. These rows do not carry a host-side
340
+ // decision, so the analyzer must not invent one.
341
+ blocks: [],
342
+ warns: [],
343
+ blockByKind: counter(),
344
+ bypassedMismatch: 0,
345
+ // D10: per-(session,file) evidence that a read or a committed edit
346
+ // happened earlier in the session, so a later `zero_read` block is a
347
+ // lost read-set rather than a genuine cold edit.
348
+ fileEvidence: new Map(),
349
+ stateLost: [],
350
+ genuineZeroRead: [],
351
+ },
352
+ extension: {
353
+ // D2: extension.log rows whose payload names a test home
354
+ // directory, grouped by pid, plus the warn/error census.
355
+ scratchRows: new Map(),
356
+ warnErrorGroups: counter(),
283
357
  },
284
358
  treeSitter: {
285
359
  phases: counter(),
@@ -292,12 +366,26 @@ function createState(files) {
292
366
  session: {
293
367
  starts: 0,
294
368
  cwds: counter(),
369
+ // E4: per-start build identity (`commit=`), so every rate can be split
370
+ // by build. Keyed by commit, with the start timestamps that carried it.
371
+ commits: counter(),
295
372
  slowStarts: [],
296
373
  slowTasks: [],
297
374
  toolNoise: counter(),
298
375
  lspNoise: counter(),
299
376
  errors: [],
300
- rotations: counter(),
377
+ // D1: every sessionstart line timestamp (ms) in window.
378
+ rowTs: [],
379
+ // D16: `pi-lens loaded: Nms` lines, with their wall clock so a short
380
+ // lived pid can be marked.
381
+ slowLoads: [],
382
+ // D16: the wall clock of every `session_start fired`, so a load with no
383
+ // session start within 60 s (a short-lived pid) is visible.
384
+ firedTs: [],
385
+ // D3/D4/D5/D8: one run per `session_start fired`. Run splitting is the
386
+ // only way to attribute a turn-end test decision to the session that made it.
387
+ runs: [],
388
+ currentRun: newRun(),
301
389
  },
302
390
  actionable: {
303
391
  events: counter(),
@@ -329,13 +417,26 @@ function createState(files) {
329
417
  };
330
418
  }
331
419
 
420
+ /**
421
+ * The failure kind of a `failed` or `crashed` runner row. `metadata.failureKind`
422
+ * is exact: inline rows carry it (#3781) and collect-later rows too (#3796).
423
+ * Rows written by released versions never carry it, so the count heuristic
424
+ * (a failure with diagnostics is found-errors) stays only as that fallback.
425
+ */
426
+ function runnerFailureKind(entry) {
427
+ if (entry.metadata?.failureKind) return entry.metadata.failureKind;
428
+ if (entry.status === "crashed") return "crashed";
429
+ return (entry.diagnosticCount ?? 0) > 0 ? "blocking_diagnostics" : "unknown";
430
+ }
431
+
332
432
  async function analyzeLatency(files, state) {
333
433
  for (const file of files) {
334
434
  await forEachJsonLine(file, "latency", state, (entry) => {
335
435
  const ts = dateOf(entry.ts);
336
436
  if (!inWindow(ts)) return;
337
437
  state.seen.inc("latency");
338
- trackProject(state, entry.filePath);
438
+ trackLatencyProject(state, entry);
439
+ trackLatencySignals(state, entry, ts);
339
440
 
340
441
  if (entry.type === "runner") {
341
442
  const status = entry.status ?? "unknown";
@@ -344,15 +445,8 @@ async function analyzeLatency(files, state) {
344
445
  if (status === "failed" || status === "crashed") {
345
446
  // Separate a genuine runner breakage from "the check ran and found
346
447
  // blocking issues" (e.g. the LSP runner reports status:failed when a
347
- // file has type errors). Prefer the logged failureKind; fall back to
348
- // the heuristic that a failure carrying diagnostics is found-errors.
349
- const kind =
350
- entry.metadata?.failureKind ??
351
- (status === "crashed"
352
- ? "crashed"
353
- : (entry.diagnosticCount ?? 0) > 0
354
- ? "blocking_diagnostics"
355
- : "unknown");
448
+ // file has type errors).
449
+ const kind = runnerFailureKind(entry);
356
450
  state.latency.runnerFailureKinds.inc(`${runner}:${kind}`);
357
451
  if (kind === "blocking_diagnostics") {
358
452
  state.latency.runnerBlockingFindings.inc(runner);
@@ -543,6 +637,7 @@ async function analyzeCascade(files, state) {
543
637
  const ts = dateOf(entry.ts);
544
638
  if (!inWindow(ts)) return;
545
639
  state.seen.inc("cascade");
640
+ if (ts) state.latency.cascadeTs.push(ts.getTime());
546
641
  trackProject(state, entry.filePath);
547
642
  const phase = entry.phase ?? "unknown";
548
643
  state.cascade.phases.inc(phase);
@@ -589,24 +684,76 @@ async function analyzeReadGuard(files, state) {
589
684
  for (const file of files) {
590
685
  await forEachJsonLine(file, "read-guard", state, (entry) => {
591
686
  const ts = dateOf(entry.ts);
592
- if (!inWindow(ts)) return;
687
+ if (!inWindow(ts)) {
688
+ // D10 needs evidence that may precede a live-monitor window.
689
+ // Keep this lookback state private; outside rows never affect counts.
690
+ if (hasReadEvidence(entry)) {
691
+ const key = `${readGuardSessionOf(entry) ?? "?"}\u0000${entry.filePath ?? "?"}`;
692
+ state.readGuard.fileEvidence.set(key, true);
693
+ }
694
+ return;
695
+ }
593
696
  state.seen.inc("read-guard");
594
697
  trackProject(state, entry.filePath);
595
698
  const event = entry.event ?? "unknown";
699
+ const md = entry.metadata ?? {};
596
700
  state.readGuard.events.inc(event);
597
701
  state.readGuard.byFile.inc(shortPath(entry.filePath));
598
- const reasonKind = entry.metadata?.reasonKind;
702
+ const reasonKind = md.reasonKind;
599
703
  if (reasonKind) state.readGuard.byReason.inc(reasonKind);
704
+
705
+ // D10: evidence that this file was read or edited earlier in the
706
+ // SAME session. Read before this row's own evidence is recorded, so a
707
+ // later `zero_read` block sees only the prior rows. An
708
+ // `edit_batch_summary` carries no sessionId, so its session is the
709
+ // turnId prefix before the last `:`.
710
+ const evidenceKey = `${readGuardSessionOf(entry) ?? "?"}\u0000${entry.filePath ?? "?"}`;
711
+ const hadEvidence =
712
+ state.readGuard.fileEvidence.get(evidenceKey) === true;
713
+
600
714
  if (event === "edit_preflight_blocked") {
601
715
  state.readGuard.preflightReasons.inc(reasonKind ?? "unknown");
716
+ registerBlock(state, entry, "preflight");
717
+ }
718
+ if (event === "edit_blocked") {
719
+ const summary = registerBlock(state, entry, "blocked");
720
+ if (reasonKind === "zero_read") {
721
+ pushTop(state.readGuard.zeroReads, summary, limit * 3, byLine);
722
+ if (hadEvidence) {
723
+ pushTop(
724
+ state.readGuard.stateLost,
725
+ { ...summary, classification: "state-lost" },
726
+ limit * 3,
727
+ byLine,
728
+ );
729
+ } else {
730
+ pushTop(
731
+ state.readGuard.genuineZeroRead,
732
+ { ...summary, classification: "genuine" },
733
+ limit * 3,
734
+ byLine,
735
+ );
736
+ }
737
+ }
738
+ }
739
+ if (event === "edit_warned") {
740
+ pushTop(
741
+ state.readGuard.warns,
742
+ summarizeReadGuard(entry),
743
+ limit * 3,
744
+ byLine,
745
+ );
602
746
  }
603
747
  if (event === "range_snapshot_validation") {
604
- const status = entry.metadata?.status ?? "unknown";
748
+ const status = md.status ?? "unknown";
749
+ const outcome = String(md.outcome ?? "");
605
750
  state.readGuard.snapshotStatus.inc(status);
606
751
  state.readGuard.snapshotEnforcement.inc(
607
- entry.metadata?.enforced ? "enforced" : "not_enforced",
752
+ md.enforced ? "enforced" : "not_enforced",
608
753
  );
609
- if (status === "mismatch") {
754
+ // E3: `bypassed-content-match` is an allowed edit, not a stale
755
+ // read. Only a mismatch the caller did NOT bypass is a smell.
756
+ if (status === "mismatch" && !outcome.startsWith("bypassed")) {
610
757
  state.smellTotals.inc("read-guard-stale-ranges");
611
758
  pushTop(
612
759
  state.readGuard.staleRanges,
@@ -614,6 +761,8 @@ async function analyzeReadGuard(files, state) {
614
761
  limit * 3,
615
762
  byLine,
616
763
  );
764
+ } else if (status === "mismatch") {
765
+ state.readGuard.bypassedMismatch += 1;
617
766
  } else if (status === "unavailable") {
618
767
  pushTop(
619
768
  state.readGuard.unavailableSnapshots,
@@ -623,21 +772,11 @@ async function analyzeReadGuard(files, state) {
623
772
  );
624
773
  }
625
774
  }
626
- if (event === "edit_blocked" || event === "edit_warned") {
627
- state.smellTotals.inc("read-guard-friction");
628
- const summary = summarizeReadGuard(entry);
629
- pushTop(state.readGuard.blocked, summary, limit * 3, byLine);
630
- if (reasonKind === "zero_read") {
631
- pushTop(state.readGuard.zeroReads, summary, limit * 3, byLine);
632
- }
633
- }
634
775
  if (
635
776
  event === "oldtext_not_found" ||
636
777
  event === "oldtext_duplicate" ||
637
- event === "touched_lines_missing" ||
638
- event === "edit_preflight_blocked"
778
+ event === "touched_lines_missing"
639
779
  ) {
640
- state.smellTotals.inc("read-guard-friction");
641
780
  pushTop(
642
781
  state.readGuard.oldTextIssues,
643
782
  summarizeReadGuard(entry),
@@ -645,10 +784,48 @@ async function analyzeReadGuard(files, state) {
645
784
  byLine,
646
785
  );
647
786
  }
787
+
788
+ if (hasReadEvidence(entry))
789
+ state.readGuard.fileEvidence.set(evidenceKey, true);
648
790
  });
649
791
  }
650
792
  }
651
793
 
794
+ /** D10: a row proving the file was read or edited earlier in its session. */
795
+ function hasReadEvidence(entry) {
796
+ const md = entry.metadata ?? {};
797
+ return (
798
+ entry.event === "edit_batch_summary" ||
799
+ (entry.event === "range_snapshot_validation" &&
800
+ Number(md.candidateReadCount ?? 0) > 0) ||
801
+ (entry.event === "edit_warned" && Number(md.readCount ?? 0) > 0)
802
+ );
803
+ }
804
+
805
+ /**
806
+ * D10: the session a read-guard row belongs to. `edit_batch_summary` rows carry
807
+ * no `sessionId`, so fall back to the turnId prefix before the last `:` (the
808
+ * report's own join instruction).
809
+ */
810
+ function readGuardSessionOf(entry) {
811
+ if (typeof entry.sessionId === "string" && entry.sessionId)
812
+ return entry.sessionId;
813
+ const turnId = entry.turnId;
814
+ if (typeof turnId === "string" && turnId.includes(":"))
815
+ return turnId.slice(0, turnId.lastIndexOf(":"));
816
+ return null;
817
+ }
818
+
819
+ /** E2: one blocked edit. Host/model provenance is not present on this row. */
820
+ function registerBlock(state, entry, source) {
821
+ const summary = summarizeReadGuard(entry);
822
+ pushTop(state.readGuard.blocks, summary, limit * 3, byLine);
823
+ state.readGuard.blockByKind.inc(
824
+ `${source}:${entry.metadata?.reasonKind ?? "unknown"}`,
825
+ );
826
+ return summary;
827
+ }
828
+
652
829
  async function analyzeTreeSitter(files, state) {
653
830
  for (const file of files) {
654
831
  await forEachJsonLine(file, "tree-sitter", state, (entry) => {
@@ -704,21 +881,48 @@ async function analyzeSessionStart(files, state) {
704
881
  const match = lineRe.exec(line);
705
882
  if (!match) return;
706
883
  const ts = dateOf(match[1]);
707
- if (!inWindow(ts)) return;
708
884
  const message = match[2];
885
+ if (!inWindow(ts)) {
886
+ // A --since window can begin after the session_start that owns its
887
+ // rows. Carry only that run anchor into the visible window.
888
+ if (message.startsWith("session_start fired")) {
889
+ state.session.currentRun = newRun();
890
+ state.session.currentRun.startTs = iso(ts);
891
+ }
892
+ return;
893
+ }
709
894
  if (isExcludedText(message)) {
710
895
  state.excludedRows++;
711
896
  return;
712
897
  }
713
898
  state.seen.inc("sessionstart");
899
+ if (ts) state.session.rowTs.push(ts.getTime());
900
+
901
+ // E4: starts are counted from `session_start fired`, which every mode
902
+ // writes, instead of the dead `session_start cwd:` full-mode line. The
903
+ // same line opens a D3/D4/D5/D8 run.
904
+ if (message.startsWith("session_start fired")) {
905
+ state.session.starts++;
906
+ if (ts) state.session.firedTs.push(ts.getTime());
907
+ state.session.runs.push(state.session.currentRun);
908
+ state.session.currentRun = newRun();
909
+ state.session.currentRun.startTs = iso(ts);
910
+ }
714
911
 
715
912
  const cwd = /session_start cwd:\s*(.*)$/.exec(message)?.[1];
716
913
  if (cwd) {
717
- state.session.starts++;
718
914
  state.session.cwds.inc(projectOf(cwd));
719
915
  trackProject(state, cwd);
720
916
  }
721
917
 
918
+ // E4: build identity for the session that just started.
919
+ const buildIdentity =
920
+ /session_start: build identity [—-] commit=(\w+)/.exec(message);
921
+ if (buildIdentity) {
922
+ state.session.commits.inc(buildIdentity[1]);
923
+ state.session.currentRun.commit = buildIdentity[1];
924
+ }
925
+
722
926
  // #2526 R3 S1: this session's config-resolution PENDING mark, published
723
927
  // by `loadLSPConfig` itself at the instant a resolution is actually
724
928
  // attempted — never a start-line prediction from `no-lsp`/subagent/
@@ -780,8 +984,49 @@ async function analyzeSessionStart(files, state) {
780
984
  );
781
985
  }
782
986
 
783
- if (message.includes("log_cleanup: rotated"))
784
- state.session.rotations.inc(message.replace(/^log_cleanup:\s*/, ""));
987
+ // D16: extension load time is invisible to `slow-session-start`, which
988
+ // reads `session_start total:` (a quick-mode total of tens of ms).
989
+ const load = /pi-lens loaded: (\d+)ms after process start/.exec(message);
990
+ if (load) {
991
+ state.session.slowLoads.push({
992
+ ts: iso(ts),
993
+ ms: ts ? ts.getTime() : null,
994
+ durationMs: Number(load[1]),
995
+ message,
996
+ });
997
+ }
998
+
999
+ const run = state.session.currentRun;
1000
+ const modified = /turn_end: (\d+) file\(s\) modified/.exec(message);
1001
+ if (modified) run.edits += 1;
1002
+ if (message.includes("excluded by the built-in turn-end policy")) {
1003
+ run.excluded += 1;
1004
+ const excludedPath = /turn_end:\s*(\S+)\s+→ test target excluded/.exec(
1005
+ message,
1006
+ )?.[1];
1007
+ if (excludedPath) run.excludedTestFiles.push(excludedPath);
1008
+ }
1009
+ if (message.includes("→ no test file found")) run.noTestFile += 1;
1010
+ const vitest =
1011
+ /^turn_end:\s+(.+?)\s+→\s+test vitest\s+(\S+)\s+\(([^)]+)\)/.exec(
1012
+ message,
1013
+ );
1014
+ if (vitest) {
1015
+ run.ran += 1;
1016
+ const [, src, tgt, mode] = vitest;
1017
+ if (mode.includes("failed-first")) {
1018
+ run.failedFirst += 1;
1019
+ const a = checkoutOf(src);
1020
+ const b = checkoutOf(tgt);
1021
+ if (a && b && a !== b) run.crossCheckout += 1;
1022
+ }
1023
+ }
1024
+ if (/turn_end: firing \d+ test target\(s\) async/.test(message))
1025
+ run.testFirings += 1;
1026
+ if (/\(stale\s*[—-]\s*turn advanced while tests ran\)/.test(message))
1027
+ run.testStale += 1;
1028
+ if (/turn_end: retaining newer turn state/.test(message))
1029
+ run.retainingNewerTurn += 1;
785
1030
 
786
1031
  const lower = message.toLowerCase();
787
1032
  if (
@@ -792,8 +1037,16 @@ async function analyzeSessionStart(files, state) {
792
1037
  state.smellTotals.inc("tool-install-noise");
793
1038
  state.session.toolNoise.inc(normalizeSessionNoise(message));
794
1039
  }
1040
+ // E1: anchor to the production failure emitters so a worktree name
1041
+ // such as `468-wait-timeout` inside a `cwd=`/`command=` token cannot
1042
+ // match (the 20 false positives in the live session). Every emitter
1043
+ // puts the failure word before any `key=` token: `lsp spawn <id>:
1044
+ // unavailable|failed` (clients/lsp/index.ts) and `lsp launch
1045
+ // candidate|managed|bundle|tree-bin failed` (clients/lsp/server.ts).
1046
+ // `lsp read warm unavailable` is informational, and `lsp process
1047
+ // <cmd>:` only ever logs `spawn-error` or `closed` (clients/lsp/launch.ts).
795
1048
  if (
796
- /lsp .*?(unavailable|failed|timeout|skipped_broken|exited immediately|binary not found)/i.test(
1049
+ /^lsp (?:spawn [^:]+: (?:unavailable|failed)|launch (?:candidate|managed|bundle|tree-bin) failed)/.test(
797
1050
  message,
798
1051
  )
799
1052
  ) {
@@ -814,6 +1067,9 @@ async function analyzeSessionStart(files, state) {
814
1067
  }
815
1068
  });
816
1069
  }
1070
+ // Close the final run so the last `session_start fired` block is analyzed.
1071
+ if (state.session.currentRun.startTs)
1072
+ state.session.runs.push(state.session.currentRun);
817
1073
  }
818
1074
 
819
1075
  async function analyzeActionableWarnings(files, state) {
@@ -984,12 +1240,18 @@ function pathValues(value, key = "") {
984
1240
  }
985
1241
 
986
1242
  function isExcludedText(text) {
1243
+ // E4: the old match only ever saw the dead `session_start cwd:` line. The
1244
+ // live cwd/root carriers are the pending-resolution mark and the spawn lines.
1245
+ const values = [];
987
1246
  const cwd = /session_start cwd:\s*(.*)$/.exec(text)?.[1];
988
- return cwd
989
- ? excludeGlobs.some((glob) =>
990
- minimatch(normalizePath(cwd), glob, { nocase: true, dot: true }),
991
- )
992
- : false;
1247
+ if (cwd) values.push(cwd);
1248
+ for (const match of text.matchAll(/\b(?:cwd|root)=(\S+)/g))
1249
+ values.push(match[1]);
1250
+ return values.some((value) =>
1251
+ excludeGlobs.some((glob) =>
1252
+ minimatch(normalizePath(value), glob, { nocase: true, dot: true }),
1253
+ ),
1254
+ );
993
1255
  }
994
1256
 
995
1257
  async function forEachLine(file, visitor) {
@@ -1200,19 +1462,18 @@ function summarizeCascade(entry) {
1200
1462
  }
1201
1463
 
1202
1464
  function summarizeReadGuard(entry) {
1465
+ const md = entry.metadata ?? {};
1466
+ // R2: the old offset/limit/symbol fields were undefined on 100% of rows.
1467
+ // `touchedLines[0]`/`range[0]` are the real line identity the rows carry.
1468
+ const touched = Array.isArray(md.touchedLines) ? md.touchedLines : [];
1469
+ const range = Array.isArray(md.range) ? md.range : [];
1203
1470
  return {
1204
1471
  ts: entry.ts,
1205
1472
  event: entry.event,
1206
1473
  filePath: shortPath(entry.filePath),
1207
1474
  project: projectOf(entry.filePath),
1208
- requestedOffset: entry.requestedOffset,
1209
- requestedLimit: entry.requestedLimit,
1210
- effectiveOffset: entry.effectiveOffset,
1211
- effectiveLimit: entry.effectiveLimit,
1212
- symbol: entry.symbol,
1213
- symbolKind: entry.symbolKind,
1214
- line: entry.symbolStartLine,
1215
- metadata: entry.metadata,
1475
+ line: touched[0] ?? range[0],
1476
+ metadata: md,
1216
1477
  };
1217
1478
  }
1218
1479
 
@@ -1295,9 +1556,613 @@ function normalizePath(filePath) {
1295
1556
  return String(filePath).replace(/\\/g, "/");
1296
1557
  }
1297
1558
 
1559
+ /** One D3/D4/D5/D8 run, opened by a `session_start fired` line. */
1560
+ function newRun() {
1561
+ return {
1562
+ startTs: null,
1563
+ commit: null,
1564
+ edits: 0,
1565
+ ran: 0,
1566
+ failedFirst: 0,
1567
+ crossCheckout: 0,
1568
+ excluded: 0,
1569
+ excludedTestFiles: [],
1570
+ noTestFile: 0,
1571
+ retainingNewerTurn: 0,
1572
+ testFirings: 0,
1573
+ testStale: 0,
1574
+ };
1575
+ }
1576
+
1577
+ /** The checkout a turn-end path belongs to (`.worktrees/<id>/` or the main `src/`). */
1578
+ function checkoutOf(p) {
1579
+ const m = /\.worktrees\/([^/]+)\//.exec(p);
1580
+ if (m) return m[1];
1581
+ if (/^(?:src|tests?)\//.test(p)) return "main";
1582
+ return null;
1583
+ }
1584
+
1585
+ /**
1586
+ * D2: the markers only a test home carries: the #3521 fork-tree witness home
1587
+ * and the suite's `pi-lens-test-*` temp dirs. `pi-lens-worktrees`,
1588
+ * `pi-lens-orchestrator/tmp` and `.probe-home/` are not markers: real
1589
+ * sessions edit there (the orchestrator writes its task files under
1590
+ * `.probe-home/orchestration/`).
1591
+ */
1592
+ const SCRATCH_PATH_RE = /(witness-home|pi-lens-test-)/;
1593
+
1594
+ /**
1595
+ * E5/D2: the `opaque_mutation_*` phases log the bash command as `filePath`
1596
+ * (clients/runtime-tool-call.ts, clients/runtime-tool-result.ts), except
1597
+ * `opaque_mutation_recovered`, which logs the recovered paths.
1598
+ */
1599
+ function isCommandTextRow(entry) {
1600
+ const phase = String(entry.phase ?? "");
1601
+ return (
1602
+ phase.startsWith("opaque_mutation_") &&
1603
+ phase !== "opaque_mutation_recovered"
1604
+ );
1605
+ }
1606
+
1607
+ /** E5: track a latency row's project unless its `filePath` is a shell command. */
1608
+ function trackLatencyProject(state, entry) {
1609
+ const filePath = entry?.filePath;
1610
+ if (typeof filePath !== "string" || filePath.length === 0) return;
1611
+ if (isCommandTextRow(entry)) return;
1612
+ // The `<pi-lens>` sentinel is never a project path.
1613
+ if (filePath.startsWith("<")) return;
1614
+ state.projects.inc(projectOf(filePath));
1615
+ }
1616
+
1617
+ function trackLatencyPollution(state, entry) {
1618
+ if (isCommandTextRow(entry)) return;
1619
+ const hit = pathValues(entry).some((value) => SCRATCH_PATH_RE.test(value));
1620
+ if (!hit) return;
1621
+ const pid = String(entry.pid ?? "unknown");
1622
+ state.latency.scratchRows.set(
1623
+ pid,
1624
+ (state.latency.scratchRows.get(pid) ?? 0) + 1,
1625
+ );
1626
+ }
1627
+
1628
+ /**
1629
+ * Every latency signal the D5/D6/D7/D9/D11-D15 detectors read. Kept in one
1630
+ * function so the streaming visitor stays a thin fan-out.
1631
+ */
1632
+ function trackLatencySignals(state, entry, ts) {
1633
+ const ms = ts instanceof Date ? ts.getTime() : null;
1634
+ if (ms != null && Number.isFinite(ms)) state.latency.rowTs.push(ms);
1635
+ const pid = String(entry.pid ?? "unknown");
1636
+ if (ms != null && Number.isFinite(ms)) {
1637
+ const first = state.latency.pidFirstTs.get(pid);
1638
+ if (first == null || ms < first) state.latency.pidFirstTs.set(pid, ms);
1639
+ const last = state.latency.pidLastTs.get(pid);
1640
+ if (last == null || ms > last) state.latency.pidLastTs.set(pid, ms);
1641
+ }
1642
+ trackLatencyPollution(state, entry);
1643
+
1644
+ if (entry.type === "runner") {
1645
+ const md = entry.metadata ?? {};
1646
+ if (
1647
+ entry.status === "failed" &&
1648
+ md.tier === "collect-later" &&
1649
+ Number(entry.diagnosticCount ?? 0) > 0 &&
1650
+ runnerFailureKind(entry) === "blocking_diagnostics"
1651
+ ) {
1652
+ state.latency.deferredRunnerFailed.push({
1653
+ ts: entry.ts,
1654
+ ms,
1655
+ pid,
1656
+ turnId: entry.turnId,
1657
+ runnerId: entry.runnerId,
1658
+ diagnosticCount: Number(entry.diagnosticCount ?? 0),
1659
+ filePath: entry.filePath,
1660
+ });
1661
+ }
1662
+ return;
1663
+ }
1664
+
1665
+ if (entry.type === "tool_result") {
1666
+ // D8: the turn_end summary row carries `blockerSections`; the existing
1667
+ // tool_result branch only counts `result`, so it never saw this duration.
1668
+ if (entry.metadata && entry.metadata.blockerSections != null) {
1669
+ const rows = state.latency.turnEndTools.get(pid) ?? [];
1670
+ rows.push({
1671
+ ts: entry.ts,
1672
+ ms,
1673
+ durationMs: Number(entry.durationMs ?? 0),
1674
+ });
1675
+ state.latency.turnEndTools.set(pid, rows);
1676
+ }
1677
+ return;
1678
+ }
1679
+ if (entry.type !== "phase") return;
1680
+
1681
+ const phase = entry.phase ?? "unknown";
1682
+ const md = entry.metadata ?? {};
1683
+ const nowIso = entry.ts;
1684
+
1685
+ if (phase === "test_runner_delivery") {
1686
+ const sid = md.sessionId ?? pid;
1687
+ const c = state.latency.testRunnerDelivery.get(sid) ?? counter();
1688
+ c.inc(md.outcome ?? "unknown");
1689
+ state.latency.testRunnerDelivery.set(sid, c);
1690
+ } else if (phase === "knip") {
1691
+ if (
1692
+ md.execution === "executed" &&
1693
+ Number.isFinite(Number(md.totalIssues))
1694
+ ) {
1695
+ const rows = state.latency.knip.get(pid) ?? [];
1696
+ rows.push({
1697
+ ts: nowIso,
1698
+ ms,
1699
+ durationMs: Number(entry.durationMs ?? 0),
1700
+ totalIssues: Number(md.totalIssues),
1701
+ });
1702
+ state.latency.knip.set(pid, rows);
1703
+ }
1704
+ } else if (phase === "degradation_ledger") {
1705
+ const kind = md.kind ?? "unknown";
1706
+ const perPid = state.latency.degradationKinds.get(pid) ?? new Map();
1707
+ perPid.set(kind, Math.max(perPid.get(kind) ?? 0, Number(md.count ?? 0)));
1708
+ state.latency.degradationKinds.set(pid, perPid);
1709
+ if (kind === "hook-await-exceeded") {
1710
+ const budgetMs = Number(md.budgetMs);
1711
+ const elapsedMs = Number(md.elapsedMs);
1712
+ state.latency.hookAwaitExceeded.push({
1713
+ ts: nowIso,
1714
+ pid,
1715
+ hook: md.hook,
1716
+ label: md.label,
1717
+ budgetMs,
1718
+ elapsedMs,
1719
+ cause: md.cause,
1720
+ ratio: budgetMs > 0 ? elapsedMs / budgetMs : 0,
1721
+ });
1722
+ }
1723
+ } else if (phase === "lsp_client_wait_timeout") {
1724
+ const serverIds = Array.isArray(md.serverIds) ? md.serverIds : [];
1725
+ if (serverIds.length === 0) {
1726
+ const cur = state.latency.lspWaitEmpty.get(pid) ?? { ms: 0, rows: 0 };
1727
+ cur.ms += Number(entry.durationMs ?? 0);
1728
+ cur.rows += 1;
1729
+ state.latency.lspWaitEmpty.set(pid, cur);
1730
+ }
1731
+ } else if (phase === "lsp_touch_file") {
1732
+ const cur = state.latency.lspTouchEdit.get(pid) ?? {
1733
+ rows: 0,
1734
+ noClients: 0,
1735
+ };
1736
+ if (md.source === "tool_call:edit") {
1737
+ cur.rows += 1;
1738
+ if (md.failureKind === "no_clients_none_spawning") cur.noClients += 1;
1739
+ }
1740
+ state.latency.lspTouchEdit.set(pid, cur);
1741
+ if (
1742
+ md.source === "tool_call:edit" &&
1743
+ md.failureKind === "no_clients_none_spawning"
1744
+ ) {
1745
+ state.latency.lspTouchNoClients.push({
1746
+ ts: nowIso,
1747
+ ms,
1748
+ pid,
1749
+ filePath: entry.filePath,
1750
+ });
1751
+ }
1752
+ } else if (phase === "lsp_client_selected") {
1753
+ if (md.outcome === "warm-reuse") {
1754
+ state.latency.lspClientSelectedWarm.push({
1755
+ ts: nowIso,
1756
+ ms,
1757
+ pid,
1758
+ filePath: entry.filePath,
1759
+ });
1760
+ }
1761
+ } else if (phase === "read_guard_branch_retained") {
1762
+ state.latency.carryRestart.push({
1763
+ ts: nowIso,
1764
+ pid,
1765
+ trigger: md.trigger,
1766
+ source: md.source,
1767
+ kept: Number(md.kept ?? 0),
1768
+ dropped: Number(md.dropped ?? 0),
1769
+ branchToolResults: Number(md.branchToolResults ?? 0),
1770
+ branchReadable: md.branchReadable === true,
1771
+ });
1772
+ } else if (phase === "agent_nudge") {
1773
+ state.latency.agentNudges.push({
1774
+ ts: nowIso,
1775
+ ms,
1776
+ pid,
1777
+ originCrossProcess: Number(md.originCrossProcess ?? 0),
1778
+ originLocal: Number(md.originLocal ?? 0),
1779
+ reasonMix: md.reasonMix,
1780
+ });
1781
+ } else if (phase === "session_scope_transition") {
1782
+ state.latency.sessionScopeTransitions.push({
1783
+ ts: nowIso,
1784
+ ms,
1785
+ pid,
1786
+ handoffSource: md.handoffSource,
1787
+ sessionId: md.sessionId,
1788
+ });
1789
+ } else if (phase === "late_runner_findings") {
1790
+ state.latency.lateRunnerFindings.push({
1791
+ ts: nowIso,
1792
+ ms,
1793
+ pid,
1794
+ turnId: entry.turnId,
1795
+ failed: Number(md.failed ?? 0),
1796
+ delivered: Number(md.delivered ?? 0),
1797
+ dropped: Number(md.dropped ?? 0),
1798
+ pending: Number(md.pending ?? 0),
1799
+ });
1800
+ } else if (phase === "late_auxiliary_findings") {
1801
+ const pairs = Array.isArray(md.stuckPairs) ? md.stuckPairs : [];
1802
+ for (const pair of pairs) {
1803
+ if (!pair?.filePath || !pair?.serverId) continue;
1804
+ const key = `${pid}\u0000${pair.filePath}\u0000${pair.serverId}`;
1805
+ state.latency.auxStuck.set(
1806
+ key,
1807
+ (state.latency.auxStuck.get(key) ?? 0) + 1,
1808
+ );
1809
+ }
1810
+ } else if (phase === "advisory_provenance_decision") {
1811
+ const reasons = Array.isArray(md.reasons) ? md.reasons : [];
1812
+ // The malformed reason implies decision "historical": the validator
1813
+ // returns status "unknown" with it (clients/advisory-provenance.ts) and
1814
+ // the row logs every non-current status as historical
1815
+ // (clients/runtime-context.ts), so the reason alone is the rule.
1816
+ if (reasons.includes("malformed-or-legacy-provenance")) {
1817
+ state.latency.advisoryProvenance.push({
1818
+ ts: nowIso,
1819
+ ms,
1820
+ pid,
1821
+ reasons,
1822
+ provenanceStamp: md.provenanceStamp,
1823
+ advisoryKind: md.advisoryKind,
1824
+ });
1825
+ }
1826
+ }
1827
+ }
1828
+
1829
+ /** D2: extension.log rows that name a test home. */
1830
+ async function analyzeExtension(files, state) {
1831
+ for (const file of files) {
1832
+ await forEachJsonLine(file, "extension", state, (entry) => {
1833
+ const ts = dateOf(entry.ts);
1834
+ if (!inWindow(ts)) return;
1835
+ state.seen.inc("extension");
1836
+ if (SCRATCH_PATH_RE.test(JSON.stringify(entry))) {
1837
+ const pid = String(entry.pid ?? "unknown");
1838
+ state.extension.scratchRows.set(
1839
+ pid,
1840
+ (state.extension.scratchRows.get(pid) ?? 0) + 1,
1841
+ );
1842
+ }
1843
+ const level = String(entry.level ?? "").toLowerCase();
1844
+ if (level === "warn" || level === "error") {
1845
+ const message = String(entry.message ?? "").slice(0, 80);
1846
+ state.extension.warnErrorGroups.inc(
1847
+ `${entry.subsystem ?? "unknown"}: ${message}`,
1848
+ );
1849
+ }
1850
+ });
1851
+ }
1852
+ }
1853
+
1854
+ function countBetween(sorted, a, b) {
1855
+ let n = 0;
1856
+ for (const value of sorted) {
1857
+ if (value > a && value < b) n += 1;
1858
+ else if (value >= b) break;
1859
+ }
1860
+ return n;
1861
+ }
1862
+
1863
+ /** D1: latency gaps >= 10 min that a live session filled with >= 20 rows. */
1864
+ function computeCoverageGaps(state) {
1865
+ const lat = [...state.latency.rowTs].sort((a, b) => a - b);
1866
+ const ss = [...state.session.rowTs].sort((a, b) => a - b);
1867
+ const cas = [...state.latency.cascadeTs].sort((a, b) => a - b);
1868
+ const gaps = [];
1869
+ for (let i = 1; i < lat.length; i += 1) {
1870
+ const a = lat[i - 1];
1871
+ const b = lat[i];
1872
+ const minutes = (b - a) / 60000;
1873
+ if (minutes < 10) continue;
1874
+ const sessionstartRows = countBetween(ss, a, b);
1875
+ if (sessionstartRows < 20) continue;
1876
+ gaps.push({
1877
+ start: new Date(a).toISOString(),
1878
+ end: new Date(b).toISOString(),
1879
+ minutes: Math.round(minutes),
1880
+ sessionstartRows,
1881
+ cascadeRows: countBetween(cas, a, b),
1882
+ });
1883
+ }
1884
+ return gaps;
1885
+ }
1886
+
1887
+ /** D5: per-session firing/stale text and per-session delivery outcomes. */
1888
+ function computeTestRunnerHealth(state) {
1889
+ const runs = state.session.runs.map((r) => ({
1890
+ startTs: r.startTs,
1891
+ firings: r.testFirings,
1892
+ stale: r.testStale,
1893
+ }));
1894
+ const staleRuns = runs.filter(
1895
+ (r) => r.firings >= 10 && r.stale / r.firings >= 0.5,
1896
+ );
1897
+ const firings = runs.reduce((n, r) => n + r.firings, 0);
1898
+ const stale = runs.reduce((n, r) => n + r.stale, 0);
1899
+ const sessions = [];
1900
+ for (const [sessionId, c] of state.latency.testRunnerDelivery) {
1901
+ const total = c.entries().reduce((n, [, v]) => n + v, 0);
1902
+ const delivered = c.get("delivered");
1903
+ sessions.push({
1904
+ sessionId,
1905
+ total,
1906
+ staged: c.get("staged"),
1907
+ eligible: c.get("eligible"),
1908
+ delivered,
1909
+ superseded: c.get("superseded"),
1910
+ deliveredShare: total ? delivered / total : 0,
1911
+ });
1912
+ }
1913
+ const lowDelivery = sessions.filter(
1914
+ (s) => s.total >= 10 && s.deliveredShare < 0.25,
1915
+ );
1916
+ return {
1917
+ firings,
1918
+ stale,
1919
+ staleShare: firings ? stale / firings : 0,
1920
+ staleRuns,
1921
+ sessions,
1922
+ lowDelivery,
1923
+ };
1924
+ }
1925
+
1926
+ /** D6: scanner issue-count drift and turn-end knip wall cost, per pid. */
1927
+ function computeKnip(state) {
1928
+ const drift = [];
1929
+ const cost = [];
1930
+ for (const [pid, rowsRaw] of state.latency.knip) {
1931
+ const rows = [...rowsRaw]
1932
+ .filter((r) => r.ms != null)
1933
+ .sort((a, b) => a.ms - b.ms);
1934
+ if (rows.length >= 10) {
1935
+ const issues = rows.map((r) => r.totalIssues);
1936
+ let increments = 0;
1937
+ for (let i = 1; i < issues.length; i += 1)
1938
+ if (issues[i] - issues[i - 1] >= 100) increments += 1;
1939
+ const min = Math.min(...issues);
1940
+ const max = Math.max(...issues);
1941
+ const spread = min > 0 ? (max - min) / min : 0;
1942
+ if (increments >= 5 || spread >= 0.25)
1943
+ drift.push({ pid, rows: rows.length, increments, min, max, spread });
1944
+ }
1945
+ const first = state.latency.pidFirstTs.get(pid);
1946
+ const last = state.latency.pidLastTs.get(pid);
1947
+ const hours = first != null && last != null ? (last - first) / 3600000 : 0;
1948
+ const totalMs = rows.reduce((n, r) => n + r.durationMs, 0);
1949
+ const maxRow = rows.reduce((n, r) => Math.max(n, r.durationMs), 0);
1950
+ // A lifetime under an hour counts as one hour, so a short pid's few
1951
+ // seconds of knip cannot extrapolate to a large hourly rate.
1952
+ const perHour = totalMs / Math.max(hours, 1);
1953
+ if (perHour >= 30000 || maxRow >= 5000)
1954
+ cost.push({ pid, rows: rows.length, totalMs, hours, perHour, maxRow });
1955
+ }
1956
+ return { drift, cost };
1957
+ }
1958
+
1959
+ /** D7: hook deopts that overran their budget, plus the degradation census. */
1960
+ function computeHookAwait(state) {
1961
+ const rows = [...state.latency.hookAwaitExceeded].sort((a, b) =>
1962
+ String(b.ts).localeCompare(String(a.ts)),
1963
+ );
1964
+ const census = [];
1965
+ for (const [pid, kinds] of state.latency.degradationKinds) {
1966
+ census.push({
1967
+ pid,
1968
+ kinds: [...kinds.entries()]
1969
+ .sort((a, b) => b[1] - a[1])
1970
+ .slice(0, 12)
1971
+ .map(([kind, count]) => `${kind}=${count}`),
1972
+ });
1973
+ }
1974
+ return { rows, over: rows.filter((r) => r.ratio >= 1.1).length, census };
1975
+ }
1976
+
1977
+ /** D8: turn_end hook duration per pid, and the retained-turn-state runs. */
1978
+ function computeTurnEndSlow(state) {
1979
+ const flagged = [];
1980
+ for (const [pid, rows] of state.latency.turnEndTools) {
1981
+ const slow = rows.filter((r) => r.durationMs > 3000).length;
1982
+ const max = rows.reduce((n, r) => Math.max(n, r.durationMs), 0);
1983
+ const share = rows.length ? slow / rows.length : 0;
1984
+ if ((rows.length >= 20 && share >= 0.1) || max >= 8000)
1985
+ flagged.push({ pid, rows: rows.length, slow, share, max });
1986
+ }
1987
+ const retainRuns = state.session.runs
1988
+ .filter((r) => r.retainingNewerTurn >= 5)
1989
+ .map((r) => ({ start: r.startTs, count: r.retainingNewerTurn }));
1990
+ return { flagged, retainRuns };
1991
+ }
1992
+
1993
+ /** D9: empty-candidate waits, edit touches with no client, warm-reuse clashes. */
1994
+ function computeLspWait(state) {
1995
+ const empty = [];
1996
+ for (const [pid, cur] of state.latency.lspWaitEmpty)
1997
+ if (cur.ms >= 5000) empty.push({ pid, ms: cur.ms, rows: cur.rows });
1998
+ const noClients = [];
1999
+ for (const [pid, cur] of state.latency.lspTouchEdit)
2000
+ if (cur.rows >= 20 && cur.noClients / cur.rows >= 0.2)
2001
+ noClients.push({
2002
+ pid,
2003
+ rows: cur.rows,
2004
+ noClients: cur.noClients,
2005
+ share: cur.noClients / cur.rows,
2006
+ });
2007
+ const contradictions = [];
2008
+ for (const touch of state.latency.lspTouchNoClients) {
2009
+ const match = state.latency.lspClientSelectedWarm.find(
2010
+ (w) =>
2011
+ w.pid === touch.pid &&
2012
+ w.filePath === touch.filePath &&
2013
+ touch.ms != null &&
2014
+ w.ms != null &&
2015
+ Math.abs(w.ms - touch.ms) <= 500,
2016
+ );
2017
+ if (match)
2018
+ contradictions.push({
2019
+ pid: touch.pid,
2020
+ filePath: touch.filePath,
2021
+ touchTs: touch.ts,
2022
+ selectedTs: match.ts,
2023
+ });
2024
+ }
2025
+ const pids = new Set([
2026
+ ...empty.map((e) => e.pid),
2027
+ ...noClients.map((e) => e.pid),
2028
+ ...contradictions.map((c) => c.pid),
2029
+ ]);
2030
+ return { empty, noClients, contradictions, pidCount: pids.size };
2031
+ }
2032
+
2033
+ /** D11: a restart kept nothing while the branch held a populated read set. */
2034
+ function computeCarryRestart(state) {
2035
+ return state.latency.carryRestart.filter(
2036
+ (r) =>
2037
+ ["startup", "resume", "fork", "reload"].includes(r.trigger) &&
2038
+ r.source !== "none" &&
2039
+ r.kept + r.dropped === 0 &&
2040
+ r.branchToolResults >= 50 &&
2041
+ r.branchReadable,
2042
+ );
2043
+ }
2044
+
2045
+ /** D12: a cross-process nudge right after a sidecar handoff (suspect grade). */
2046
+ function computeRestartSelfNudge(state) {
2047
+ const flags = [];
2048
+ for (const nudge of state.latency.agentNudges) {
2049
+ if (nudge.originCrossProcess < 1) continue;
2050
+ const transition = state.latency.sessionScopeTransitions.find(
2051
+ (t) =>
2052
+ t.pid === nudge.pid &&
2053
+ ["own-sidecar", "parent-sidecar"].includes(t.handoffSource) &&
2054
+ nudge.ms != null &&
2055
+ t.ms != null &&
2056
+ nudge.ms >= t.ms &&
2057
+ nudge.ms - t.ms <= 300000,
2058
+ );
2059
+ if (transition) flags.push({ ...nudge, transitionTs: transition.ts });
2060
+ }
2061
+ return flags;
2062
+ }
2063
+
2064
+ /** D13: a collect-later runner failed with findings and nothing was delivered. */
2065
+ function computeDeferredRunner(state) {
2066
+ const flags = [];
2067
+ for (const runner of state.latency.deferredRunnerFailed) {
2068
+ const delivery = state.latency.lateRunnerFindings.find(
2069
+ (f) =>
2070
+ f.pid === runner.pid &&
2071
+ f.turnId === runner.turnId &&
2072
+ (runner.ms == null || f.ms == null || f.ms >= runner.ms),
2073
+ );
2074
+ if (
2075
+ delivery &&
2076
+ delivery.failed >= 1 &&
2077
+ delivery.delivered === 0 &&
2078
+ delivery.dropped === 0
2079
+ )
2080
+ flags.push({
2081
+ ...runner,
2082
+ deliveryTs: delivery.ts,
2083
+ pending: delivery.pending,
2084
+ });
2085
+ }
2086
+ return flags;
2087
+ }
2088
+
2089
+ /** D14: an auxiliary (file, server) pair stuck in two or more turn ends. */
2090
+ function computeAuxStuck(state) {
2091
+ const flagged = [];
2092
+ for (const [key, count] of state.latency.auxStuck) {
2093
+ if (count < 2) continue;
2094
+ const [pid, filePath, serverId] = key.split("\u0000");
2095
+ flagged.push({ pid, filePath, serverId, count });
2096
+ }
2097
+ flagged.sort((a, b) => b.count - a.count);
2098
+ return flagged;
2099
+ }
2100
+
2101
+ /** D15: `historical` provenance with a malformed/legacy stamp. */
2102
+ function computeAdvisoryProvenance(state) {
2103
+ return state.latency.advisoryProvenance.map((row) => {
2104
+ const first = state.latency.pidFirstTs.get(row.pid);
2105
+ const seconds =
2106
+ first != null && row.ms != null
2107
+ ? Math.round((row.ms - first) / 1000)
2108
+ : null;
2109
+ return { ...row, secondsSinceFirstRow: seconds };
2110
+ });
2111
+ }
2112
+
2113
+ /** D16: `pi-lens loaded: Nms` loads over 2 s. A load with no
2114
+ * `session_start fired` within the next 60 s is a short-lived pid. */
2115
+ function computeSlowExtensionLoad(state) {
2116
+ const fired = state.session.firedTs;
2117
+ return state.session.slowLoads
2118
+ .filter((l) => l.durationMs >= 2000)
2119
+ .map((l) => ({
2120
+ ...l,
2121
+ shortLived: !(
2122
+ Number.isFinite(l.ms) &&
2123
+ fired.some((f) => f >= l.ms && f - l.ms <= 60000)
2124
+ ),
2125
+ }));
2126
+ }
2127
+
1298
2128
  function buildReport(state) {
1299
2129
  const smells = [];
1300
2130
  const smellCount = (id) => state.smellTotals.get(id);
2131
+
2132
+ // D1-D16 detector computations. Kept out of the smell list so every value
2133
+ // they produce is assertable on its own, not only through a smell count.
2134
+ const coverageGaps = computeCoverageGaps(state);
2135
+ const testRunnerHealth = computeTestRunnerHealth(state);
2136
+ const knip = computeKnip(state);
2137
+ const hookAwait = computeHookAwait(state);
2138
+ const turnEndSlow = computeTurnEndSlow(state);
2139
+ const lspWait = computeLspWait(state);
2140
+ const carryRestart = computeCarryRestart(state);
2141
+ const restartSelfNudge = computeRestartSelfNudge(state);
2142
+ const deferredRunner = computeDeferredRunner(state);
2143
+ const auxStuck = computeAuxStuck(state);
2144
+ const advisoryProvenance = computeAdvisoryProvenance(state);
2145
+ const slowExtensionLoad = computeSlowExtensionLoad(state);
2146
+ const latencyScratch = [...state.latency.scratchRows.values()].reduce(
2147
+ (n, v) => n + v,
2148
+ 0,
2149
+ );
2150
+ const extensionScratch = [...state.extension.scratchRows.values()].reduce(
2151
+ (n, v) => n + v,
2152
+ 0,
2153
+ );
2154
+ const d3Runs = state.session.runs.filter((r) => {
2155
+ const excludedTest = r.excludedTestFiles.some((p) =>
2156
+ /\.(test|spec)\.|\/tests?\//.test(p),
2157
+ );
2158
+ return (r.excluded >= 1 && excludedTest) || (r.edits >= 10 && r.ran === 0);
2159
+ });
2160
+ const d4Cross = state.session.runs.reduce(
2161
+ (n, r) => n + (r.crossCheckout >= 3 ? r.crossCheckout : 0),
2162
+ 0,
2163
+ );
2164
+ const testRunnerSmellCount =
2165
+ testRunnerHealth.staleRuns.length + testRunnerHealth.lowDelivery.length;
1301
2166
  addSmell(
1302
2167
  smells,
1303
2168
  "diagnostic-blockers",
@@ -1407,19 +2272,17 @@ function buildReport(state) {
1407
2272
  );
1408
2273
  addSmell(
1409
2274
  smells,
1410
- "read-guard-friction",
1411
- smellCount("read-guard-friction"),
1412
- "Read-guard blocked/warned edits or exact replacement misses",
1413
- [...state.readGuard.blocked, ...state.readGuard.oldTextIssues].slice(
1414
- 0,
1415
- limit,
1416
- ),
2275
+ "read-guard-blocks",
2276
+ state.readGuard.events.get("edit_blocked") +
2277
+ state.readGuard.events.get("edit_preflight_blocked"),
2278
+ "Read-guard blocked edits; host/model provenance is not present on the block rows, and warns and exact-replacement misses are informational",
2279
+ state.readGuard.blocks.slice(0, limit),
1417
2280
  );
1418
2281
  addSmell(
1419
2282
  smells,
1420
2283
  "read-guard-stale-ranges",
1421
2284
  smellCount("read-guard-stale-ranges"),
1422
- "Covered edit ranges whose read snapshot no longer matched current content",
2285
+ `Enforced range mismatches whose read snapshot no longer matched (bypassed-content-match ${state.readGuard.bypassedMismatch} excluded as an allowed edit)`,
1423
2286
  state.readGuard.staleRanges.slice(0, limit),
1424
2287
  );
1425
2288
  addSmell(
@@ -1479,6 +2342,258 @@ function buildReport(state) {
1479
2342
  ws.sweeps.slice(0, limit),
1480
2343
  );
1481
2344
 
2345
+ // D1: a latency gap a live session filled.
2346
+ addSmell(
2347
+ smells,
2348
+ "log-coverage-gap",
2349
+ coverageGaps.length,
2350
+ "Latency stream gap >= 10 min that a live session filled with >= 20 sessionstart rows",
2351
+ coverageGaps.slice(0, limit).map((g) => ({
2352
+ ts: g.start,
2353
+ message: `${g.minutes} min gap ${g.start} -> ${g.end}; sessionstartRows=${g.sessionstartRows}; cascadeRows=${g.cascadeRows}`,
2354
+ })),
2355
+ );
2356
+ // D2: test home markers in a real log. Never added to the
2357
+ // default denylist so the pollution stays visible.
2358
+ addSmell(
2359
+ smells,
2360
+ "real-log-test-pollution",
2361
+ latencyScratch + extensionScratch,
2362
+ `Rows naming a test home (witness-home, pi-lens-test-*) (latency ${latencyScratch}, extension ${extensionScratch}); these never belong in a real log`,
2363
+ [
2364
+ ...[...state.latency.scratchRows.entries()].map(([pid, count]) => ({
2365
+ key: `latency pid ${pid}`,
2366
+ count,
2367
+ })),
2368
+ ...[...state.extension.scratchRows.entries()].map(([pid, count]) => ({
2369
+ key: `extension pid ${pid}`,
2370
+ count,
2371
+ })),
2372
+ ].slice(0, limit),
2373
+ );
2374
+ addSmell(
2375
+ smells,
2376
+ "extension-warn-errors",
2377
+ state.extension.warnErrorGroups.size,
2378
+ "extension.log warn/error groups by subsystem and message prefix",
2379
+ state.extension.warnErrorGroups.top(limit),
2380
+ );
2381
+ // D3: a run that excluded its own test files, or edited without running one.
2382
+ addSmell(
2383
+ smells,
2384
+ "turn-end-tests-excluded",
2385
+ d3Runs.length,
2386
+ "Turn-end runs that skipped test files by policy, or edited >= 10 files and ran no test",
2387
+ d3Runs.slice(0, limit).map((r) => ({
2388
+ ts: r.startTs,
2389
+ message: `edits=${r.edits} ran=${r.ran} excluded=${r.excluded} noTestFile=${r.noTestFile}${
2390
+ r.excludedTestFiles.length ? `: ${r.excludedTestFiles.join(", ")}` : ""
2391
+ }`,
2392
+ })),
2393
+ );
2394
+ // D4: a failed-first target that resolved to a different checkout (#3649).
2395
+ addSmell(
2396
+ smells,
2397
+ "test-target-cross-checkout",
2398
+ d4Cross,
2399
+ "Failed-first test targets resolved to a different checkout than the edited source (regression guard for #3649)",
2400
+ state.session.runs
2401
+ .filter((r) => r.crossCheckout >= 3)
2402
+ .slice(0, limit)
2403
+ .map((r) => ({
2404
+ ts: r.startTs,
2405
+ message: `${r.crossCheckout} cross-checkout of ${r.failedFirst} failed-first`,
2406
+ })),
2407
+ );
2408
+ // D5: sessions whose turn-end verdicts went stale, or were rarely delivered.
2409
+ addSmell(
2410
+ smells,
2411
+ "test-runner-stale-verdicts",
2412
+ testRunnerSmellCount,
2413
+ `Sessions with >= 10 test firings and >= 50% stale (${testRunnerHealth.staleRuns.length}), or >= 10 delivery rows and < 25% delivered (${testRunnerHealth.lowDelivery.length}); window firings ${testRunnerHealth.firings}, stale ${testRunnerHealth.stale}`,
2414
+ [
2415
+ ...testRunnerHealth.staleRuns.map((r) => ({
2416
+ ts: r.startTs,
2417
+ count: r.stale,
2418
+ message: `firings=${r.firings} stale=${r.stale}`,
2419
+ })),
2420
+ ...testRunnerHealth.lowDelivery.map((s) => ({
2421
+ key: s.sessionId,
2422
+ count: s.delivered,
2423
+ message: `delivered ${s.delivered}/${s.total} (${Math.round(
2424
+ s.deliveredShare * 100,
2425
+ )}%), staged ${s.staged}, eligible ${s.eligible}, superseded ${s.superseded}`,
2426
+ })),
2427
+ ].slice(0, limit),
2428
+ );
2429
+ // D6: scanner-count drift and knip wall cost.
2430
+ addSmell(
2431
+ smells,
2432
+ "scanner-count-drift",
2433
+ knip.drift.length,
2434
+ "knip totalIssues rose by >= 100 between consecutive executed runs at least 5 times, or its spread >= 25% (pids with >= 10 executed runs)",
2435
+ knip.drift.slice(0, limit).map((d) => ({
2436
+ key: `pid ${d.pid}`,
2437
+ count: d.increments,
2438
+ message: `issues ${d.min} -> ${d.max} (+${Math.round(
2439
+ d.spread * 100,
2440
+ )}%), ${d.increments} increments >= 100`,
2441
+ })),
2442
+ );
2443
+ addSmell(
2444
+ smells,
2445
+ "turn-end-knip-cost",
2446
+ knip.cost.length,
2447
+ "knip consumed >= 30 s per hour of pid lifetime (a lifetime under an hour counts as one hour), or a single run took >= 5 s",
2448
+ knip.cost.slice(0, limit).map((c) => ({
2449
+ key: `pid ${c.pid}`,
2450
+ count: Math.round(c.perHour),
2451
+ message: `${c.totalMs}ms over ${c.hours.toFixed(2)}h = ${Math.round(
2452
+ c.perHour,
2453
+ )}ms/h, max ${c.maxRow}ms`,
2454
+ })),
2455
+ );
2456
+ // D7: hook deopts that overran their budget.
2457
+ addSmell(
2458
+ smells,
2459
+ "hook-await-exceeded",
2460
+ hookAwait.rows.length,
2461
+ `Hook awaits that overran their budget (${hookAwait.over} with ratio >= 1.1)`,
2462
+ hookAwait.rows.slice(0, limit).map((r) => ({
2463
+ ts: r.ts,
2464
+ message: `${r.hook}:${r.label} ${r.elapsedMs}/${r.budgetMs}ms (${r.ratio.toFixed(
2465
+ 3,
2466
+ )}) pid=${r.pid} cause=${r.cause}`,
2467
+ })),
2468
+ );
2469
+ // D8: turn_end hook duration and retained-state churn.
2470
+ addSmell(
2471
+ smells,
2472
+ "turn-end-slow",
2473
+ turnEndSlow.flagged.length,
2474
+ "turn_end hook duration over budget (a pid with >= 10% of summaries > 3 s, or one >= 8 s)",
2475
+ turnEndSlow.flagged.slice(0, limit).map((f) => ({
2476
+ key: `pid ${f.pid}`,
2477
+ count: f.slow,
2478
+ message: `${f.slow}/${f.rows} over 3s (${Math.round(
2479
+ f.share * 100,
2480
+ )}%), max ${f.max}ms`,
2481
+ })),
2482
+ );
2483
+ addSmell(
2484
+ smells,
2485
+ "turn-end-retained-state",
2486
+ turnEndSlow.retainRuns.length,
2487
+ "Turn-end retained newer turn state at least 5 times in one session",
2488
+ turnEndSlow.retainRuns.slice(0, limit).map((r) => ({
2489
+ ts: r.start,
2490
+ count: r.count,
2491
+ })),
2492
+ );
2493
+ // D9: empty-candidate waits and no-client/edit warm-reuse contradictions.
2494
+ addSmell(
2495
+ smells,
2496
+ "lsp-wait-empty-candidates",
2497
+ lspWait.pidCount,
2498
+ `LSP touch saw no client while a warm client was selected, or waited with an empty candidate set (empty ${lspWait.empty.length}, noClients ${lspWait.noClients.length}, contradictions ${lspWait.contradictions.length})`,
2499
+ [
2500
+ ...lspWait.empty.map((e) => ({
2501
+ key: `pid ${e.pid}`,
2502
+ count: e.rows,
2503
+ message: `${e.ms}ms waiting with no candidate server`,
2504
+ })),
2505
+ ...lspWait.noClients.map((e) => ({
2506
+ key: `pid ${e.pid}`,
2507
+ count: Math.round(e.share * 100),
2508
+ message: `${e.noClients}/${e.rows} edits saw no client (${Math.round(
2509
+ e.share * 100,
2510
+ )}%)`,
2511
+ })),
2512
+ ...lspWait.contradictions.map((c) => ({
2513
+ key: `pid ${c.pid}`,
2514
+ count: 1,
2515
+ message: `${c.filePath}: no-client touch at ${c.touchTs}, warm-reuse at ${c.selectedTs}`,
2516
+ })),
2517
+ ].slice(0, limit),
2518
+ );
2519
+ // D10: a zero_read block that contradicts earlier read/edit evidence.
2520
+ addSmell(
2521
+ smells,
2522
+ "resume-state-loss",
2523
+ state.readGuard.stateLost.length,
2524
+ `zero_read edits blocked despite earlier read/edit evidence in the same session (genuine ${state.readGuard.genuineZeroRead.length})`,
2525
+ state.readGuard.stateLost.slice(0, limit),
2526
+ );
2527
+ // D11: a restart whose read-set carry was empty while the branch was populated.
2528
+ addSmell(
2529
+ smells,
2530
+ "carry-empty-restart",
2531
+ carryRestart.length,
2532
+ "Restart carry kept and dropped nothing while the branch held a populated read set",
2533
+ carryRestart.slice(0, limit).map((r) => ({
2534
+ key: `pid ${r.pid}`,
2535
+ count: r.branchToolResults,
2536
+ message: `trigger=${r.trigger} source=${r.source} kept=${r.kept} dropped=${r.dropped} branchToolResults=${r.branchToolResults}`,
2537
+ })),
2538
+ );
2539
+ // D12: a cross-process nudge right after a sidecar handoff (suspect until O7).
2540
+ addSmell(
2541
+ smells,
2542
+ "restart-self-nudge",
2543
+ restartSelfNudge.length,
2544
+ "Cross-process nudge within 300 s of a sidecar handoff (suspect-grade until the nudge carries a file id, O7)",
2545
+ restartSelfNudge.slice(0, limit).map((r) => ({
2546
+ key: `pid ${r.pid}`,
2547
+ count: r.originCrossProcess,
2548
+ message: `${JSON.stringify(r.reasonMix)} crossProcess=${r.originCrossProcess} after ${r.transitionTs}`,
2549
+ })),
2550
+ );
2551
+ // D13: a collect-later runner failed with findings and none were delivered.
2552
+ addSmell(
2553
+ smells,
2554
+ "deferred-runner-failed-undelivered",
2555
+ deferredRunner.length,
2556
+ "collect-later runner failed with findings and the delivery row shows failed>=1, delivered=0, dropped=0 (#3796 witness)",
2557
+ deferredRunner.slice(0, limit).map((r) => ({
2558
+ ts: r.ts,
2559
+ message: `${r.runnerId} pid=${r.pid} turnId=${r.turnId} diagnostics=${r.diagnosticCount} delivery=${r.deliveryTs}`,
2560
+ })),
2561
+ );
2562
+ // D14: an auxiliary (file, server) pair stuck in >= 2 turn ends.
2563
+ addSmell(
2564
+ smells,
2565
+ "aux-stuck-pair",
2566
+ auxStuck.length,
2567
+ "Auxiliary (file, server) pair stuck in >= 2 turn ends",
2568
+ auxStuck.slice(0, limit).map((p) => ({
2569
+ key: p.serverId,
2570
+ count: p.count,
2571
+ message: `${p.filePath} pid=${p.pid}`,
2572
+ })),
2573
+ );
2574
+ // D15: historical provenance with a malformed/legacy stamp.
2575
+ addSmell(
2576
+ smells,
2577
+ "advisory-provenance-unknown",
2578
+ advisoryProvenance.length,
2579
+ "Advisory provenance resolved to historical with a malformed-or-legacy stamp",
2580
+ advisoryProvenance.slice(0, limit).map((r) => ({
2581
+ ts: r.ts,
2582
+ message: `${r.provenanceStamp} (${r.advisoryKind}) ${r.secondsSinceFirstRow}s after pid start`,
2583
+ })),
2584
+ );
2585
+ // D16: extension load time over 2 s.
2586
+ addSmell(
2587
+ smells,
2588
+ "slow-extension-load",
2589
+ slowExtensionLoad.length,
2590
+ "pi-lens loaded >= 2000ms after process start (short-lived: no session_start fired within 60 s)",
2591
+ slowExtensionLoad.slice(0, limit).map((l) => ({
2592
+ ...l,
2593
+ message: `${l.message}${l.shortLived ? " [short-lived]" : ""}`,
2594
+ })),
2595
+ );
2596
+
1482
2597
  return {
1483
2598
  window: state.window,
1484
2599
  filesScanned: Object.fromEntries(
@@ -1549,6 +2664,12 @@ function buildReport(state) {
1549
2664
  0,
1550
2665
  limit,
1551
2666
  ),
2667
+ // E2/D10: the block/warn split and the lost-read-set classification.
2668
+ warns: state.readGuard.warns.slice(0, limit),
2669
+ blockByKind: state.readGuard.blockByKind.toJSON(),
2670
+ bypassedMismatch: state.readGuard.bypassedMismatch,
2671
+ stateLost: state.readGuard.stateLost.slice(0, limit),
2672
+ genuineZeroRead: state.readGuard.genuineZeroRead.slice(0, limit),
1552
2673
  },
1553
2674
  treeSitter: {
1554
2675
  phases: state.treeSitter.phases.toJSON(),
@@ -1559,7 +2680,8 @@ function buildReport(state) {
1559
2680
  session: {
1560
2681
  starts: state.session.starts,
1561
2682
  cwds: state.session.cwds.top(limit),
1562
- rotations: state.session.rotations.toJSON(),
2683
+ commits: state.session.commits.toJSON(),
2684
+ slowLoads: state.session.slowLoads.slice(0, limit),
1563
2685
  errors: state.session.errors.slice(0, limit),
1564
2686
  },
1565
2687
  // #2526: the positive-observability counters behind the
@@ -1602,6 +2724,43 @@ function buildReport(state) {
1602
2724
  .sort((a, b) => b.total - a.total)
1603
2725
  .slice(0, limit * 3),
1604
2726
  },
2727
+ // D1-D16: the per-detector values behind the smells above, so a test can
2728
+ // pin the rule (not only the rendered count) and a reader can see WHY.
2729
+ detectors: {
2730
+ logCoverageGap: { gaps: coverageGaps },
2731
+ realLogTestPollution: {
2732
+ latencyByPid: Object.fromEntries(state.latency.scratchRows),
2733
+ extensionByPid: Object.fromEntries(state.extension.scratchRows),
2734
+ latencyTotal: latencyScratch,
2735
+ extensionTotal: extensionScratch,
2736
+ },
2737
+ turnEndTestsExcluded: {
2738
+ runs: d3Runs.map((r) => ({ ...r })),
2739
+ },
2740
+ testTargetCrossCheckout: {
2741
+ runs: state.session.runs.map((r) => ({
2742
+ startTs: r.startTs,
2743
+ crossCheckout: r.crossCheckout,
2744
+ failedFirst: r.failedFirst,
2745
+ })),
2746
+ },
2747
+ testRunnerStaleVerdicts: testRunnerHealth,
2748
+ knip,
2749
+ hookAwait,
2750
+ turnEndSlow,
2751
+ lspWait,
2752
+ resumeStateLoss: {
2753
+ stateLost: state.readGuard.stateLost,
2754
+ genuine: state.readGuard.genuineZeroRead,
2755
+ },
2756
+ carryEmptyRestart: carryRestart,
2757
+ restartSelfNudge,
2758
+ deferredRunnerFailedUndelivered: deferredRunner,
2759
+ auxStuckPairs: auxStuck,
2760
+ advisoryProvenanceUnknown: advisoryProvenance,
2761
+ slowExtensionLoad,
2762
+ extensionWarnErrors: state.extension.warnErrorGroups.toJSON(),
2763
+ },
1605
2764
  };
1606
2765
  }
1607
2766
 
@@ -1649,6 +2808,60 @@ function printReport(report) {
1649
2808
  }
1650
2809
  }
1651
2810
 
2811
+ // D1/D2/D5/D6/D7: the non-smell breakdown behind the new detectors.
2812
+ const det = report.detectors ?? {};
2813
+ const gap = det.logCoverageGap;
2814
+ if (gap?.gaps?.length) {
2815
+ console.log("\nLog coverage");
2816
+ for (const g of gap.gaps ?? [])
2817
+ console.log(
2818
+ ` gap ${g.minutes}min ${g.start} → ${g.end} sessionstart=${g.sessionstartRows} cascade=${g.cascadeRows}`,
2819
+ );
2820
+ }
2821
+ const warnErrors = Object.entries(det.extensionWarnErrors ?? {});
2822
+ if (warnErrors.length) {
2823
+ console.log("\nExtension diagnostics");
2824
+ for (const [k, v] of warnErrors.slice(0, limit))
2825
+ console.log(` ${String(v).padStart(5)} ${k}`);
2826
+ }
2827
+ const deg = det.hookAwait?.census ?? [];
2828
+ if (deg.length) {
2829
+ console.log("\nDegradation ledger census (max count per pid)");
2830
+ for (const row of deg.slice(0, limit))
2831
+ console.log(` pid ${row.pid}: ${row.kinds.join(", ")}`);
2832
+ }
2833
+ const delivery = det.testRunnerStaleVerdicts;
2834
+ if (delivery) {
2835
+ console.log("\nTest-runner delivery");
2836
+ console.log(
2837
+ ` firings=${delivery.firings} stale=${delivery.stale} (${(
2838
+ delivery.staleShare * 100
2839
+ ).toFixed(1)}%)`,
2840
+ );
2841
+ for (const s of delivery.sessions ?? [])
2842
+ console.log(
2843
+ ` ${s.sessionId}: delivered ${s.delivered}/${s.total} staged=${s.staged} eligible=${s.eligible} superseded=${s.superseded}`,
2844
+ );
2845
+ }
2846
+ const knip = det.knip;
2847
+ if (knip && (knip.drift.length || knip.cost.length)) {
2848
+ console.log("\nknip drift and cost");
2849
+ for (const d of knip.drift)
2850
+ console.log(
2851
+ ` pid ${d.pid}: issues ${d.min} → ${d.max} (${d.increments} increments >= 100)`,
2852
+ );
2853
+ for (const c of knip.cost)
2854
+ console.log(
2855
+ ` pid ${c.pid}: ${Math.round(c.perHour)}ms/h, max ${c.maxRow}ms`,
2856
+ );
2857
+ }
2858
+ const extLoads = det.slowExtensionLoad ?? [];
2859
+ if (extLoads.length) {
2860
+ const shortLived = extLoads.filter((l) => l.shortLived).length;
2861
+ console.log("\nExtension loads");
2862
+ console.log(` slow=${extLoads.length} shortLived=${shortLived}`);
2863
+ }
2864
+
1652
2865
  section(
1653
2866
  "Diagnostic rules",
1654
2867
  report.diagnostics.byRule,
@@ -1705,7 +2918,7 @@ function printReport(report) {
1705
2918
  report.latency.testRunnerVerdicts ?? {},
1706
2919
  );
1707
2920
  if (verdictSessions.length) {
1708
- console.log("\nTest-runner stale verdicts per session");
2921
+ console.log("\nTest-runner stale verdicts per session (verdict rows only)");
1709
2922
  for (const [sessionId, summary] of verdictSessions) {
1710
2923
  console.log(
1711
2924
  ` ${sessionId}: ${summary.stale}/${summary.total} stale (${(summary.rate * 100).toFixed(1)}%)`,