pi-lens 4.3.0 → 4.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (659) hide show
  1. package/CHANGELOG.md +1159 -0
  2. package/README.md +116 -25
  3. package/config/dependency-cruiser-eager-allowlist.json +6 -2
  4. package/dist/chunk-2BEMIWEW.js +51 -0
  5. package/dist/chunk-2EECBNC5.js +89 -0
  6. package/dist/chunk-2FQ62SPM.js +83 -0
  7. package/dist/chunk-2UMYMRI3.js +118 -0
  8. package/dist/chunk-34OCDWOS.js +48 -0
  9. package/dist/chunk-3FERWMCH.js +144 -0
  10. package/dist/chunk-3JL6IFD7.js +97 -0
  11. package/dist/chunk-3JSEWUWI.js +327 -0
  12. package/dist/chunk-3MED75HT.js +37 -0
  13. package/dist/chunk-3MRXIJ6I.js +79 -0
  14. package/dist/chunk-3P4PF2BX.js +2250 -0
  15. package/dist/chunk-3PNFWVHT.js +201 -0
  16. package/dist/chunk-3TTPKMEF.js +162 -0
  17. package/dist/chunk-5JHXUETW.js +326 -0
  18. package/dist/chunk-5W3R6LFT.js +24 -0
  19. package/dist/chunk-64LU2OVH.js +341 -0
  20. package/dist/chunk-6B5I6ICN.js +44 -0
  21. package/dist/chunk-6P54TMWL.js +274 -0
  22. package/dist/chunk-6UYA642J.js +145 -0
  23. package/dist/chunk-6XX5HCLK.js +398 -0
  24. package/dist/chunk-7AXO3ZDF.js +30 -0
  25. package/dist/chunk-7BX5DWSA.js +1907 -0
  26. package/dist/chunk-7ELY2CSM.js +902 -0
  27. package/dist/chunk-7M4T5N3V.js +38 -0
  28. package/dist/chunk-7U2IDNXN.js +37 -0
  29. package/dist/chunk-ABBOA7UD.js +157 -0
  30. package/dist/chunk-AFZYMAIV.js +756 -0
  31. package/dist/chunk-ALQRYH6O.js +112 -0
  32. package/dist/chunk-AXKPQ6ZI.js +1610 -0
  33. package/dist/chunk-BHWHXMQ2.js +62 -0
  34. package/dist/chunk-BJF25SUG.js +22 -0
  35. package/dist/chunk-BNKLYOOO.js +18 -0
  36. package/dist/chunk-BW5KFIQC.js +59 -0
  37. package/dist/chunk-CBD2B5CP.js +599 -0
  38. package/dist/chunk-CFYISHUW.js +42 -0
  39. package/dist/chunk-CMU4RMJB.js +30 -0
  40. package/dist/chunk-CULQYFLU.js +14 -0
  41. package/dist/chunk-D65GOCJB.js +289 -0
  42. package/dist/chunk-D6YF5HKX.js +59 -0
  43. package/dist/chunk-DMS52BEP.js +342 -0
  44. package/dist/chunk-ELVWJYRV.js +211 -0
  45. package/dist/chunk-EUT3B22Y.js +27 -0
  46. package/dist/chunk-F5VLZHUW.js +146 -0
  47. package/dist/chunk-FC4RP4I2.js +12 -0
  48. package/dist/chunk-FGALODG3.js +55 -0
  49. package/dist/chunk-G3RLJYA2.js +2358 -0
  50. package/dist/chunk-GAYGGICG.js +32 -0
  51. package/dist/chunk-H2W2AXD3.js +419 -0
  52. package/dist/chunk-H5EV3VMF.js +114 -0
  53. package/dist/chunk-HREZQCT3.js +106 -0
  54. package/dist/chunk-HSIRMK2D.js +517 -0
  55. package/dist/chunk-IMSQR4WO.js +184 -0
  56. package/dist/chunk-IQZXOZ7S.js +4816 -0
  57. package/dist/chunk-IR6XFA5P.js +37 -0
  58. package/dist/chunk-IUKPRZER.js +78 -0
  59. package/dist/chunk-IUR76K6A.js +262 -0
  60. package/dist/chunk-J3FZR3YY.js +98 -0
  61. package/dist/chunk-J6DFZDFA.js +562 -0
  62. package/dist/chunk-J7WJZ6W7.js +50 -0
  63. package/dist/chunk-JONGANYH.js +202 -0
  64. package/dist/chunk-KJCWXUUS.js +425 -0
  65. package/dist/chunk-KJN4IOEJ.js +17 -0
  66. package/dist/chunk-KMGPUKFD.js +25 -0
  67. package/dist/chunk-LD7WFTRX.js +4533 -0
  68. package/dist/chunk-LJDODBBZ.js +78 -0
  69. package/dist/chunk-LNOMA3MP.js +169 -0
  70. package/dist/chunk-MGIWSROG.js +2334 -0
  71. package/dist/chunk-MQA2YBAH.js +308 -0
  72. package/dist/chunk-MTDRLSI7.js +21720 -0
  73. package/dist/chunk-N2IRDBZI.js +2355 -0
  74. package/dist/chunk-N77WFUL7.js +50 -0
  75. package/dist/chunk-N7YNJE5R.js +466 -0
  76. package/dist/chunk-NABLC7W3.js +148 -0
  77. package/dist/chunk-NFXP47RV.js +999 -0
  78. package/dist/chunk-NJF2X5XF.js +45 -0
  79. package/dist/chunk-NXL4FFQQ.js +47 -0
  80. package/dist/chunk-O7HZX7CA.js +309 -0
  81. package/dist/chunk-OC7LYAU5.js +708 -0
  82. package/dist/chunk-OEX24ECI.js +348 -0
  83. package/dist/chunk-OYOB3ZFG.js +78 -0
  84. package/dist/chunk-OYTMZXRF.js +41 -0
  85. package/dist/chunk-OZJINN4O.js +84 -0
  86. package/dist/chunk-PCHDGUKT.js +23 -0
  87. package/dist/chunk-QOZXJ4NA.js +3364 -0
  88. package/dist/chunk-QYF5S6P6.js +45 -0
  89. package/dist/chunk-RJFED73B.js +79 -0
  90. package/dist/chunk-RTQNRM5X.js +27 -0
  91. package/dist/chunk-S6AMWKPC.js +189 -0
  92. package/dist/chunk-T4QZ6FEH.js +308 -0
  93. package/dist/chunk-T54VJRAD.js +5456 -0
  94. package/dist/chunk-TA5L5KWQ.js +167 -0
  95. package/dist/chunk-TQFBZVCG.js +27 -0
  96. package/dist/chunk-U52SAGRM.js +62 -0
  97. package/dist/chunk-UEAHAMEI.js +5814 -0
  98. package/dist/chunk-UGSMCQ4D.js +327 -0
  99. package/dist/chunk-UOPVJQ4N.js +103 -0
  100. package/dist/chunk-USHG4DLH.js +459 -0
  101. package/dist/chunk-UXVNL3D7.js +23 -0
  102. package/dist/chunk-V5MEBST3.js +4139 -0
  103. package/dist/chunk-VGEVL5EQ.js +29 -0
  104. package/dist/chunk-VPWKF3GN.js +87 -0
  105. package/dist/chunk-VSMJK3ZJ.js +1250 -0
  106. package/dist/chunk-VYKKMYVT.js +560 -0
  107. package/dist/chunk-VZHJS6DR.js +442 -0
  108. package/dist/chunk-WATC3IQR.js +213 -0
  109. package/dist/chunk-WPE5FPGR.js +54 -0
  110. package/dist/chunk-WSEWKCXU.js +188 -0
  111. package/dist/chunk-WW5BLB74.js +659 -0
  112. package/dist/chunk-XGUL4SLQ.js +406 -0
  113. package/dist/chunk-XHSCVSOL.js +170 -0
  114. package/dist/chunk-XIMYZ3JA.js +27 -0
  115. package/dist/chunk-YLN4K6WD.js +385 -0
  116. package/dist/chunk-Z3CCVDUB.js +76 -0
  117. package/dist/chunk-Z54ZSOBZ.js +61 -0
  118. package/dist/chunk-Z6BB4JLQ.js +478 -0
  119. package/dist/chunk-Z7QQA2KT.js +27 -0
  120. package/dist/chunk-ZETYB3XG.js +2626 -0
  121. package/dist/chunk-ZMOMPX2Z.js +87 -0
  122. package/dist/chunk-ZOEYVXSV.js +149 -0
  123. package/dist/chunk-ZZUFZUQZ.js +12120 -0
  124. package/dist/index.js +39712 -29876
  125. package/dist/mcp/analyze-cli.js +186 -232
  126. package/dist/mcp/cli.js +137 -98
  127. package/dist/mcp/server.js +22688 -1573
  128. package/dist/mcp/worker.js +97 -38
  129. package/dist/probes/installer.js +135 -0
  130. package/dist/workers/project-snapshot-persist-worker.js +22 -0
  131. package/dist/workers/review-graph-persist-worker.js +15 -0
  132. package/docs/agent-guide.md +13 -4
  133. package/docs/agent-tools.md +42 -0
  134. package/docs/ast-grep_rules_catalog.md +5 -4
  135. package/docs/configuration.md +28 -2
  136. package/docs/custom-rules.md +25 -2
  137. package/docs/environment-variables.md +86 -8
  138. package/docs/features.md +45 -5
  139. package/docs/globalconfig.md +2 -1
  140. package/docs/io-bridge-v2-migration.md +94 -0
  141. package/docs/lsp-capability-matrix.md +37 -4
  142. package/docs/lsp-idle-eviction.md +124 -0
  143. package/docs/mcp.md +21 -5
  144. package/docs/merge-queue-rollout.md +77 -0
  145. package/docs/pi-lens-fixer.md +160 -88
  146. package/docs/pi-lens-investigator.md +74 -26
  147. package/docs/pi-lens-merge-policy.md +736 -0
  148. package/docs/pi-lens-monitor.md +75 -7
  149. package/docs/pi-lens-retro.md +6 -4
  150. package/docs/pi-lens-reviewer.md +189 -48
  151. package/docs/pi-lens-subagent.md +246 -45
  152. package/docs/pi-lens-warden.md +18 -7
  153. package/docs/public-api-stability.md +55 -0
  154. package/docs/release-qa-baseline.md +84 -17
  155. package/docs/rfcs/3654-unified-io-bridge-v2.md +316 -0
  156. package/docs/schema/pi-lens-config-v1.json +530 -0
  157. package/docs/servercapabilities.md +56 -49
  158. package/docs/settings.md +1 -0
  159. package/docs/usage.md +5 -1
  160. package/docs/word-index.md +6 -0
  161. package/grammars/tree-sitter-bash.wasm +0 -0
  162. package/grammars/tree-sitter-bash.wasm.json +3 -3
  163. package/package.json +19 -7
  164. package/rules/ast-grep-rules/rule-tests/advisory-registry-subset-test.yml +41 -0
  165. package/rules/ast-grep-rules/rule-tests/no-identity-operator-on-literals-test.yml +12 -2
  166. package/rules/ast-grep-rules/rule-tests/throw-new-error-js-test.yml +2 -0
  167. package/rules/ast-grep-rules/rule-tests/throw-new-error-test.yml +2 -0
  168. package/rules/ast-grep-rules/rules/advisory-registry-subset.yml +50 -0
  169. package/rules/ast-grep-rules/rules/no-identity-operator-on-literals.yml +20 -12
  170. package/rules/ast-grep-rules/rules/throw-new-error-js.yml +2 -2
  171. package/rules/ast-grep-rules/rules/throw-new-error.yml +2 -2
  172. package/scripts/analyze-pi-lens-logs.mjs +1275 -62
  173. package/scripts/download-grammars.js +5 -0
  174. package/scripts/grammars.lock.json +6 -1
  175. package/scripts/install-selftest.mjs +33 -34
  176. package/scripts/lib/host-provided-deps.mjs +13 -4
  177. package/scripts/lib/web-tree-sitter-dir.mjs +18 -0
  178. package/dist/clients/actionable-warnings-logger.js +0 -35
  179. package/dist/clients/actionable-warnings.js +0 -1610
  180. package/dist/clients/advisory-provenance.js +0 -593
  181. package/dist/clients/agent-behavior-client.js +0 -147
  182. package/dist/clients/agent-nudge.js +0 -325
  183. package/dist/clients/analysed-root.js +0 -1
  184. package/dist/clients/ast-grep-client.js +0 -731
  185. package/dist/clients/ast-grep-rule-manager.js +0 -146
  186. package/dist/clients/ast-grep-tool-logger.js +0 -118
  187. package/dist/clients/ast-grep-types.js +0 -10
  188. package/dist/clients/ast-grep-yaml-synth.js +0 -154
  189. package/dist/clients/atomic-write-staging.js +0 -75
  190. package/dist/clients/atomic-write.js +0 -189
  191. package/dist/clients/availability-probe-flight.js +0 -12
  192. package/dist/clients/bash-file-access.js +0 -1056
  193. package/dist/clients/biome-client.js +0 -357
  194. package/dist/clients/blocker-freshness.js +0 -837
  195. package/dist/clients/blocker-past-eof.js +0 -129
  196. package/dist/clients/bootstrap.js +0 -569
  197. package/dist/clients/bounded-cache.js +0 -190
  198. package/dist/clients/bounded-pid-file-lock.js +0 -202
  199. package/dist/clients/bounded-telemetry.js +0 -295
  200. package/dist/clients/build-identity.js +0 -95
  201. package/dist/clients/bundled-resource-health.js +0 -113
  202. package/dist/clients/bus-events-logger.js +0 -105
  203. package/dist/clients/bus-publish.js +0 -171
  204. package/dist/clients/cache/rule-cache.js +0 -190
  205. package/dist/clients/cache-manager.js +0 -524
  206. package/dist/clients/cache-observability.js +0 -1313
  207. package/dist/clients/call-graph.js +0 -852
  208. package/dist/clients/cargo-manifest.js +0 -422
  209. package/dist/clients/cascade-budget.js +0 -196
  210. package/dist/clients/cascade-format.js +0 -315
  211. package/dist/clients/cascade-logger.js +0 -31
  212. package/dist/clients/cascade-types.js +0 -9
  213. package/dist/clients/child-unref.js +0 -124
  214. package/dist/clients/code-quality-warnings.js +0 -286
  215. package/dist/clients/codebase-model.js +0 -173
  216. package/dist/clients/collateral-test-role.js +0 -40
  217. package/dist/clients/complexity-client.js +0 -462
  218. package/dist/clients/config-core/deny.js +0 -221
  219. package/dist/clients/config-core/index.js +0 -50
  220. package/dist/clients/config-core/merge.js +0 -357
  221. package/dist/clients/config-core/normalize.js +0 -340
  222. package/dist/clients/config-core/process-spec.js +0 -248
  223. package/dist/clients/config-core/provenance.js +0 -164
  224. package/dist/clients/config-core/records.js +0 -218
  225. package/dist/clients/config-core/resolve.js +0 -125
  226. package/dist/clients/config-core/safe-object.js +0 -78
  227. package/dist/clients/config-core/schema.js +0 -165
  228. package/dist/clients/config-diagnostic-codes.js +0 -285
  229. package/dist/clients/config-locations.js +0 -388
  230. package/dist/clients/config-resolve.js +0 -798
  231. package/dist/clients/config-schema.js +0 -251
  232. package/dist/clients/config-warn.js +0 -407
  233. package/dist/clients/console-guard-install.js +0 -26
  234. package/dist/clients/cooperative-budget.js +0 -42
  235. package/dist/clients/dead-code-client.js +0 -510
  236. package/dist/clients/dead-code-logger.js +0 -30
  237. package/dist/clients/deadline-utils.js +0 -280
  238. package/dist/clients/debug-handles.js +0 -211
  239. package/dist/clients/debug-heap.js +0 -174
  240. package/dist/clients/deferred-lsp-work.js +0 -106
  241. package/dist/clients/degradation-ledger.js +0 -426
  242. package/dist/clients/demoted-finding-render.js +0 -169
  243. package/dist/clients/dependency-checker.js +0 -885
  244. package/dist/clients/deps/ast-grep-napi.js +0 -22
  245. package/dist/clients/deps/js-yaml.js +0 -9
  246. package/dist/clients/deps/minimatch.js +0 -4
  247. package/dist/clients/deps/pi-tui.js +0 -14
  248. package/dist/clients/deps/typebox.js +0 -14
  249. package/dist/clients/deps/vscode-jsonrpc.js +0 -1
  250. package/dist/clients/deps/web-tree-sitter.js +0 -24
  251. package/dist/clients/diagnostic-dispositions.js +0 -630
  252. package/dist/clients/diagnostic-line-freshness.js +0 -305
  253. package/dist/clients/diagnostic-logger.js +0 -73
  254. package/dist/clients/diagnostic-tracker.js +0 -105
  255. package/dist/clients/diagnostics-publish.js +0 -230
  256. package/dist/clients/dispatch/ast-grep-catalog.js +0 -65
  257. package/dist/clients/dispatch/auxiliary-lsp.js +0 -343
  258. package/dist/clients/dispatch/collect-later-tier.js +0 -35
  259. package/dist/clients/dispatch/diagnostic-taxonomy.js +0 -66
  260. package/dist/clients/dispatch/dispatcher.js +0 -1269
  261. package/dist/clients/dispatch/fact-provider-types.js +0 -1
  262. package/dist/clients/dispatch/fact-rule-runner.js +0 -17
  263. package/dist/clients/dispatch/fact-runner.js +0 -30
  264. package/dist/clients/dispatch/fact-scheduler.js +0 -68
  265. package/dist/clients/dispatch/fact-store.js +0 -377
  266. package/dist/clients/dispatch/facts/comment-facts.js +0 -22
  267. package/dist/clients/dispatch/facts/file-content.js +0 -19
  268. package/dist/clients/dispatch/facts/function-facts.js +0 -348
  269. package/dist/clients/dispatch/facts/import-facts.js +0 -204
  270. package/dist/clients/dispatch/facts/tree-sitter-facts.js +0 -34
  271. package/dist/clients/dispatch/facts/try-catch-facts.js +0 -119
  272. package/dist/clients/dispatch/finding-policy.js +0 -339
  273. package/dist/clients/dispatch/indent-detect.js +0 -417
  274. package/dist/clients/dispatch/inline-suppressions.js +0 -89
  275. package/dist/clients/dispatch/integration.js +0 -2377
  276. package/dist/clients/dispatch/lazy.js +0 -11
  277. package/dist/clients/dispatch/pending-runner-findings.js +0 -87
  278. package/dist/clients/dispatch/plan.js +0 -278
  279. package/dist/clients/dispatch/priorities.js +0 -20
  280. package/dist/clients/dispatch/rule-id-normalize.js +0 -50
  281. package/dist/clients/dispatch/rule-ignores.js +0 -69
  282. package/dist/clients/dispatch/rule-policy.js +0 -135
  283. package/dist/clients/dispatch/rules/async-noise.js +0 -40
  284. package/dist/clients/dispatch/rules/async-unnecessary-wrapper.js +0 -27
  285. package/dist/clients/dispatch/rules/cors-wildcard.js +0 -54
  286. package/dist/clients/dispatch/rules/error-obscuring.js +0 -32
  287. package/dist/clients/dispatch/rules/error-swallowing.js +0 -31
  288. package/dist/clients/dispatch/rules/framework-call-noise.js +0 -67
  289. package/dist/clients/dispatch/rules/high-complexity.js +0 -72
  290. package/dist/clients/dispatch/rules/high-fan-out.js +0 -74
  291. package/dist/clients/dispatch/rules/high-import-coupling.js +0 -37
  292. package/dist/clients/dispatch/rules/missing-error-propagation.js +0 -62
  293. package/dist/clients/dispatch/rules/no-commented-credentials.js +0 -52
  294. package/dist/clients/dispatch/rules/pass-through-wrappers.js +0 -42
  295. package/dist/clients/dispatch/rules/placeholder-comments.js +0 -40
  296. package/dist/clients/dispatch/rules/unsafe-boundary.js +0 -126
  297. package/dist/clients/dispatch/runner-context.js +0 -52
  298. package/dist/clients/dispatch/runners/actionlint.js +0 -111
  299. package/dist/clients/dispatch/runners/ast-grep-napi.js +0 -1196
  300. package/dist/clients/dispatch/runners/biome-check.js +0 -296
  301. package/dist/clients/dispatch/runners/cpp-check.js +0 -219
  302. package/dist/clients/dispatch/runners/credo.js +0 -82
  303. package/dist/clients/dispatch/runners/cue-vet.js +0 -416
  304. package/dist/clients/dispatch/runners/dart-analyze.js +0 -183
  305. package/dist/clients/dispatch/runners/detekt.js +0 -176
  306. package/dist/clients/dispatch/runners/dotnet-build.js +0 -164
  307. package/dist/clients/dispatch/runners/elixir-check.js +0 -195
  308. package/dist/clients/dispatch/runners/eslint.js +0 -137
  309. package/dist/clients/dispatch/runners/fact-rules.js +0 -29
  310. package/dist/clients/dispatch/runners/fish-indent.js +0 -69
  311. package/dist/clients/dispatch/runners/gleam-check.js +0 -167
  312. package/dist/clients/dispatch/runners/go-vet.js +0 -104
  313. package/dist/clients/dispatch/runners/golangci-lint.js +0 -135
  314. package/dist/clients/dispatch/runners/hadolint.js +0 -91
  315. package/dist/clients/dispatch/runners/helm-lint.js +0 -171
  316. package/dist/clients/dispatch/runners/helm-render.js +0 -940
  317. package/dist/clients/dispatch/runners/htmlhint.js +0 -117
  318. package/dist/clients/dispatch/runners/index.js +0 -108
  319. package/dist/clients/dispatch/runners/javac.js +0 -99
  320. package/dist/clients/dispatch/runners/ktlint.js +0 -110
  321. package/dist/clients/dispatch/runners/lsp.js +0 -379
  322. package/dist/clients/dispatch/runners/markdownlint.js +0 -167
  323. package/dist/clients/dispatch/runners/mypy.js +0 -102
  324. package/dist/clients/dispatch/runners/oxlint.js +0 -414
  325. package/dist/clients/dispatch/runners/php-lint.js +0 -85
  326. package/dist/clients/dispatch/runners/phpstan.js +0 -120
  327. package/dist/clients/dispatch/runners/prisma-validate.js +0 -62
  328. package/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -445
  329. package/dist/clients/dispatch/runners/pyright.js +0 -169
  330. package/dist/clients/dispatch/runners/rubocop.js +0 -99
  331. package/dist/clients/dispatch/runners/ruff.js +0 -88
  332. package/dist/clients/dispatch/runners/rust-clippy.js +0 -256
  333. package/dist/clients/dispatch/runners/shellcheck.js +0 -173
  334. package/dist/clients/dispatch/runners/shfmt.js +0 -102
  335. package/dist/clients/dispatch/runners/spellcheck.js +0 -128
  336. package/dist/clients/dispatch/runners/spotbugs.js +0 -259
  337. package/dist/clients/dispatch/runners/sqlfluff.js +0 -183
  338. package/dist/clients/dispatch/runners/stylelint.js +0 -166
  339. package/dist/clients/dispatch/runners/swiftlint.js +0 -163
  340. package/dist/clients/dispatch/runners/taplo.js +0 -174
  341. package/dist/clients/dispatch/runners/terragrunt.js +0 -168
  342. package/dist/clients/dispatch/runners/tflint.js +0 -125
  343. package/dist/clients/dispatch/runners/tree-sitter.js +0 -643
  344. package/dist/clients/dispatch/runners/trivy-config.js +0 -253
  345. package/dist/clients/dispatch/runners/utils/availability-policy.js +0 -496
  346. package/dist/clients/dispatch/runners/utils/candidate-probe.js +0 -90
  347. package/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -95
  348. package/dist/clients/dispatch/runners/utils/lazy-installer.js +0 -274
  349. package/dist/clients/dispatch/runners/utils/runner-helpers.js +0 -1701
  350. package/dist/clients/dispatch/runners/utils/spawn-outcome.js +0 -101
  351. package/dist/clients/dispatch/runners/utils/tool-failure.js +0 -235
  352. package/dist/clients/dispatch/runners/utils/toolchain-availability.js +0 -169
  353. package/dist/clients/dispatch/runners/utils.js +0 -15
  354. package/dist/clients/dispatch/runners/vale.js +0 -128
  355. package/dist/clients/dispatch/runners/yaml-rule-parser.js +0 -242
  356. package/dist/clients/dispatch/runners/yamllint.js +0 -95
  357. package/dist/clients/dispatch/runners/zig-check.js +0 -79
  358. package/dist/clients/dispatch/suppress-writer.js +0 -79
  359. package/dist/clients/dispatch/tool-profile.js +0 -30
  360. package/dist/clients/dispatch/types.js +0 -21
  361. package/dist/clients/dispatch/utils/format-utils.js +0 -45
  362. package/dist/clients/dispatch/utils/lsp-diagnostics.js +0 -35
  363. package/dist/clients/disposition-logger.js +0 -32
  364. package/dist/clients/disposition-publish.js +0 -124
  365. package/dist/clients/durable-store.js +0 -97
  366. package/dist/clients/effective-config.js +0 -415
  367. package/dist/clients/env-utils.js +0 -83
  368. package/dist/clients/error-class.js +0 -23
  369. package/dist/clients/eval-timestamp.js +0 -17
  370. package/dist/clients/event-loop-hold.js +0 -274
  371. package/dist/clients/event-loop-monitor.js +0 -250
  372. package/dist/clients/extension-log.js +0 -504
  373. package/dist/clients/extension-mode.js +0 -73
  374. package/dist/clients/feature-hints.js +0 -103
  375. package/dist/clients/file-kinds.js +0 -412
  376. package/dist/clients/file-role.js +0 -134
  377. package/dist/clients/file-time.js +0 -160
  378. package/dist/clients/file-utils.js +0 -1032
  379. package/dist/clients/finding-delivery-gate.js +0 -494
  380. package/dist/clients/finding-identity.js +0 -88
  381. package/dist/clients/fix-worklog.js +0 -103
  382. package/dist/clients/format-events-publish.js +0 -343
  383. package/dist/clients/format-service.js +0 -222
  384. package/dist/clients/formatters-lazy.js +0 -9
  385. package/dist/clients/formatters.js +0 -2032
  386. package/dist/clients/freshness-cadence.js +0 -17
  387. package/dist/clients/freshness.js +0 -38
  388. package/dist/clients/generated-artifacts.js +0 -394
  389. package/dist/clients/generation-guard.js +0 -215
  390. package/dist/clients/git-guard.js +0 -1136
  391. package/dist/clients/git-tracked-ignore.js +0 -243
  392. package/dist/clients/gitleaks-client.js +0 -509
  393. package/dist/clients/go-client.js +0 -104
  394. package/dist/clients/govulncheck-client.js +0 -605
  395. package/dist/clients/gradle-ktfmt-style.js +0 -348
  396. package/dist/clients/grammar-source.js +0 -465
  397. package/dist/clients/gzip-stage-write.js +0 -135
  398. package/dist/clients/hashline-anchor.js +0 -424
  399. package/dist/clients/hook-budgets.js +0 -76
  400. package/dist/clients/host-edit-normalize.js +0 -140
  401. package/dist/clients/host-ports.js +0 -39
  402. package/dist/clients/indent-retarget.js +0 -154
  403. package/dist/clients/inline-blocker-dispositions.js +0 -186
  404. package/dist/clients/install-diagnostics.js +0 -156
  405. package/dist/clients/installer/index.js +0 -5416
  406. package/dist/clients/installer/managed-tool-refresh-session.js +0 -58
  407. package/dist/clients/installer/managed-tool-refresh.js +0 -783
  408. package/dist/clients/instance-reaper-state.js +0 -7
  409. package/dist/clients/instance-reaper.js +0 -1137
  410. package/dist/clients/instance-registry-lock.js +0 -145
  411. package/dist/clients/instance-registry.js +0 -773
  412. package/dist/clients/jscpd-client.js +0 -378
  413. package/dist/clients/json-cache-read.js +0 -65
  414. package/dist/clients/knip-client.js +0 -788
  415. package/dist/clients/language-policy.js +0 -252
  416. package/dist/clients/language-profile.js +0 -361
  417. package/dist/clients/language-registry.js +0 -603
  418. package/dist/clients/latency-logger.js +0 -727
  419. package/dist/clients/lazy-import.js +0 -21
  420. package/dist/clients/ledger-bounds.js +0 -54
  421. package/dist/clients/lens-config.js +0 -404
  422. package/dist/clients/lens-engine.js +0 -358
  423. package/dist/clients/lens-events.js +0 -125
  424. package/dist/clients/lens-flag-registry.js +0 -467
  425. package/dist/clients/lens-map.js +0 -1156
  426. package/dist/clients/live-bus-emitter.js +0 -74
  427. package/dist/clients/log-cleanup.js +0 -222
  428. package/dist/clients/lsp/aggregation.js +0 -178
  429. package/dist/clients/lsp/cascade-tier.js +0 -421
  430. package/dist/clients/lsp/client.js +0 -4593
  431. package/dist/clients/lsp/config.js +0 -691
  432. package/dist/clients/lsp/content-limits.js +0 -41
  433. package/dist/clients/lsp/diagnostic-binding.js +0 -225
  434. package/dist/clients/lsp/document-drift.js +0 -467
  435. package/dist/clients/lsp/edits.js +0 -1219
  436. package/dist/clients/lsp/index.js +0 -8305
  437. package/dist/clients/lsp/inferred-project.js +0 -216
  438. package/dist/clients/lsp/jvm-runtime.js +0 -298
  439. package/dist/clients/lsp/language.js +0 -63
  440. package/dist/clients/lsp/launch.js +0 -746
  441. package/dist/clients/lsp/lombok.js +0 -158
  442. package/dist/clients/lsp/path-utils.js +0 -5
  443. package/dist/clients/lsp/pending-aux-coverage.js +0 -251
  444. package/dist/clients/lsp/position-encoding.js +0 -61
  445. package/dist/clients/lsp/ruby-drive-dirs.js +0 -83
  446. package/dist/clients/lsp/server.js +0 -3290
  447. package/dist/clients/lsp/session-roots.js +0 -192
  448. package/dist/clients/lsp/spawn-history.js +0 -17
  449. package/dist/clients/lsp/sync-kind.js +0 -56
  450. package/dist/clients/lsp/tsserver-sync.js +0 -332
  451. package/dist/clients/lsp/wait-policy/capability-snapshot.js +0 -1
  452. package/dist/clients/lsp/wait-policy/classification.js +0 -85
  453. package/dist/clients/lsp/wait-policy/index.js +0 -10
  454. package/dist/clients/lsp/wait-policy/strategies.js +0 -392
  455. package/dist/clients/lsp/watch-queue.js +0 -61
  456. package/dist/clients/lsp/workspace-diagnostics-cache.js +0 -639
  457. package/dist/clients/lsp/workspace-diagnostics-session.js +0 -30
  458. package/dist/clients/lsp/workspace-sweep-hold.js +0 -184
  459. package/dist/clients/lsp-budget.js +0 -186
  460. package/dist/clients/lsp-document-symbols.js +0 -110
  461. package/dist/clients/lsp-lazy.js +0 -9
  462. package/dist/clients/lsp-mutation.js +0 -396
  463. package/dist/clients/lsp-status.js +0 -59
  464. package/dist/clients/map-with-concurrency.js +0 -40
  465. package/dist/clients/mcp/analyze.js +0 -420
  466. package/dist/clients/mcp/host-shim.js +0 -31
  467. package/dist/clients/mcp/ipc.js +0 -523
  468. package/dist/clients/mcp/review.js +0 -197
  469. package/dist/clients/mcp/session.js +0 -412
  470. package/dist/clients/memory-sampler.js +0 -208
  471. package/dist/clients/message-end-attribution.js +0 -30
  472. package/dist/clients/metrics-client.js +0 -107
  473. package/dist/clients/metrics-history.js +0 -327
  474. package/dist/clients/middle-man-analysis.js +0 -263
  475. package/dist/clients/model-provider.js +0 -53
  476. package/dist/clients/module-report-lsp.js +0 -189
  477. package/dist/clients/module-report.js +0 -2150
  478. package/dist/clients/mutating-tool.js +0 -651
  479. package/dist/clients/mutation-attribution.js +0 -368
  480. package/dist/clients/mutation-bridge.js +0 -240
  481. package/dist/clients/ndjson-logger.js +0 -734
  482. package/dist/clients/observed-mutation-sources.js +0 -101
  483. package/dist/clients/observed-mutation.js +0 -1253
  484. package/dist/clients/oldtext-autopatch.js +0 -128
  485. package/dist/clients/opaque-mutation-scan.js +0 -496
  486. package/dist/clients/opengrep-client.js +0 -333
  487. package/dist/clients/opengrep-config.js +0 -76
  488. package/dist/clients/package-manager.js +0 -607
  489. package/dist/clients/package-root.js +0 -38
  490. package/dist/clients/partial-edit-apply.js +0 -439
  491. package/dist/clients/path-attribution-telemetry.js +0 -43
  492. package/dist/clients/path-keyed-map.js +0 -108
  493. package/dist/clients/path-utils.js +0 -1244
  494. package/dist/clients/performance-report.js +0 -331
  495. package/dist/clients/persist-debounce.js +0 -70
  496. package/dist/clients/persistent-reverify.js +0 -271
  497. package/dist/clients/php-cs-fixer-config.js +0 -119
  498. package/dist/clients/pipeline.js +0 -1480
  499. package/dist/clients/post-autofix-notice.js +0 -37
  500. package/dist/clients/print-mode.js +0 -21
  501. package/dist/clients/probe-home-state.js +0 -228
  502. package/dist/clients/process-bridge.js +0 -66
  503. package/dist/clients/process-singletons.js +0 -210
  504. package/dist/clients/process-snapshot.js +0 -68
  505. package/dist/clients/project-changes.js +0 -181
  506. package/dist/clients/project-conventions.js +0 -171
  507. package/dist/clients/project-diagnostics/cache.js +0 -137
  508. package/dist/clients/project-diagnostics/extractors.js +0 -109
  509. package/dist/clients/project-diagnostics/fresh-fetch.js +0 -668
  510. package/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +0 -118
  511. package/dist/clients/project-diagnostics/runner-adapters/dead-code.js +0 -47
  512. package/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +0 -40
  513. package/dist/clients/project-diagnostics/runner-adapters/govulncheck.js +0 -34
  514. package/dist/clients/project-diagnostics/runner-adapters/jscpd.js +0 -38
  515. package/dist/clients/project-diagnostics/runner-adapters/knip.js +0 -47
  516. package/dist/clients/project-diagnostics/runner-adapters/madge.js +0 -48
  517. package/dist/clients/project-diagnostics/runner-adapters/opengrep.js +0 -40
  518. package/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +0 -127
  519. package/dist/clients/project-diagnostics/runner-adapters/trivy.js +0 -81
  520. package/dist/clients/project-diagnostics/scanner.js +0 -520
  521. package/dist/clients/project-diagnostics/types.js +0 -1
  522. package/dist/clients/project-lens-config.js +0 -807
  523. package/dist/clients/project-report.js +0 -701
  524. package/dist/clients/project-scale.js +0 -237
  525. package/dist/clients/project-scan-policy.js +0 -11
  526. package/dist/clients/project-snapshot-fingerprint.js +0 -48
  527. package/dist/clients/project-snapshot-persist-worker.js +0 -20
  528. package/dist/clients/project-snapshot.js +0 -1653
  529. package/dist/clients/project-trust.js +0 -181
  530. package/dist/clients/python-environment.js +0 -217
  531. package/dist/clients/python-provenance.js +0 -639
  532. package/dist/clients/quiet-window-config.js +0 -47
  533. package/dist/clients/quiet-window.js +0 -236
  534. package/dist/clients/read-bridge.js +0 -139
  535. package/dist/clients/read-expansion.js +0 -266
  536. package/dist/clients/read-guard-logger.js +0 -251
  537. package/dist/clients/read-guard-tool-lines.js +0 -1306
  538. package/dist/clients/read-guard.js +0 -1561
  539. package/dist/clients/recent-touches.js +0 -240
  540. package/dist/clients/redact/secrets.js +0 -449
  541. package/dist/clients/resource-sampler.js +0 -628
  542. package/dist/clients/reverse-deps.js +0 -270
  543. package/dist/clients/review-graph/builder.js +0 -5126
  544. package/dist/clients/review-graph/format.js +0 -33
  545. package/dist/clients/review-graph/git-identity.js +0 -163
  546. package/dist/clients/review-graph/import-resolvers.js +0 -481
  547. package/dist/clients/review-graph/persist-worker.js +0 -10
  548. package/dist/clients/review-graph/query.js +0 -244
  549. package/dist/clients/review-graph/revision-drift.js +0 -21
  550. package/dist/clients/review-graph/service.js +0 -96
  551. package/dist/clients/review-graph/shared-extraction-ir.js +0 -82
  552. package/dist/clients/review-graph/symbol-id.js +0 -103
  553. package/dist/clients/review-graph/tsconfig-paths.js +0 -299
  554. package/dist/clients/review-graph/types.js +0 -1
  555. package/dist/clients/review-graph/workspace-modules.js +0 -449
  556. package/dist/clients/review-graph-logger.js +0 -81
  557. package/dist/clients/ruff-client.js +0 -179
  558. package/dist/clients/rules-scanner.js +0 -131
  559. package/dist/clients/run-duration.js +0 -55
  560. package/dist/clients/runtime-agent-end.js +0 -746
  561. package/dist/clients/runtime-config.js +0 -77
  562. package/dist/clients/runtime-context.js +0 -211
  563. package/dist/clients/runtime-coordinator.js +0 -1324
  564. package/dist/clients/runtime-session.js +0 -2485
  565. package/dist/clients/runtime-tool-call.js +0 -1295
  566. package/dist/clients/runtime-tool-result.js +0 -2049
  567. package/dist/clients/runtime-turn.js +0 -3767
  568. package/dist/clients/rust-client.js +0 -97
  569. package/dist/clients/safe-spawn.js +0 -2357
  570. package/dist/clients/sanitize.js +0 -286
  571. package/dist/clients/scan-utils.js +0 -25
  572. package/dist/clients/scratch-tree-policy.js +0 -165
  573. package/dist/clients/search-read-registration.js +0 -94
  574. package/dist/clients/secret-findings.js +0 -114
  575. package/dist/clients/security-scan-client.js +0 -352
  576. package/dist/clients/session-event-guard.js +0 -297
  577. package/dist/clients/session-lifecycle.js +0 -484
  578. package/dist/clients/session-start-observability.js +0 -94
  579. package/dist/clients/session-state-store.js +0 -101
  580. package/dist/clients/session-summary.js +0 -15
  581. package/dist/clients/sessionstart-logger.js +0 -27
  582. package/dist/clients/sg-runner.js +0 -876
  583. package/dist/clients/sgconfig.js +0 -347
  584. package/dist/clients/shared-checkout-guard.js +0 -338
  585. package/dist/clients/single-flight.js +0 -158
  586. package/dist/clients/situational-tool-telemetry.js +0 -108
  587. package/dist/clients/skills-resolver.js +0 -141
  588. package/dist/clients/slow-fs.js +0 -138
  589. package/dist/clients/smells-rollup.js +0 -231
  590. package/dist/clients/source-filter.js +0 -596
  591. package/dist/clients/source-walker.js +0 -211
  592. package/dist/clients/spawn-output-cap.js +0 -120
  593. package/dist/clients/spawn-timeout-cooldown.js +0 -110
  594. package/dist/clients/stale-marker.js +0 -17
  595. package/dist/clients/startup-scan.js +0 -400
  596. package/dist/clients/startup-timing.js +0 -130
  597. package/dist/clients/string-utils.js +0 -33
  598. package/dist/clients/subagent-mode.js +0 -111
  599. package/dist/clients/symbol-containment.js +0 -24
  600. package/dist/clients/symbol-types.js +0 -5
  601. package/dist/clients/test-runner-client.js +0 -2507
  602. package/dist/clients/test-runner-delivery.js +0 -287
  603. package/dist/clients/todo-scanner.js +0 -229
  604. package/dist/clients/tool-agreement.js +0 -423
  605. package/dist/clients/tool-config.js +0 -249
  606. package/dist/clients/tool-cwd.js +0 -270
  607. package/dist/clients/tool-definition.js +0 -47
  608. package/dist/clients/tool-event.js +0 -105
  609. package/dist/clients/tool-policy.js +0 -2755
  610. package/dist/clients/tool-probe.js +0 -53
  611. package/dist/clients/tool-render.js +0 -156
  612. package/dist/clients/tool-set-policy.js +0 -118
  613. package/dist/clients/tree-sitter-cache.js +0 -364
  614. package/dist/clients/tree-sitter-client.js +0 -4195
  615. package/dist/clients/tree-sitter-logger.js +0 -100
  616. package/dist/clients/tree-sitter-navigator.js +0 -269
  617. package/dist/clients/tree-sitter-query-loader.js +0 -569
  618. package/dist/clients/tree-sitter-shared.js +0 -158
  619. package/dist/clients/tree-sitter-symbol-extractor.js +0 -1265
  620. package/dist/clients/trivy-client.js +0 -467
  621. package/dist/clients/tui-fit.js +0 -50
  622. package/dist/clients/turn-context.js +0 -52
  623. package/dist/clients/turn-end/lane.js +0 -51
  624. package/dist/clients/turn-end/lanes/govulncheck.js +0 -112
  625. package/dist/clients/turn-end/lanes/secrets.js +0 -204
  626. package/dist/clients/turn-summary-render.js +0 -72
  627. package/dist/clients/turn-summary.js +0 -132
  628. package/dist/clients/typos-config.js +0 -29
  629. package/dist/clients/user-notify.js +0 -51
  630. package/dist/clients/vanished-instance-marker.js +0 -57
  631. package/dist/clients/warm-attach.js +0 -305
  632. package/dist/clients/widget-state.js +0 -1808
  633. package/dist/clients/word-index-logger.js +0 -49
  634. package/dist/clients/word-index-store.js +0 -459
  635. package/dist/clients/word-index.js +0 -2080
  636. package/dist/clients/workspace-topology.js +0 -443
  637. package/dist/clients/write-ordering-guard.js +0 -65
  638. package/dist/clients/zizmor-config.js +0 -340
  639. package/dist/i18n.js +0 -69
  640. package/dist/mcp/build-staleness.js +0 -124
  641. package/dist/scripts/lib/process-scan.mjs +0 -583
  642. package/dist/scripts/lib/skills-predicate.mjs +0 -129
  643. package/dist/scripts/lib/web-tree-sitter-dir.mjs +0 -123
  644. package/dist/tools/activate-tools.js +0 -87
  645. package/dist/tools/ast-grep-outline.js +0 -137
  646. package/dist/tools/ast-grep-replace.js +0 -244
  647. package/dist/tools/ast-grep-search.js +0 -762
  648. package/dist/tools/effective-config.js +0 -90
  649. package/dist/tools/lens-diagnostic-mark.js +0 -375
  650. package/dist/tools/lens-diagnostics.js +0 -2571
  651. package/dist/tools/lsp-diagnostics.js +0 -1745
  652. package/dist/tools/lsp-navigation.js +0 -1367
  653. package/dist/tools/lsp-structured-output.js +0 -294
  654. package/dist/tools/module-report.js +0 -382
  655. package/dist/tools/project-report.js +0 -86
  656. package/dist/tools/render-compact.js +0 -374
  657. package/dist/tools/scan-progress.js +0 -53
  658. package/dist/tools/shared.js +0 -29
  659. package/dist/tools/symbol-search.js +0 -155
@@ -0,0 +1,316 @@
1
+ # RFC 3654: Unified File I/O Lifecycle Bridge (v2 Specification)
2
+
3
+ **Status:** Accepted by the maintainer on #4145 (2026-10-08)
4
+ **Tracking Issue:** [apmantza/pi-lens#3654](https://github.com/apmantza/pi-lens/issues/3654)
5
+ **Related Issues/PRs:** #3651 (multi-span batching), #3652 (0-line reads), #3650 (mutating-tool path), #3785 (read-guard evidence & FileTime), #2465 (no-read-guard split), #3620 (lineage fence)
6
+ **Authors:** Architecture Council (`lens-orchestrator`, `council-peer`)
7
+
8
+ ---
9
+
10
+ ## 1. Executive Summary & Teleological Frame
11
+
12
+ ### Problem
13
+ Pi coding-agent extensions interacting with the filesystem outside pi-lens's native tool hooks currently face severe friction and architectural fragmentation:
14
+ 1. **Dual Bridge Overhead**: Producers performing atomic edit + preview-diff turns must call two separate global symbol bridges: `Symbol.for("pi-lens:mutation-bridge")` then `Symbol.for("pi-lens:read-bridge")`.
15
+ 2. **Redundant Synchronous I/O (#3651)**: `ReadBridge.recordRead` executes two synchronous `fs.readFileSync` calls per read (one for `captureReadContentBinding` and one for `captureLineHashes`), even when the producer already holds file content in memory.
16
+ 3. **Absence of Delete Lifecycle**: Programmatic file deletion is unrepresented in existing bridges, leaving zombie records in `readGuard.reads`, `writtenThisSession`, and `knownPathIndex`.
17
+ 4. **Ordering & Freshness Hazards**: Concurrent writers and multi-window reads risk desynchronizing `FileTime` and read evidence if the mutation and read facets are not executed in strict atomic sequence.
18
+
19
+ ### Proposed Outcome
20
+ A unified, cohesive File I/O Lifecycle Bridge mounted at `Symbol.for("pi-lens:io-bridge")` carrying `readonly version: 2`, consolidating file observation (`read`), partial modification (`edit`), whole-file authorship (`write`), and deletion (`delete`) into an atomic, synchronous, non-throwing contract. Existing v1 bridges are preserved as zero-overhead shims mounted in the same first-wins pass.
21
+
22
+ ---
23
+
24
+ ## 2. Non-Negotiable Architectural Decisions (D1–D13)
25
+
26
+ | ID | Decision | Invariant & Enforcement |
27
+ |---|---|---|
28
+ | **D1** | **Symbol Key & Version** | `Symbol.for("pi-lens:io-bridge")` + `readonly version: 2`. Policy: `version` covers minors; breaking majors require a new symbol key (first-wins frozen per-process slot). In the same first-wins pass, mount v1 shims for `pi-lens:read-bridge` and `pi-lens:mutation-bridge` preserving v1 return types (`void` and `boolean`). |
29
+ | **D2** | **Mutation Umbrella Name** | The mutation facet is named **`mutate`** (not `write`), because `delete` is an absence event, not a write. |
30
+ | **D3** | **`delete` Primitive** | `delete` is a `kind` under `mutate` (`mutate: { kind: "delete" }`), wrapped by the enclosing `!getFlag("no-read-guard")` gate, followed by the 4 confirmed-delete gates in production order, shared with the native bash path through `judgeConfirmedDelete` (`clients/confirmed-delete.ts`): (1) `isExternalOrVendorFile`, (2) `isPathIgnoredByProject`, (3) `readGuard.hasKnownPath`, (4) `!existsSync` confirm. Action: `readGuard.forgetPath(filePath)` and, when `!getFlag("no-lsp")`, `notifyExternalFileChange(filePath, 3)`. `no-lsp` suppresses only the notification; the native bash path skips the whole delete under `no-lsp`. Bypasses `recordWritten` and `addModifiedRange`; emits no change-log receipt. |
31
+ | **D4** | **`rename` Deferred** | Compose as `delete(old)` + `write(new)`. Dead fields like `renamedFrom` are omitted from v2 until an active consumer exists. |
32
+ | **D5** | **Read Evidence Policy** | Default is **coverage-only** (`evidence: "caller"` with no content/hashes binds range without line hashes). `"disk"` is an explicit opt-in for bridge-initiated disk hashing. The bridge never synthesizes hash evidence unless `"disk"` is passed. |
33
+ | **D6** | **`content` Semantics** | Delivered raw text starting at `ranges[0][0]` (span-relative). No caller-fabricated `contentBinding`; whole-file staleness checks stay disk-derived only. |
34
+ | **D7** | **Span Constraints** | `lineHashes` is keyed by absolute 1-indexed line numbers and supports multi-span reads. `content` is valid **only** with a single contiguous range (`ranges.length === 1`). |
35
+ | **D8** | **Hash Map Typing** | `lineHashes` is `Record<number, string>` (keys integers $\ge 1$), matching `ReadRecord.lineHashes`. |
36
+ | **D9** | **Never Throws** | `record()` never throws. Malformed, out-of-scope, or ignored calls return `{ accepted: false, reason: RecordReason }`. Hash keys outside declared ranges are rejected with `"malformed"`. |
37
+ | **D10** | **Independent Facet Outcomes** | Facets apply independently; `record()` returns `{ read?: RecordOutcome, mutate?: RecordOutcome }`. |
38
+ | **D11** | **Accessor Idiom** | `getIOBridge()` returns `undefined` on version mismatch (mirroring v1). |
39
+ | **D12** | **Public API Stability** | Add section to `docs/public-api-stability.md` designating `PiLensIOBridge` as public API from day one, linking `docs/io-bridge-v2-migration.md`, with changelog fragment audience `user`. |
40
+ | **D13** | **TLA+ & Governance Plan** | PR implementation must add row in `formal/coverage-map.json` mapping `clients/io-bridge.ts` to `[session-lifecycle, read-guard]`; verify write-before-read and lineage fence invariants, or cite `TLA+ unaffected`. |
41
+ | **D14** | **Backward-Compatible Refinement** | v1 behavior may be refined in v2, but v1's observable behavior MUST NOT break. v1 shims (`read-bridge`, `mutation-bridge`) are a frozen compatibility layer. Every input v1 accepted, v1 accepts; same return types; same effective guard/cache key; same display path. Detailed mappings live in `docs/io-bridge-v2-migration.md`. |
42
+
43
+ ---
44
+
45
+ ## 3. Normative TypeScript Interface
46
+
47
+ ```typescript
48
+ /**
49
+ * Generic File I/O Lifecycle Bridge for pi-lens (v2).
50
+ * Mounts at globalThis[IO_BRIDGE_SYMBOL].
51
+ */
52
+
53
+ export const IO_BRIDGE_SYMBOL = Symbol.for("pi-lens:io-bridge");
54
+ export const IO_BRIDGE_VERSION = 2 as const;
55
+
56
+ /** 1-indexed closed line interval: [start, end] where start <= end. */
57
+ export type LineRange = [start: number, end: number];
58
+
59
+ /** Map of 1-indexed line numbers to alphanumeric line-content hashes. */
60
+ export type LineHashMap = Record<number, string>;
61
+
62
+ export interface ReadFacet {
63
+ /**
64
+ * 1-indexed line ranges delivered to the model. REQUIRED.
65
+ * `[]` represents an explicit zero-line / 0-byte read (#3652).
66
+ * Full-file read is represented by `[[1, lineCount]]`.
67
+ */
68
+ ranges: LineRange[];
69
+
70
+ /**
71
+ * Delivered raw text starting at ranges[0][0].
72
+ * Valid ONLY when ranges.length === 1 (single contiguous span).
73
+ */
74
+ content?: string;
75
+
76
+ /**
77
+ * Precomputed alphanumeric line hashes keyed by absolute 1-indexed line numbers.
78
+ * Supports multi-span reads. All keys must fall within declared ranges.
79
+ */
80
+ lineHashes?: LineHashMap;
81
+
82
+ /**
83
+ * Evidence collection mode:
84
+ * - "caller" (default): Use content / lineHashes if provided. If neither is provided,
85
+ * records coverage-only with NO hashes (no disk reads performed).
86
+ * - "disk": Bridge synchronously disk-reads and hashes the declared ranges.
87
+ */
88
+ evidence?: "caller" | "disk";
89
+ }
90
+
91
+ export type MutationFacet =
92
+ | {
93
+ /** Partial file modification (line replacement / splice). */
94
+ kind: "edit";
95
+ /** 1-indexed line ranges modified. Must be non-empty. */
96
+ ranges: LineRange[];
97
+ /**
98
+ * Whether to queue deferred autofix and formatting for agent_settled.
99
+ * Defaults to true. When true, publishes `pilens:format:queued` on `pi.events`
100
+ * for newly-queued files (resolving the v1 event drop in mutation-bridge).
101
+ * Set to false to suppress both queueing and event emission.
102
+ */
103
+ deferAutofix?: boolean;
104
+ importsChanged?: boolean;
105
+ }
106
+ | {
107
+ /** Whole-file authorship (creation or complete replacement). */
108
+ kind: "write";
109
+ // The authored bytes are not part of v2 yet: binding creation read
110
+ // evidence from them is #3524's design (G9), and the field had no
111
+ // consumer (#4145 review F4).
112
+ /**
113
+ * Whether to queue deferred autofix and formatting for agent_settled.
114
+ * Defaults to true. When true, publishes `pilens:format:queued` on `pi.events`
115
+ * for newly-queued files. Set to false to suppress both queueing and event emission.
116
+ */
117
+ deferAutofix?: boolean;
118
+ importsChanged?: boolean;
119
+ }
120
+ | {
121
+ /**
122
+ * Lifecycle deletion: removes file from read records, writtenThisSession,
123
+ * and knownPathIndex, and notifies LSP DidClose / FileChangeType.Deleted (3).
124
+ * Mutually exclusive with `read`. Never calls recordWritten and emits no change-log receipt.
125
+ * Requires file to no longer exist on disk at the moment of recording.
126
+ */
127
+ kind: "delete";
128
+ };
129
+
130
+ export interface BridgeEntry {
131
+ /**
132
+ * Path to the file. Non-empty string.
133
+ * Preserves caller's raw display path verbatim (matching v1 read-bridge.ts:137-139
134
+ * and mutation-bridge.ts:322). Scope check runs deps.isRecordable(filePath) directly
135
+ * on the input string without pre-resolving; internal map keying is handled downstream
136
+ * via normalizeFilePath / normalizeEphemeralMapKey.
137
+ */
138
+ filePath: string;
139
+
140
+ /** Optional caller identity for logging and telemetry (e.g. "my-tool"). */
141
+ consumer?: string;
142
+
143
+ /** Mutation facet (at most one). */
144
+ mutate?: MutationFacet;
145
+
146
+ /** Read facet (at most one). Mutually exclusive with mutate.kind: "delete". */
147
+ read?: ReadFacet;
148
+ }
149
+
150
+ export type RecordReason =
151
+ | "malformed"
152
+ | "out-of-scope"
153
+ | "ignored"
154
+ | "no-read-guard"
155
+ | "stale-lineage"
156
+ | "bookkeeping-error";
157
+
158
+ export type RecordOutcome =
159
+ | { accepted: true }
160
+ | { accepted: false; reason: RecordReason };
161
+
162
+ export interface RecordResult {
163
+ read?: RecordOutcome;
164
+ mutate?: RecordOutcome;
165
+ }
166
+
167
+ export interface PiLensIOBridge {
168
+ readonly version: 2;
169
+ /**
170
+ * Synchronously records file I/O operations.
171
+ * Atomic: executes mutate before read.
172
+ * Evaluates per-facet scope and #2465 flag gates.
173
+ * Never throws.
174
+ */
175
+ record(entry: BridgeEntry): RecordResult;
176
+ }
177
+
178
+ /** Convenience accessor performing symbol lookup + version === 2 check. */
179
+ export function getIOBridge(): PiLensIOBridge | undefined;
180
+ ```
181
+
182
+ ---
183
+
184
+ ## 4. Keel Architectural Review
185
+
186
+ | Keel Dimension | Concern | Current Design / Trap | Defect / Risk | Proposed Normative Rule | Justification |
187
+ |---|---|---|---|---|---|
188
+ | **Authority** | Path Normalization (§3, F1) | Pre-resolving `filePath` against `deps.getProjectRoot()` at the bridge entry. | Clobbers the caller's raw display path and creates key divergence if resolution semantics drift from `ReadGuard.key()` / `normalizeFilePath`. Violates AGENTS Shape #1 and display-path preservation. | `filePath` MUST be a non-empty string (`typeof filePath === "string" && filePath.length > 0`). Evaluated via `deps.isRecordable(filePath)` directly. The bridge NEVER pre-resolves or clobbers `filePath`. Internal canonicalization and map keying remain strictly encapsulated in downstream owners (`ReadGuard.key()` via `normalizeFilePath` and `normalizeEphemeralMapKey`). | Preserves exact v1 shim parity (`read-bridge.ts:137-139`, `mutation-bridge.ts:322`), preserves original caller display paths, and prevents divergent path keys. |
189
+ | **Negative Path** | Invariant Conflict (§5) | `mutate: { kind: "delete" }` combined with `read`. | Contradictory state: deleting a file while binding a read on it creates a zombie read record on an absent file. | Validation strictly rejects `mutate.kind === "delete" && read !== undefined` with `{ accepted: false, reason: "malformed" }` for both facets. | Preserves invariant closure: an absent file has no read coverage. |
190
+ | **Negative Path** | Contiguous Span Bounds (§5) | `content` passed with `ranges.length !== 1`. | Unclear line mapping if multiple disjoint ranges are passed with one text buffer. | If `content !== undefined`, require `ranges.length === 1`. If `ranges.length === 0`, `content` must be omitted or `""`. Else reject with `"malformed"`. | Enforces D7 deterministically; eliminates semantic ambiguity. |
191
+ | **Negative Path** | Disk Failure in "disk" mode (§5) | `evidence: "disk"` on unreadable or deleted file. | Synchronous `fs.readFileSync` could throw an uncaught exception, crashing the host. | Disk reads in `"disk"` mode wrapped in try/catch. On failure, return `read: { accepted: false, reason: "bookkeeping-error" }`. | Preserves D9 (never throws). |
192
+ | **Negative Path** | Delete Confirmation & Seam (F3) | Fabricating a change-log receipt, omitting enclosing flag gates (!no-read-guard, !no-lsp), or misordering confirmation gates. | Emitting LSP notifications when no-lsp is active (regression against v1 tool-result), or leaving zombie records in readGuard.reads when files are deleted on disk. | Wrapped by the enclosing `!getFlag("no-read-guard")` gate, followed by the 4 confirmed-delete gates in production order, shared with the native bash path (`judgeConfirmedDelete`, `clients/confirmed-delete.ts`): (1) `isExternalOrVendorFile` -> `"out-of-scope"`; (2) `isPathIgnoredByProject` -> `"ignored"`; (3) `hasKnownPath`: an untracked path that is absent returns `{ accepted: true }` without notifying LSP, and one still on disk returns `bookkeeping-error`; (4) `existsSync` confirm: a tracked file still on disk returns `{ accepted: false, reason: "bookkeeping-error" }` (v2 refinement; the bash path skips it). Action: `forgetPath(filePath)` and, unless `no-lsp`, `notifyExternalFileChange(filePath, 3)`; `no-lsp` suppresses only the notification. Bypasses `recordWritten` and emits NO change-log receipt. | Aligns with tool-result delete lifecycle and flags; refines disk-still-exists into explicit bookkeeping-error; closes zombie-record leaks without fabricated receipts. |
193
+ | **State & Timing** | Lineage / Branch Fence (§3, §5) | Delayed write arriving after session `/tree` or shutdown. | Stale write accepted across epoch boundary, corrupting branch turn state (#3620). | `mutate` validates `runtime.readGuard.currentBranchEpoch`. If branch changed or lineage retired, call `recordDroppedRead` (`clients/session-scope.ts:281-301`, emitting `session-scope-read-dropped` at `clients/session-scope.ts:297`) and return `mutate: { accepted: false, reason: "stale-lineage" }`. If epoch is malformed, call `recordDegradationOnce` (`clients/mutation-bridge.ts:264`, emitting `mutation-bridge-invalid-branch-epoch`). | Preserves TLA+ `session-lifecycle` model and degradation accounting. |
194
+ | **Boundaries** | Flag & Scope Gating (§4) | Collapsing `isRecordable` into a single combined check. | If `no-read-guard` is set, a single gate drops mutation turn-state and change logs (#2465). | Gating is strictly per-facet: `read` gates on `!noReadGuard && isRecordable`; `mutate` gates on `isRecordable`; read-guard write stamp gates on `!noReadGuard`. | Preserves independent accountability of turn-state vs read-guard. |
195
+ | **Retirement** | Legacy Shims & First-Wins (§8) | Old extensions calling `read-bridge` or `mutation-bridge`. | In fresh processes, mounts v1 shims delegating to `ioBridge.record()`. If a dirty process already mounted legacy v1, first-wins retains incumbent. | Mount shims for `Symbol.for("pi-lens:read-bridge")` and `Symbol.for("pi-lens:mutation-bridge")` delegating to `ioBridge.record()`. Document first-wins behavior. | Zero-friction backward compatibility. |
196
+ | **Observability** | Pull-Only Observability (Shape #31, F5) | Unobservable drops or citing non-existent degradation IDs. | Blind spots in monitors and analyzers; silent drops violate Shape #31. | State exact sinks with literal citations: (1) Read log provenance: v1 shim sets `source: "bridge:<consumer>"` (`clients/read-bridge.ts:207`); v2 native sets `source: "io-bridge:<consumer>"` (`[NEW]` in `clients/io-bridge.ts:NEW`, matching `ReadRecord.source` type at `clients/read-guard.ts:55`), written to `read-guard.log`; (2) `turn-state.json` via `deps.getCacheManager().addModifiedRange` (`clients/mutation-bridge.ts:382`); (3) Change-log receipt via `runtime.recordProjectMutation` (`clients/mutation-bridge.ts:393`, edit/write only); (4) Stale lineage write: `session-scope-read-dropped` (`clients/session-scope.ts:297`); (5) Foreign epoch: `mutation-bridge-invalid-branch-epoch` (`clients/mutation-bridge.ts:264`); (6) [NEW] Bridge-level drop telemetry: declare NEW `DegradationKind` union members in `clients/degradation-ledger.ts:40+`: `"io-bridge-read-dropped"` and `"io-bridge-mutate-dropped"`. Caller-synchronous outcome is also immediately returned in `RecordResult`. | Total observability across existing and new streams; fully verifiable by automated tests. |
197
+ | **Side Channels** | Event Bus Parity (`pi.events`) | `mutation-bridge.ts:431` calls `runtime.deferMutation` without calling `publishFormatQueued`. | Tools listening on `pi.events` for `pilens:format:queued` never receive notice when mutations are recorded through the bridge, causing unexpected formatting at `agent_settled` to invalidate in-process line leases and anchors mid-session (AGENTS Shape #5; refs Rianico/pi-better-edit#32). | When `mutate` queues deferred format/autofix (`deferAutofix !== false`), `io-bridge` MUST invoke `publishFormatQueued` (`clients/format-events-publish.ts:187`) for newly-queued files. Passing `deferAutofix: false` suppresses both queueing and bus emission. | Closes the side-channel drop between native tool results and bridge mutations; provides deterministic warning before `agent_settled` rewrites. |
198
+
199
+ ---
200
+
201
+ ## 5. AI Engineering & Usability Review
202
+
203
+ ### 1. One-Page Learnability
204
+ The interface is compact, strongly typed, and self-documenting. The four operations (`read`, `edit`, `write`, `delete`) map directly to the developer's mental model.
205
+
206
+ ### 2. Ordered Producer Guide
207
+ Producers follow a clean 3-step lifecycle:
208
+ ```typescript
209
+ // 1. Discover
210
+ const bridge = getIOBridge();
211
+ if (!bridge) return; // Bridge absent or unsupported version
212
+
213
+ // 2. Formulate Entry (e.g. Hashline Edit + Preview)
214
+ const result = bridge.record({
215
+ filePath: "src/app.ts", // relative or absolute string preserved
216
+ consumer: "my-tool",
217
+ mutate: {
218
+ kind: "edit",
219
+ ranges: [[12, 15]],
220
+ },
221
+ read: {
222
+ ranges: [[8, 25]],
223
+ content: previewText, // raw lines 8-25
224
+ },
225
+ });
226
+
227
+ // 3. Inspect Result
228
+ if (!result.mutate?.accepted) countDrop("mutate", result.mutate?.reason);
229
+ if (!result.read?.accepted) countDrop("read", result.read?.reason);
230
+ ```
231
+
232
+ ### 3. D5 Evidence Disambiguation
233
+ - **Coverage-Only (Default)**: Passing `read: { ranges: [[10, 20]] }` without `content` or `lineHashes` credits lines 10–20 in `ReadGuard` coverage math, but does **not** read disk or bind line hashes. If lines 10–20 are modified externally, `FileTime` flags staleness and blocks the next edit.
234
+ - **In-Memory Hash Evidence**: Passing `content` (or `lineHashes`) hashes the text in memory via `deliveredLineEvidence(text, offset)` and populates `lineHashes`. If the file is modified externally but lines 10–20 remain intact, `canIgnoreStalenessByHashes` allows the next edit without blocking.
235
+ - **Disk Evidence (`evidence: "disk"`)**: Explicitly requests pi-lens to read the file from disk and compute line hashes.
236
+
237
+ ### 4. Full-File Read vs Content Binding (F7 Clarification)
238
+ For a whole-file read `ranges: [[1, lineCount]]`, passing `content` derives in-memory line hashes for lines $1 \dots lineCount$. It does **not** synthesize a whole-file `contentBinding` (which binds `mtime` and `sha256` from disk). In `evidence: "caller"` mode, `contentBinding` is omitted, cleanly avoiding the #3652 false-block hazard.
239
+
240
+ ### 5. Producer Footguns & Safeguards
241
+ - **Footgun: 0-indexed line numbers**: Bridge requires 1-indexed line numbers (`[1, 10]`). Ranges with $start < 1$ or $start > end$ are deterministically rejected as `"malformed"`.
242
+ - **Footgun: Diff markers in `content`**: If a tool passes diff output (`+const x = 1`) instead of raw file lines, line hashes will not match on-disk bytes. Docs prominently warn: *`content` must be raw file text, never diff hunks*.
243
+ - **Footgun: Delete followed by edit**: Deleting a file clears read coverage. Re-creating a file must be submitted as `mutate: { kind: "write" }`.
244
+ - **Footgun: Deleting a file that still exists on disk**: If a caller calls `mutate: { kind: "delete" }` before removing the file from the filesystem, `nodeFs.existsSync(filePath)` fails the confirm gate and returns `{ accepted: false, reason: "bookkeeping-error" }`. The on-disk deletion must precede or occur synchronously with the bridge call.
245
+
246
+ ---
247
+
248
+ ## 6. Verification Plan
249
+
250
+ ### A. Unit Tests (`tests/clients/io-bridge.test.ts`)
251
+ - **Facet Validation Matrix**: Exhaustive testing of invalid inputs returning `"malformed"`:
252
+ - Empty path string `""`.
253
+ - Zero-indexed ranges (`[0, 10]`), inverted ranges (`[10, 5]`), non-integer ranges.
254
+ - Multi-range with `content` (`ranges: [[1, 5], [10, 15]]` + `content`).
255
+ - `delete` combined with `read`.
256
+ - `lineHashes` with out-of-range keys (e.g. key `30` when range is `[1, 10]`).
257
+ - **Path Parity & Display Preservation Matrix (F1)**:
258
+ - Red-before-green witness: pass a relative path (`"src/app.ts"`) through:
259
+ 1. v1 shim `recordRead`: verify `deps.isRecordable("src/app.ts")` called; verify `ReadRecord.filePath === "src/app.ts"`; verify `ReadGuard.key("src/app.ts")` normalization.
260
+ 2. v1 shim `recordMutation`: verify `deps.isRecordable("src/app.ts")` called; verify `addModifiedRange` called with `"src/app.ts"`.
261
+ 3. v2 bridge `record`: verify identical outcomes, with display path preserved verbatim and no clobbering.
262
+ - **Scope & Flag Gating Matrix (#2465)**:
263
+ - Out-of-project paths return `{ accepted: false, reason: "out-of-scope" }`.
264
+ - `no-read-guard` flag active: `read` returns `{ accepted: false, reason: "no-read-guard" }`; `mutate` returns `{ accepted: true }` but skips `ReadGuard.recordWritten`.
265
+ - **Delete Seam & Flag Gates Matrix (F3)**:
266
+ - Enclosing flag gates:
267
+ - `no-read-guard` active: skips read-guard eviction, returns `{ accepted: false, reason: "no-read-guard" }`.
268
+ - `no-lsp` active: eviction `readGuard.forgetPath` runs, but `notifyExternalFileChange(filePath, 3)` is suppressed.
269
+ - 4 inner gates in production order (`judgeConfirmedDelete`, `clients/confirmed-delete.ts`, shared with the native bash path):
270
+ 1. External / vendor path (`isExternalOrVendorFile`): returns `{ accepted: false, reason: "out-of-scope" }`.
271
+ 2. Ignored path (`isPathIgnoredByProject`): returns `{ accepted: false, reason: "ignored" }`.
272
+ 3. Untracked path (`!hasKnownPath`): if file absent, returns `{ accepted: true }`; zero LSP notifications sent. If it is still on disk, returns `{ accepted: false, reason: "bookkeeping-error" }`.
273
+ 4. Disk confirm (`nodeFs.existsSync`): if file still exists on disk, returns `{ accepted: false, reason: "bookkeeping-error" }`.
274
+ - Confirmed delete on tracked path: calls `forgetPath(filePath)` and `notifyExternalFileChange(filePath, 3)`. Asserts NO `recordProjectMutation` or `addModifiedRange` invoked.
275
+ - **Observability & Degradation Ledger Matrix (F5)**:
276
+ - Telemetry witness: trigger bridge drop on read and mutate; flush `latency.log` (via `getGlobalPiLensLogDir()` sink) and read back records, asserting:
277
+ - `kind: "io-bridge-read-dropped"`, `subject: "${consumer}:${reason}"`.
278
+ - `kind: "io-bridge-mutate-dropped"`, `subject: "${consumer}:${reason}"`.
279
+ - Stale lineage write: asserts emission of `session-scope-read-dropped` (`clients/session-scope.ts:297`).
280
+ - Malformed branch epoch: asserts emission of `mutation-bridge-invalid-branch-epoch` (`clients/mutation-bridge.ts:264`).
281
+
282
+ ### B. Seam Integration Probes & Red-Before-Green Witnesses
283
+
284
+ #### 1. Witness for Write-Before-Read Atomic Ordering
285
+ - **Production Seam**: `ReadGuard.checkEdit` -> `ioBridge.record(...)` -> `ReadGuard.checkEdit`.
286
+ - **Probe Scenario**: An edit occurs to a file that was previously read. The producer submits a compound entry:
287
+ ```typescript
288
+ ioBridge.record({
289
+ filePath,
290
+ mutate: { kind: "edit", ranges: [[10, 15]] },
291
+ read: { ranges: [[5, 25]], content: postMutationContext }
292
+ });
293
+ ```
294
+ - **Red Witness (Order Inversion)**: The order does not change an immediate `checkEdit`: the #4145 probe found the same verdict for every order, and there is no `file_time_stale` reason. What it changes is `consumedReadFiles`. `recordWritten` marks a file's existing reads consumed, and `recordRead` makes them outstanding again. Read-then-mutate therefore leaves the preview read consumed, and a consumed read is evictable on idle and under the file cap: after the idle window the read is gone and the next edit blocks as never read.
295
+ - **Green Witness**: With `mutate` running before `read`, the preview read is the last word and stays outstanding, so idle eviction cannot drop it. `tests/clients/io-bridge.test.ts` "keeps the compound call's preview read outstanding through idle eviction" pins this; swapping the facets reds it (`expected [] to have a length of 1`).
296
+
297
+ #### 2. Witness for D5 Coverage-Only Default
298
+ - **Production Seam**: `ioBridge.record` -> `readGuard.reads.get(filePath)`.
299
+ - **Probe Scenario**: Record a read with `read: { ranges: [[1, 10]] }` without `content` or `lineHashes`.
300
+ - **Red Witness (Unwanted Disk Hashing)**: If the bridge silently read disk, `storedRecord.lineHashes` would be populated.
301
+ - **Green Witness**: `storedRecord.lineHashes` is strictly `undefined`. Zero disk reads occur.
302
+
303
+ #### 3. Witness for Delete Lifecycle Eviction (F3)
304
+ - **Production Seam**: `ioBridge.record` -> `readGuard.hasKnownPath` + `notifyExternalFileChange`.
305
+ - **Probe Scenario**: A file with existing read records is deleted via `mutate: { kind: "delete" }` after on-disk removal.
306
+ - **Witness**: `readGuard.hasKnownPath(filePath) === false`, `readGuard.reads.has(filePath) === false`, `writtenThisSession.has(filePath) === false`, and mock LSP receives FileChangeType 3. Asserts NO change log receipt was appended.
307
+
308
+ ---
309
+
310
+ ## 7. Issues & Status
311
+
312
+ - **Open Defects / Ambiguities**: **None**.
313
+ - **F1 (Path Resolution & Display Parity)**: Fully resolved. `filePath` non-empty string preserved verbatim without re-resolving or clobbering at bridge layer; unit test matrix verifies v1 parity.
314
+ - **F3 (Delete Seam & Flag Gates)**: Fully resolved. The enclosing `!getFlag("no-read-guard")` gate, then the 4 confirmed-delete gates shared with the native bash path (`clients/confirmed-delete.ts`), followed by `forgetPath` and, unless `no-lsp`, `notifyExternalFileChange`. Dropped fabricated change-log receipt.
315
+ - **F5 (Observability & Exact Citations)**: Fully resolved. Read log provenance uses `bridge:<consumer>` for v1 shim (`clients/read-bridge.ts:207`) and `io-bridge:<consumer>` as `[NEW]` for v2 native (`ReadRecord.source` type at `clients/read-guard.ts:55`). Re-uses existing `session-scope-read-dropped` (`clients/session-scope.ts:297`) and `mutation-bridge-invalid-branch-epoch` (`clients/mutation-bridge.ts:264`); explicitly declares `[NEW]` `DegradationKind` union members `"io-bridge-read-dropped"` and `"io-bridge-mutate-dropped"` in `clients/degradation-ledger.ts:40+` with test reading `latency.log`.
316
+ - **Maintainer Decisions**: the maintainer accepted this design on #4145 (2026-10-08): the D5/D6 evidence model, the public field set, relative paths out of scope, and a tracked `docs/rfcs/`.