@herbertgao/pi-extensions 2026.9.14 → 2026.9.16

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 (206) hide show
  1. package/README.md +8 -3
  2. package/THIRD_PARTY_NOTICES.md +28 -2
  3. package/node_modules/@herbertgao/pi-cc-extensions/package.json +1 -1
  4. package/node_modules/@narumitw/pi-btw/dist/index.ts +12 -6
  5. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +3 -3
  6. package/node_modules/@narumitw/pi-btw/package.json +4 -4
  7. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +2 -0
  8. package/node_modules/@narumitw/pi-btw/src/side-thread.ts +16 -4
  9. package/node_modules/pi-jev-auto-mode/CHANGELOG.md +8 -0
  10. package/node_modules/pi-jev-auto-mode/README.md +7 -0
  11. package/node_modules/pi-jev-auto-mode/package.json +1 -1
  12. package/node_modules/pi-jev-auto-mode/src/extension.ts +21 -2
  13. package/node_modules/pi-jev-auto-mode/src/records.ts +68 -2
  14. package/node_modules/pi-jev-auto-mode/src/settings.ts +22 -0
  15. package/node_modules/pi-jev-auto-mode/src/ui.ts +10 -0
  16. package/node_modules/pi-lens/CHANGELOG.md +498 -0
  17. package/node_modules/pi-lens/README.md +45 -2
  18. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +23 -7
  19. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +187 -51
  20. package/node_modules/pi-lens/dist/clients/cache-manager.js +64 -5
  21. package/node_modules/pi-lens/dist/clients/child-unref.js +17 -8
  22. package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +2 -0
  23. package/node_modules/pi-lens/dist/clients/config-locations.js +237 -9
  24. package/node_modules/pi-lens/dist/clients/config-resolve.js +7 -1
  25. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +33 -3
  26. package/node_modules/pi-lens/dist/clients/dependency-checker.js +87 -64
  27. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +39 -3
  28. package/node_modules/pi-lens/dist/clients/dispatch/finding-policy.js +118 -1
  29. package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +8 -2
  30. package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +23 -4
  31. package/node_modules/pi-lens/dist/clients/dispatch/rule-ignores.js +14 -7
  32. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +26 -28
  33. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +38 -25
  34. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +32 -49
  35. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +16 -14
  36. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +25 -7
  37. package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +28 -47
  38. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +11 -4
  39. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +27 -22
  40. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +31 -46
  41. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +29 -46
  42. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +91 -11
  43. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +16 -2
  44. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +38 -23
  45. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +27 -15
  46. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +17 -8
  47. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +31 -21
  48. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +34 -60
  49. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +6 -5
  50. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +12 -5
  51. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +2 -1
  52. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +22 -6
  53. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +39 -16
  54. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +1 -1
  55. package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +27 -18
  56. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +18 -19
  57. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +6 -1
  58. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +6 -2
  59. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +10 -3
  60. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +11 -2
  61. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +10 -2
  62. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +11 -2
  63. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +12 -6
  64. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +30 -15
  65. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +23 -0
  66. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +8 -2
  67. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +11 -1
  68. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +15 -3
  69. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +12 -2
  70. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +27 -39
  71. package/node_modules/pi-lens/dist/clients/dispatch/suppress-writer.js +13 -3
  72. package/node_modules/pi-lens/dist/clients/effective-config.js +3 -1
  73. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +42 -11
  74. package/node_modules/pi-lens/dist/clients/formatters.js +49 -11
  75. package/node_modules/pi-lens/dist/clients/git-guard.js +247 -34
  76. package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +121 -25
  77. package/node_modules/pi-lens/dist/clients/inline-blocker-dispositions.js +186 -0
  78. package/node_modules/pi-lens/dist/clients/install-diagnostics.js +19 -6
  79. package/node_modules/pi-lens/dist/clients/installer/index.js +281 -32
  80. package/node_modules/pi-lens/dist/clients/jscpd-client.js +8 -0
  81. package/node_modules/pi-lens/dist/clients/latency-logger.js +8 -0
  82. package/node_modules/pi-lens/dist/clients/lens-config.js +66 -8
  83. package/node_modules/pi-lens/dist/clients/lsp/client.js +139 -11
  84. package/node_modules/pi-lens/dist/clients/lsp/config.js +9 -7
  85. package/node_modules/pi-lens/dist/clients/lsp/content-limits.js +41 -0
  86. package/node_modules/pi-lens/dist/clients/lsp/index.js +1 -1
  87. package/node_modules/pi-lens/dist/clients/lsp/launch.js +23 -2
  88. package/node_modules/pi-lens/dist/clients/lsp/server.js +106 -25
  89. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +14 -0
  90. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +105 -16
  91. package/node_modules/pi-lens/dist/clients/mcp/ipc.js +232 -44
  92. package/node_modules/pi-lens/dist/clients/mcp/review.js +53 -7
  93. package/node_modules/pi-lens/dist/clients/opengrep-client.js +11 -6
  94. package/node_modules/pi-lens/dist/clients/pipeline.js +121 -66
  95. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +21 -3
  96. package/node_modules/pi-lens/dist/clients/project-lens-config.js +11 -3
  97. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +34 -26
  98. package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +16 -10
  99. package/node_modules/pi-lens/dist/clients/ruff-client.js +31 -4
  100. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +38 -4
  101. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +16 -5
  102. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +27 -12
  103. package/node_modules/pi-lens/dist/clients/runtime-turn.js +447 -280
  104. package/node_modules/pi-lens/dist/clients/safe-spawn.js +256 -65
  105. package/node_modules/pi-lens/dist/clients/spawn-output-cap.js +84 -1
  106. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +119 -58
  107. package/node_modules/pi-lens/dist/clients/turn-end/lane.js +51 -0
  108. package/node_modules/pi-lens/dist/clients/turn-end/lanes/govulncheck.js +112 -0
  109. package/node_modules/pi-lens/dist/clients/turn-end/lanes/secrets.js +204 -0
  110. package/node_modules/pi-lens/dist/clients/warm-attach.js +59 -0
  111. package/node_modules/pi-lens/dist/clients/widget-state.js +145 -14
  112. package/node_modules/pi-lens/dist/clients/workspace-topology.js +57 -3
  113. package/node_modules/pi-lens/dist/clients/zizmor-config.js +26 -13
  114. package/node_modules/pi-lens/dist/index.js +5695 -3733
  115. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +4 -0
  116. package/node_modules/pi-lens/dist/mcp/server.js +33 -21
  117. package/node_modules/pi-lens/dist/scripts/lib/web-tree-sitter-dir.mjs +123 -0
  118. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +43 -16
  119. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +118 -7
  120. package/node_modules/pi-lens/docs/adr/0001-stale-advisory-live-arm.md +29 -0
  121. package/node_modules/pi-lens/docs/adr/0002-workspace-hash-rendezvous.md +30 -0
  122. package/node_modules/pi-lens/docs/adr/0003-git-guard-latch-writer.md +203 -0
  123. package/node_modules/pi-lens/docs/adr/0004-disposition-policy-seam.md +38 -0
  124. package/node_modules/pi-lens/docs/adr/0005-tool-availability-enforcement-seam.md +29 -0
  125. package/node_modules/pi-lens/docs/adr/0006-derived-state-benchmark-first.md +31 -0
  126. package/node_modules/pi-lens/docs/adr/0007-end-to-end-witness-per-seam-slice.md +38 -0
  127. package/node_modules/pi-lens/docs/adr/0008-turn-end-lane-interface.md +165 -0
  128. package/node_modules/pi-lens/docs/adr/0009-reported-path-attribution.md +114 -0
  129. package/node_modules/pi-lens/docs/adr/README.md +26 -0
  130. package/node_modules/pi-lens/docs/agent-tools.md +18 -1
  131. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +4 -3
  132. package/node_modules/pi-lens/docs/configuration.md +55 -1
  133. package/node_modules/pi-lens/docs/engineering-principles.md +202 -0
  134. package/node_modules/pi-lens/docs/environment-variables.md +61 -4
  135. package/node_modules/pi-lens/docs/features.md +2 -2
  136. package/node_modules/pi-lens/docs/globalconfig.md +6 -1
  137. package/node_modules/pi-lens/docs/language-coverage.md +17 -0
  138. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +98 -31
  139. package/node_modules/pi-lens/docs/pi-lens-fixer.md +57 -3
  140. package/node_modules/pi-lens/docs/pi-lens-investigator.md +9 -0
  141. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +16 -0
  142. package/node_modules/pi-lens/docs/pi-lens-subagent.md +9 -0
  143. package/node_modules/pi-lens/docs/public-api-stability.md +1 -0
  144. package/node_modules/pi-lens/docs/release-qa-baseline.md +2 -0
  145. package/node_modules/pi-lens/docs/servercapabilities.md +21 -11
  146. package/node_modules/pi-lens/docs/settings.md +12 -3
  147. package/node_modules/pi-lens/docs/subagent-compat.md +1 -1
  148. package/node_modules/pi-lens/package.json +5 -3
  149. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-assert-tuple-test.yml +13 -0
  150. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-boolean-in-except-test.yml +26 -0
  151. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-http-headers-bracket-access-test.yml +10 -0
  152. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/unchecked-numeric-parse-python-test.yml +18 -0
  153. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/unchecked-throwing-call-python-test.yml +4 -3
  154. package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-fmt-println.yml +2 -2
  155. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-assert-tuple.yml +23 -13
  156. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-boolean-in-except.yml +7 -3
  157. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-console-except-error-js.yml +2 -2
  158. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-console-except-error.yml +2 -2
  159. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-http-headers-bracket-access.yml +12 -7
  160. package/node_modules/pi-lens/rules/ast-grep-rules/rules/unchecked-numeric-parse-python.yml +44 -0
  161. package/node_modules/pi-lens/rules/ast-grep-rules/rules/unchecked-throwing-call-python.yml +5 -3
  162. package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/console-statement.yml +2 -2
  163. package/node_modules/pi-lens/scripts/install-selftest.mjs +21 -17
  164. package/node_modules/pi-lens/scripts/lib/web-tree-sitter-dir.mjs +123 -0
  165. package/node_modules/pi-mcp-adapter/CHANGELOG.md +29 -0
  166. package/node_modules/pi-mcp-adapter/OAUTH.md +369 -0
  167. package/node_modules/pi-mcp-adapter/README.md +12 -5
  168. package/node_modules/pi-mcp-adapter/config.ts +31 -8
  169. package/node_modules/pi-mcp-adapter/direct-tool-surface.ts +1 -1
  170. package/node_modules/pi-mcp-adapter/dist/config.js +33 -8
  171. package/node_modules/pi-mcp-adapter/dist/config.js.map +1 -1
  172. package/node_modules/pi-mcp-adapter/dist/json-schema-validator.js +15 -2
  173. package/node_modules/pi-mcp-adapter/dist/json-schema-validator.js.map +1 -1
  174. package/node_modules/pi-mcp-adapter/dist/mcp-auth.d.ts +2 -0
  175. package/node_modules/pi-mcp-adapter/dist/mcp-auth.js +27 -2
  176. package/node_modules/pi-mcp-adapter/dist/mcp-auth.js.map +1 -1
  177. package/node_modules/pi-mcp-adapter/dist/metadata-cache.js +1 -1
  178. package/node_modules/pi-mcp-adapter/dist/metadata-cache.js.map +1 -1
  179. package/node_modules/pi-mcp-adapter/dist/server-manager.js +6 -4
  180. package/node_modules/pi-mcp-adapter/dist/server-manager.js.map +1 -1
  181. package/node_modules/pi-mcp-adapter/dist/types.d.ts +2 -2
  182. package/node_modules/pi-mcp-adapter/dist/utils.d.ts +2 -0
  183. package/node_modules/pi-mcp-adapter/dist/utils.js +10 -3
  184. package/node_modules/pi-mcp-adapter/dist/utils.js.map +1 -1
  185. package/node_modules/pi-mcp-adapter/index.ts +37 -20
  186. package/node_modules/pi-mcp-adapter/init.ts +14 -15
  187. package/node_modules/pi-mcp-adapter/json-schema-validator.ts +16 -2
  188. package/node_modules/pi-mcp-adapter/mcp-auth.ts +27 -2
  189. package/node_modules/pi-mcp-adapter/metadata-cache.ts +1 -1
  190. package/node_modules/pi-mcp-adapter/package.json +3 -2
  191. package/node_modules/pi-mcp-adapter/proxy-modes.ts +1 -1
  192. package/node_modules/pi-mcp-adapter/server-manager.ts +6 -3
  193. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +99 -6
  194. package/node_modules/pi-mcp-adapter/types.ts +2 -2
  195. package/node_modules/pi-mcp-adapter/ui-session.ts +60 -27
  196. package/node_modules/pi-mcp-adapter/utils.ts +11 -3
  197. package/node_modules/pi-multi-account/CHANGELOG.md +6 -0
  198. package/node_modules/pi-multi-account/README.md +15 -1
  199. package/node_modules/pi-multi-account/index.ts +295 -41
  200. package/node_modules/pi-multi-account/package.json +1 -1
  201. package/node_modules/pi-next-cue/.github/workflows/publish.yml +20 -0
  202. package/node_modules/pi-next-cue/LICENSE +21 -0
  203. package/node_modules/pi-next-cue/README.md +81 -0
  204. package/node_modules/pi-next-cue/index.ts +358 -0
  205. package/node_modules/pi-next-cue/package.json +28 -0
  206. package/package.json +10 -7
@@ -16,6 +16,504 @@ All notable changes to pi-lens will be documented in this file.
16
16
 
17
17
  ### Security
18
18
 
19
+ ## [4.3.0] - 2026-09-25
20
+
21
+ ### Added
22
+
23
+ - Read SonarCloud's master quality gate and open vulnerabilities in the nightly tool-smoke workflow, failing loudly for quality errors while treating SonarCloud outages as visible, fail-open warnings (closes #3319).
24
+
25
+ - **Report shadowed global config files (closes #3299)** — When both supported global config files exist, pi-lens keeps the legacy winner and records the shadowed agent-dir path once per session.
26
+
27
+ - **Read (and edit) the global config from the host's config dir (refs #2457)** — When `PI_CODING_AGENT_DIR` is set, `$PI_CODING_AGENT_DIR/extensions/pi-lens.json` is read when it exists and the legacy `~/.pi-lens/config.json` does not; a file at the legacy default keeps winning while it exists, so current users never move. Settings editors resolve through the same seam and create in the agent-dir location when NEITHER file exists ("edit the one that exists; prefer the new one if neither does"). The canonical default and `PI_LENS_CONFIG_PATH` semantics are unchanged.
28
+
29
+ - Added an advisory CI lane that runs the existing capped targeted-test selector on every pull request and publishes its selection result in the step summary (refs #3215).
30
+
31
+ - **Durable CI test history.** Record bounded per-file Vitest results for nightly flake analysis.
32
+
33
+ ### Changed
34
+
35
+ - Folded three hand-rolled path-key transformations in the LSP server, LSP
36
+ launch and tool-call seams onto `clients/path-utils.ts` and added a
37
+ comment- and string-blanked source sweep that pins every remaining raw `\` → `/` fold and
38
+ path-key case fold per file, so a new copy fails CI (refs #1193). The PATH
39
+ entry dedupe key now parses with `path.win32`/`path.posix` chosen from its own
40
+ `platform` argument instead of the host default, so two spellings of one
41
+ Windows directory dedupe on every lane.
42
+
43
+ - Extracted the turn-end govulncheck delivery (the 🛡️ Go CVE advisory tier) out
44
+ of `clients/runtime-turn.ts` into `clients/turn-end/lanes/govulncheck.ts`
45
+ behind the same `TurnEndLane` interface as the secrets lane, and added the
46
+ advisory tier to `TurnEndLaneParts` with it — the first lane that renders one,
47
+ so the field, the composer's tagged push and the registered surface arrive
48
+ together rather than as a slot nothing reads (refs #1892, ADR 0008
49
+ amendment). The six rules the block stated inline — the `onMissing: "demote"`
50
+ first-trace-frame freshness declaration, the disposition anchor over both
51
+ freshness arms, the withheld stale line and its marker, the module/package
52
+ fallback, the upgrade hint and the display cap — now live once, in the lane.
53
+ Agent-facing text is byte-identical: both committed witness goldens are
54
+ unchanged and a third pins this lane's live rows, demoted rows, deleted
55
+ call-site demotion, module fallback, cap and suppression notice through the
56
+ real pi host entry.
57
+
58
+ - Extracted the turn-end secrets delivery (the gitleaks and trivy-secrets
59
+ blocker and demoted tiers) out of `clients/runtime-turn.ts` into
60
+ `clients/turn-end/lanes/secrets.ts` behind one `TurnEndLane` interface —
61
+ collect the stores, gate them with the composer's shared freshness pass, then
62
+ render — so the ten rendering and disposition rules that block used to state
63
+ inline now live once, in the lane, and the composer states none of them
64
+ (refs #1892, ADR 0008). Agent-facing text is byte-identical: the committed
65
+ witness golden for the scanner lanes is unchanged and a second golden pins the
66
+ lane's own live tier, demoted tier, combined ast-grep provenance and
67
+ suppressed-by-disposition notice through the real pi host entry.
68
+
69
+ - **Type-aware async and optional-chain checks now lint the test population (refs #3244)** — `npm run lint` now runs the pinned type-aware test lane, using a stable Unix formatter compatible with the repository's oxlint-tsgolint toolchain.
70
+
71
+ - Restrict tool-smoke drift ticket side effects to scheduled and default-branch runs while preserving branch-dispatch reports.
72
+
73
+ - Internal-only maintenance: extend the socket listener sweep to reusable test support fixtures and remove an inert MCP socket teardown call.
74
+
75
+ - Reuse the shared directory-mtime freshness predicate for project-config discovery.
76
+
77
+ - **Mechanise train lessons (refs #3426)** — pre-commit now checks staged files with the pinned formatter, and pre-push selects production tree-scanning governance suites. Real-spawn suites that exceed the pre-push budget move to a reasoned CI-only tier, and a mechanical tree-scanner census fails any unregistered scanner rather than trusting a hand list.
78
+
79
+ - **Close the pre-4.3.0 docs gap audit (closes #3431)** — document the LSP
80
+ sync content bound (2 MiB / 5000 lines) and the new `textDocument/didSave`
81
+ behaviour in `docs/agent-tools.md`; add the missing `pilens_session_end`
82
+ row and the review-graph/config-core nodes to the README architecture
83
+ diagram; correct the "a dozen-plus language servers" figure to 46; index
84
+ ADR 0008; document the `PILENS_PROBE`/`PILENS_UNSAFE_FORCE_GRAMMAR_LOAD`/
85
+ `PILENS_PUB_DEBUG` diagnostic env vars and widen the recovery grep; bring
86
+ the pre-commit hook description and the contributor path (guard-bash.mjs,
87
+ `PI_LENS_TEST_MAX_WORKERS`, the mutation lane, release-QA) up to date; and
88
+ rename the retired `lsp_diagnostics` tool name to `lens_diagnostics` in two
89
+ earlier changelog fragments.
90
+
91
+ - Make nightly LSP capability documentation deterministic and skip byte-identical refresh PRs.
92
+
93
+ - **Glossary, witness, and seam contracts (refs #3259, refs #3260)** — Document the shared pi-lens vocabulary and seven accepted architecture decisions, add the end-to-end witness ADR and adapter heuristic, require the fixer boundaries and PR-body structure, and make the `Why` section's one-sentence contract executable.
94
+
95
+ - **Docs: require exact-pin sweeps on the merge with master (refs #3259, refs #3260)** — Fixer, reviewer, and merge-train contracts now require glossary synonym sweeps on both the PR head and the merge with `origin/master`, with same-PR re-pinning from the sweep's `UNPINNED`/`STALE` evidence.
96
+
97
+ - **Trigger-based agent guidance (refs #3259)** — Group the recurring defect
98
+ catalog and conditional repository procedures under explicit `<important if>`
99
+ triggers, require investigator slices to verify already-shipped work, and
100
+ enforce standalone paired markers plus Markdown-comment- and string-blanked
101
+ catalog scanning so hidden counterfeits cannot satisfy the 52-shape contract.
102
+
103
+ - **Docs contract pre-flight and reviewer/fixer mechanics (refs #3260)** —
104
+ Record the merge-train pre-flight rows, reviewer continuity rule, and
105
+ evidence-pass mechanics harvested from the 2026-09-22/23 train.
106
+
107
+ - Docs: the winning global-config-location table (every combination of `PI_LENS_CONFIG_PATH`, the legacy file and the agent-dir file), the probe-error tier note, and the XDG-like setup recipe with the migration order (closes #3250).
108
+
109
+ - AGENTS.md points at the engineering-principles repository first, and a vendored copy of PRINCIPLES.md lives at docs/engineering-principles.md for readers without a global instruction file (regenerated by that repo's sync script).
110
+
111
+ - The three cached scanner lanes turn_end reads — gitleaks, trivy secrets and
112
+ govulncheck — now share ONE cited-path freshness pass instead of calling the
113
+ gate once each (refs #1892). A file both secret scanners flag was stat'd
114
+ twice and wrote two `finding_stale_line_demote` rows for one decision about
115
+ one file; it is now one stat and one bounded record per delivery, carrying a
116
+ `byStore` breakdown so the row still says which store's findings were
117
+ retired. The stat allowance stays per store — a shared pool would let one
118
+ lane spend it and leave another re-rendering an edited cited line as current
119
+ — and a delivery that exhausts one now writes a bounded
120
+ `finding_path_stat_budget_exhausted` row naming every store affected, which
121
+ nothing disclosed before. Source identity survives the fold: each store's own `scannedAt`
122
+ decides its own findings' staleness, and each store's own missing-path policy
123
+ decides its own findings' deletion, so a govulncheck CVE still survives a
124
+ deleted call site on the same path that drops a gitleaks secret. Scanner
125
+ cache records written by 4.2.1 parse and render unchanged.
126
+
127
+ - **Pin the glossary synonym-retirement inventory (refs #3259)** — A
128
+ comment- and string-blanked governance sweep now holds the per-file retired
129
+ identifier population to exact equality across the runtime TypeScript
130
+ sources, with distinct failures for an unpinned new use and for a stale pin,
131
+ and it pins the canonical-collision exclusions with the module that owns each
132
+ canonical sense.
133
+
134
+ - Every `parseToolRun` runner now documents its nonzero exit table, its
135
+ documented `ran` codes are pinned exactly so neither adding nor removing one
136
+ passes silently, and each documented code needs an executable status fixture
137
+ in the runner's test matrix (refs #3292).
138
+
139
+ - Improve nightly LSP clean-gate fixture coverage for project-backed Vue, Svelte, F#, and Mix workspaces.
140
+
141
+ - Refreshed measured LSP clean-transition markers: cue retains its publish
142
+ signal, while lua-language-server and marksman are recognized as silent on
143
+ clean transitions (refs #3347).
144
+
145
+ - The lint workflow installs yamllint with `--only-binary=:all:`, so pip can never fall back to running a source distribution's setup script on the runner (SonarCloud githubactions:S8541; the only open finding failing the master quality gate since 2026-09-08).
146
+
147
+ - **Release-QA rows for the global config location and the shadowed-global record (refs #2608)** — the matrix now drives a real pi-lens MCP server with `PI_CODING_AGENT_DIR` set and `extensions/pi-lens.json` present and witnesses that the agent-dir file supplies the global tier, and with both global files present witnesses the once-per-session `config-location-shadowed` record (`PILENS_CFG_0010`) holding its count at 1 across repeated config loads.
148
+
149
+ ### Fixed
150
+
151
+ - `elixir-check` now decides whether a compiler diagnostic belongs to the
152
+ edited file through `pathsEqual`, the shared on-disk path identity seam,
153
+ instead of its own win32 case fold. Findings whose reported spelling differs
154
+ from the dispatched one — a lowercase drive letter on Windows, a backslash
155
+ segment anywhere — reach the agent as the blocking error they are instead of
156
+ a generic unparseable-output warning (refs #1193).
157
+
158
+ - **Share actionlint and Credo runner outcome parsing (refs #1816)** — failed
159
+ CLI output now reaches the common empty-result and parsed-nothing gates.
160
+
161
+ - **Share ESLint and golangci-lint runner outcome parsing (refs #1816)** — failed
162
+ JSON output now reaches the common empty-result and parsed-nothing gates.
163
+
164
+ - The marker-root seam's "a nearer project marker created below an
165
+ already-resolved root wins" behaviour is now pinned through the production
166
+ paths that consume it: the real `rust-clippy` runner (so `cargo clippy`
167
+ follows a crate scaffolded by `cargo init crates/engine` mid-session instead
168
+ of staying at the workspace root), the real `BiomeClient` autofix as a
169
+ non-runner consumer, and one derived case over every runner in the marker
170
+ vocabulary baseline. The behaviour itself shipped in #2948; nothing above the
171
+ seam had proven it, and the positive-hit direction was untested (refs #2922).
172
+
173
+ - `lens_diagnostics mode=full` no longer retires retained opengrep findings
174
+ across the whole project when an opengrep scan completed but scanned zero
175
+ files (an empty `paths.scanned`, e.g. no rule language matched the root).
176
+ A coverage producer that declares zero scanned files now has file-level
177
+ authority over nothing instead of falling back to whole-root authority, so
178
+ a finding is only retired when a scan actually covered its file. The
179
+ one-per-session `runner-coverage-empty` ledger row says when that happened
180
+ (refs #2962).
181
+
182
+ - Make the mutation-diff lane actually evaluate mutants: mutate only the diff's changed lines, give instrumented related tests their own timeout, and bound the run below the job cap so a lane that evaluated nothing says so.
183
+
184
+ - **The `suppressed: N` chip counts a mark for as long as the mark stands (closes #3183)** —
185
+ a `false-positive`/`suppress` mark stopped contributing to the pi-lens footer's
186
+ suppressed count at the marked file's next edit, even an edit that left the
187
+ marked line byte-identical and the mark still applying, with no way to get it
188
+ back. The retained row's lifetime is now the mark's own: it stands while the
189
+ marked line is still in the file and is retired when that line changes, the
190
+ file is deleted, a scan reports the finding again, or the per-file retention
191
+ cap truncates it. A second finding of the same rule with the same message no
192
+ longer displaces the marked one, and `lens_diagnostics mode=all` never lists a
193
+ marked finding as a live warning.
194
+
195
+ - A blocking finding whose cited file no longer exists is now retracted from
196
+ every surface, not just the tool result it was rendered into: the durable
197
+ record `pi-lens` re-serves at turn end (`Unresolved from this turn — <path>`)
198
+ and the verdict that latches the `--lens-guard` commit gate are both built
199
+ from the same deleted-path-gated set the per-edit 🔴 STOP block renders. A
200
+ retracted finding no longer comes back a moment later, and no longer blocks
201
+ `git commit` under a summary quoting the pipeline's own all-clear line
202
+ (closes #3190).
203
+
204
+ - **CSS, SCSS, LESS, SASS and Prisma files get LSP diagnostics again (refs #3217)** — pi-lens answered a language server's `workspace/configuration` request with `null` when it had no value for the requested section. `vscode-css-language-server` passes that answer straight into its own lint configuration and threw `Cannot read properties of null (reading 'validProperties')` inside its diagnostics computation, returning an empty report for every css-kind file and surfacing the error only as a `window/logMessage`; `@prisma/language-server` reads `settings.enableDiagnostics` unguarded and its process exited outright. pi-lens now answers an unresolvable section with an empty settings object, which is what "no value for this section" means, and still never leaks the whole configuration blob for a section the server did not ask for (#983). The nightly `Tool smoke` clean gate had reported 0 diagnostics for css on every run since the gate landed; it now reports real findings for both servers, and the gate itself went from 7 opted-in fixtures to 41 with a `gated N / handshake-only M / unavailable K` census line and a governance test that refuses a new fixture carrying neither `lspGate` nor an explicit `lspGateExempt` reason.
205
+
206
+ - Keep the subagent compatibility smoke aligned with pi-subagents' compiled contract source layout.
207
+
208
+ - **Preserve opaque mutation ownership (refs #3226)** — Opaque bash recovery
209
+ still records freshness and runs analysis, but observed paths no longer
210
+ receive read-guard authorship, autonomous format/autofix, or edit-directed
211
+ blocker/actionable delivery. Explicit native and recognized direct bash writes
212
+ retain their configured writer policy. Repeated opaque paths use one counted
213
+ ledger subject with bounded representative evidence instead of retaining one
214
+ once-key per artifact path.
215
+
216
+ - **Nested bundled rule ignores (refs #3240)** — Console and Go rule directory carve-outs now apply at nested package depths without hiding similarly named application paths; the shared matcher now treats POSIX, drive-letter, and UNC paths with segment-aware containment on every host OS.
217
+
218
+ - **Turn-end scanner-cache reads no longer block the hook (refs #3274)** — the
219
+ gitleaks, trivy and govulncheck stores `turn_end` reads once per delivery were
220
+ read synchronously, so a slow or wedged filesystem held the whole hook and no
221
+ deadline or Escape could release it. They now read through
222
+ `CacheManager.readCacheAsync` under the turn_end budget and the hook's abort
223
+ signal; a read that cannot finish inside the budget is abandoned, delivers the
224
+ same output as a cold cache, and is recorded — once per turn, naming the
225
+ stores the turn was composed without — instead of stalling the turn.
226
+
227
+ - `go vet` findings for the file you just edited are no longer dropped when
228
+ go's spelling of that file's path differs from the dispatcher's. The runner
229
+ now decides "is this line about the edited file?" through `pathsEqual`, the
230
+ shared on-disk path identity seam, instead of a bare string compare, so a
231
+ differently-cased path on Windows or a case-folding macOS volume is
232
+ recognised as the same file and the run is no longer reported clean
233
+ (closes #3277, refs #1193).
234
+
235
+ - The go-vet filesystem case-identity regression test is explicitly admitted to
236
+ the Windows Vitest lane, so its real Windows case-folding arm is executed.
237
+
238
+ - Runner diagnostics are attributed to the edited file through one seam:
239
+ `pathsEqual` against the path the tool itself reported, resolved from the cwd
240
+ the tool ran in. `golangci-lint`, `rust-clippy`, `javac`, `zig-check`,
241
+ `detekt`, `cpp-check`, `dotnet-build`, `dart-analyze` and `cue-vet` each
242
+ decided it locally before — a bare `===`, a `path.resolve` with no base, a
243
+ basename compare, an `endsWith` — so a `golangci-lint` run in any project
244
+ whose Go module root is not the extension's own working directory dropped
245
+ every finding for the edited file and reported it clean, and a `cue vet` error
246
+ in a sibling directory whose file shared the touched file's name was reported
247
+ as the touched file's own failure (refs #3278).
248
+
249
+ - `gleam check` diagnostics and `ruff` autofix findings are now attributed to the
250
+ edited file through the same seam every other tool uses (`pathsEqual` against
251
+ the path the tool reported, resolved from the cwd it ran in). gleam's location
252
+ line arrives inside `codespan_reporting`'s `┌─` gutter, which a suffix compare
253
+ was tolerating, so on Windows — and on any case-folding mount — a spelling
254
+ that differed only in case dropped every gleam diagnostic for the edited file;
255
+ ruff's JSON `filename` was compared with a bare `!==` against a path resolved
256
+ with no base at all, which silently meant the extension's own working
257
+ directory (refs #3285, refs #3286).
258
+
259
+ - Gleam diagnostics now carry their error or warning title and inline label instead of codespan's border glyph, and locationless project output is left unattributed rather than charged to the dispatched file (refs #3293).
260
+
261
+ - LSP workspace-edit changed paths are compared with the dispatched file through
262
+ the filesystem-aware path identity seam instead of a bare string equality, so a
263
+ differently spelled path to the same file is no longer announced as a
264
+ collateral change of itself (refs #3294).
265
+
266
+ - The LSP clean gate now reuses the handshake layer's per-server availability decision, so unavailable servers remain ⚠ while handshaking servers are gated (refs #3309).
267
+
268
+ - Added real LSP gate-entry coverage for handshake-census admission and handler-owned unavailability.
269
+
270
+ - A language server that answers `didOpen` with an empty diagnostic set while it
271
+ indexes no longer makes pi-lens report the file clean. intelephense publishes
272
+ `[]` before its whole-workspace index is warm and the real findings once
273
+ indexing ends, so `lsp_diagnostics` reported a php file carrying an undefined
274
+ variable as "confirmed clean". pi-lens now holds such a server's empty first
275
+ publish — once per session, released by the server's own next publish — so the
276
+ finding is reported, while every server that publishes `[]` for a genuinely
277
+ clean file keeps confirming it with no added wait (refs #3310).
278
+
279
+ - Launch fish-lsp through its stdio transport so standalone Fish diagnostics reach the LSP smoke gate (refs #3311).
280
+
281
+ - Prepare LSP smoke fixtures before gating C#, F#, Elixir, Expert, and Vue diagnostics, and give csharp-ls the measured 6s diagnostic budget its restored project needs (the 1.5s default read a real compiler error as clean).
282
+
283
+ - A tool the installer finds on `PATH` is now probed with that tool's own
284
+ check before it is resolved, the way every other rung of the resolution
285
+ ladder already was. A file on `PATH` that cannot actually run — rustup's
286
+ `rust-analyzer` proxy on a box where that component was never installed, a
287
+ `pipx`-installed `cmake-language-server` whose venv resolved a pygls that
288
+ removed the symbol it imports — no longer shadows the managed install that
289
+ works, and no longer reports as an available server that then never answers.
290
+ A probe that stalls or cannot be read still resolves as before: a kill is not
291
+ a verdict. `cmake-language-server` also pins its pygls floor, and that pin now
292
+ reaches whichever resolver pipx picked — pip and uv each read the same
293
+ constraints file through their own environment variable — so the install stops
294
+ producing a launcher that cannot start (refs #3311).
295
+
296
+ - The tmp-fixture hygiene census now judges only the temp entries the running
297
+ vitest invocation created: a root left by another invocation sharing the same
298
+ `TMPDIR` is neither reported as this run's leak nor deleted from under it, a
299
+ leak report names the test file behind a raw `mkdtempSync` prefix instead of
300
+ `owner: tests/unknown`, and the owner-marker liveness cases read only the
301
+ markers they wrote, so a fully skipped test file in the same invocation no
302
+ longer reds them (refs #3314, refs #3316, refs #3306).
303
+
304
+ - Provision the pinned `fish` executable in the nightly tool-smoke runner so `fish-lsp` can complete its handshake and clean-gate diagnostics.
305
+
306
+ - `scripts/ci-verdict.mjs` now reads every check-run page and reports an exact rerun command for a latest cancelled check (closes #3373).
307
+
308
+ - A child process that writes without limit can no longer terminate the Pi
309
+ host. `safeSpawnAsync` now caps retained output at 32 MiB when the caller
310
+ passes no `maxOutputBytes` — 98 of its 110 call sites passed none, and an
311
+ absent cap grew one JavaScript string until V8 threw `RangeError: Invalid
312
+ string length` from inside a stdout/stderr `data` handler, where no caller's
313
+ `try`/`catch` could reach it. The noisy child is now truncated, terminated
314
+ and reported through the existing `outputTruncated` / `killedForOutputCap`
315
+ result, and any other fault in that handler becomes the same bounded result
316
+ instead of an uncaught exception. A ledger row names the command, the cap,
317
+ the bytes observed and whether the child was terminated (closes #3375).
318
+
319
+ - Fix `ci-verdict` rerun hints to pass the workflow run id instead of the check-run job id.
320
+
321
+ - A degradation row whose subject is blank now reads `unknown` instead of
322
+ rendering as `⚠ <kind>: 1 — : <reason>` with an empty discriminator, and its
323
+ once-latch and tally key agree with the row. Any subject that normalizes to
324
+ empty or whitespace is folded at the ledger's write paths; metadata values and
325
+ reasons are untouched, so a genuinely empty value is still recorded as itself
326
+ (refs #3389).
327
+
328
+ - A pi session serving warm diagnostics to a peer no longer dies when a client
329
+ disconnects mid-reply. Every `requestWarmDiagnostics` timeout, schema refusal
330
+ and validation refusal destroys the client's socket, and the incumbent's
331
+ accepted socket had no `error` listener — so a routine `read ECONNRESET`
332
+ became an uncaught exception in the host. The event is now handled and
333
+ counted in the degradation ledger as `warm-attach-socket-error`, keyed by
334
+ errno (closes #3389).
335
+
336
+ - **Elixir diagnostics refresh again after an edit (refs [#3405](https://github.com/apmantza/pi-lens/issues/3405))** — pi-lens advertised `textDocument/didSave` but never sent one, so a language server whose diagnose pass runs on save (Expert, which recompiles the Mix project on save and on nothing else) never reported anything for a file edited through pi-lens. The post-write sync and the explicit `lens_diagnostics` (`source=lsp`) query now send the notification to every server that asked for it, carrying the document text when the server requested it and the file is inside the same 2 MiB / 5000-line bound the rest of the LSP sync already honours; servers that declare no `save` capability are unaffected, as are warm-up, cascade and workspace-sweep touches.
337
+
338
+ - **Non-core tree-sitter grammars on a compiled host (closes #3409)** — On a
339
+ runtime that cannot resolve a bare package specifier — pi ships as a
340
+ `bun build --compile` binary — pi-lens could not work out where to put a
341
+ lazily fetched grammar, so no non-core language (C#, C++, OCaml, …) could
342
+ ever be analysed: symbol search, module reports and structural rules stayed
343
+ degraded forever while the notification blamed package-manager build scripts
344
+ and the network. Both the read and the write path now resolve web-tree-sitter
345
+ through a subpath that works there — and only accept a directory that really is
346
+ that package, so a fetched grammar can never land in an unrelated tree — the
347
+ report names the real cause (nothing resolved, or the directory is not
348
+ writable), a tree-sitter run whose grammar never loaded is recorded as a skip
349
+ instead of a clean pass, and `scripts/install-selftest.mjs` stops reporting the
350
+ grammar asset missing on those same hosts.
351
+
352
+ - LSP root detection no longer trusts a resolved project root for the rest of
353
+ the session. Every memoized root carries the mtime of each directory its
354
+ marker walk probed and is re-walked as soon as one of them changes, so a
355
+ manifest scaffolded below an already-resolved root (`swift package init` in a
356
+ subdirectory, a new `package.json`, `prisma/schema.prisma` written into an
357
+ existing `prisma/`) moves the root on the next file touch, and a manifest
358
+ deleted at the resolved root falls back to the outer project. A hit also
359
+ expires on the shared 2 s re-check cadence, so even a change a directory mtime
360
+ cannot see — one made inside the same timestamp tick on a 1 s-granularity
361
+ volume — costs one window instead of a restart. All 45 marker detectors share
362
+ the one seam (refs #3412).
363
+
364
+ - Revalidate Git repository identity when repository metadata is created or removed during a process (closes #3417).
365
+
366
+ - **`lens_diagnostics mode=full` pointed circular-dependency findings at a file that does not exist (closes [#3428](https://github.com/apmantza/pi-lens/issues/3428))** — the project scan read madge's `--circular --json` output as a dependency graph while the turn-end lane read it correctly as an array of cycle arrays, so `Object.entries([["src/a.ts","src/b.ts"]])` handed the scan the array INDEX as the file name: every cycle was reported on `<root>/0`, `<root>/1`, … with its members resolved against the extension host's working directory instead of the scanned project root. The cycle COUNT was right either way, which is why it went unnoticed. Both sites now share one reader of madge's contract — the hand-rolled parses are deleted, so the number of readers goes from two to one — anchored at the cycle's first real file and resolved against the root that was scanned. The shape is pinned by fixtures captured from madge 8.0.0 (the install pi-lens manages) driven with the client's own argv, and the new test asserts the client still spawns that argv, so the captured bytes cannot silently become evidence for an invocation the client abandoned. Two neighbouring defects the upstream read surfaced are filed rather than folded in: madge resolves a single-file scan's members against that file's directory, not the project root ([#3435](https://github.com/apmantza/pi-lens/issues/3435)), and `--warning` is inert under `--json`, so the "skipped local file" count was structurally always zero ([#3436](https://github.com/apmantza/pi-lens/issues/3436)).
367
+
368
+ - **The turn-end circular-dependency pass keyed nested files by paths that do not exist (closes [#3435](https://github.com/apmantza/pi-lens/issues/3435))** — the single-file madge lane (`checkFilesBatch`, the pass `runtime-turn` runs) points madge at one file, and madge prints that file's cycle members relative to the target's directory. The client resolved them against the project root instead, so a cycle between `src/a.ts` and `src/b.ts` seeded the shared circular-file set with `<root>/a.ts` and `<root>/b.ts` — neither exists — and `isInCircular`/`getCircularForFile` answered false for the real nested files (`hasCircular` was right by accident, since it counts parsed cycles). Members now resolve against the target file's directory through the shared `parseMadgeCycles` reader, including `../` members for cycles that leave that directory. Pinned by fixtures captured from madge 8.0.0 with the client's own argv.
369
+
370
+ - **The madge circular-dependency pass reported a "skipped local file" count that was always zero (closes [#3436](https://github.com/apmantza/pi-lens/issues/3436))** — `buildMadgeArgs` passed `--warning`, but madge 8.0.0 gates that flag on `!program.json` (bin/cli.js) and the client always passes `--json`, so nothing reached stderr and `parseMadgeSkips` / `DepCheckResult.localSkips` could only ever return `{ total: 0, local: [] }`; ungated, the skip list prints to stdout and would corrupt the JSON this reader parses. The flag, the parser, the count field and the "possible silent cycle-miss" log line are deleted. Because madge cannot be asked for its skipped-file list under `--json`, that lost visibility is recorded once per session/root as `madge-skip-visibility-unavailable`, from `parseMadgeCycles` — the one reader both the turn-end and session-start lanes pass through — instead of being read as a clean graph. Pinned by a captured madge 8.0.0 invocation from a workspace with an unresolvable local import.
371
+
372
+ - Keep preserved LSP capability rows and bullets deterministic across locales.
373
+
374
+ - Share runner outcome parsing for ktlint and PHP lint so nonzero findings are
375
+ delivered instead of being mistaken for skipped output (refs #1816).
376
+
377
+ - **Share runner outcome parsing for Dart Analyze and Elixir Check (refs #1816)** — Nonzero empty, malformed, stderr-only, or signaled runs no longer report a clean file; valid findings remain visible through the shared bounded outcome path.
378
+
379
+ - **Share compiler runner outcome parsing (refs #1816)** — `cpp-check` and `zig-check` now distinguish failed, skipped, and clean invocations through the shared runner outcome seam.
380
+
381
+ - **Share compiler runner outcome parsing (refs #1816)** — Route `javac` and `dotnet-build` through the shared runner outcome parser so failed compiler invocations cannot be reported as clean files.
382
+
383
+ - **Consolidate RuboCop and Ruff run outcomes (refs #1816)** — Nonzero empty or unparsable tool output no longer reports a clean file, while status-2 findings and bounded failure context retain their existing semantics.
384
+
385
+ - **Carry Spotless ktfmt styles into the standalone formatter (closes #2481)** — `googleStyle()` and `kotlinlangStyle()` now map to ktfmt CLI flags. `dropboxStyle()` records the CLI limitation and falls back to the bare invocation.
386
+
387
+ - **Worker-side test diagnostics now reach passing-run logs (closes #3128)** — replaced the scoped warning/error paths with newline-terminated `process.stderr.write` records, including the tests-tree guard's two sites, so Vitest's default worker reporter cannot silently drop them.
388
+
389
+ - Skip formatter style inference when indentation exists only inside masked comment or template interiors.
390
+
391
+ - The temporary-fixture hygiene sweep now waits for live test owners to finish
392
+ their bounded cleanup drain and attributes remaining entries to the owner
393
+ file, preventing cross-worker false leaks without adding prefix admissions.
394
+ Owner markers authenticate themselves with a heartbeat the worker refreshes
395
+ from its own test lifecycle, so an orphaned or PID-reused worker is attributed
396
+ after the bound on every platform, not only where `/proc` exists (refs #3186).
397
+
398
+ - **Three Python ast-grep rules no longer false-positive (closes #3211)** —
399
+ `no-boolean-in-except` stops its `boolean_operator` search at the handler's
400
+ own block, so an `or`/`and` inside the handler *body* (`return 2 or 3`) is
401
+ no longer mistaken for a boolean exception-type condition.
402
+ `no-http-headers-bracket-access` now excludes `headers[...]` writes
403
+ (`resp.headers["X"] = v`, `+=`, `del`) — only reads can raise `KeyError`.
404
+ `unchecked-throwing-call-python`'s `int()`/`float()` patterns moved to a
405
+ new `unchecked-numeric-parse-python` rule (`warning` severity) that also
406
+ excludes numeric-literal and already-numeric-constructor (`int`/`float`/
407
+ `Decimal`) arguments.
408
+
409
+ - **Parse inline suppression reasons (closes #3213)** - `pi-lens-ignore` comments now suppress their listed rule ids when followed by a supported trailing reason.
410
+
411
+ - **GitHub CLI authentication for release tools (closes #3221)** — GitHub-release
412
+ tool installation and refresh now reuse authenticated `gh` CLI credentials for
413
+ API metadata requests, while keeping release asset downloads unauthenticated
414
+ and reporting evidence-supported anonymous rate-limit failures with an
415
+ actionable remedy.
416
+
417
+ - **`no-assert-tuple` now checks only the assertion condition (closes #3223).**
418
+ Tuple literals used in comparisons, calls, comprehensions, containers, and
419
+ assertion messages no longer produce false-positive diagnostics.
420
+
421
+ - **Avoid unsupported Windows SIGHUP re-raise (refs #3239)** — Console-close cleanup now preserves tracked-child termination without recording a false crash from `kill ENOSYS`.
422
+
423
+ - **Honor `lens_diagnostic_mark` on turn-end unresolved blockers (refs #3246)** — The "Unresolved from this turn" section now applies the shared disposition/rule/inline-suppression policy to the blockers it re-serves, so a finding marked `false-positive` stops coming back on later turns; when every blocker on a file is marked, the section and its `🔴 STOP` text are dropped entirely.
424
+
425
+ - **Filter the built actionable-warnings advisory (closes #3270, refs #3248)** — the turn-end advisory now applies stored dispositions and suppresses ast-grep secret duplicates after LSP enrichment, while the raw cache remains available for `mode=delta`.
426
+
427
+ - **The commit gate honors a turn-end disposition mark (refs #3248)** — when every blocker on a file is marked `false-positive`, turn end now recomputes the `--lens-guard` commit-gate latch from the surviving findings instead of from the raw blocker map, so the gate stops quoting findings pi-lens no longer prints, and the persisted turn-end record stops re-serving them; a blocker the policy did not suppress, a file demoted by the freshness sweep, and a freshly dispatched blocker all keep gating exactly as before, and a marked file whose bytes change outside pi-lens blocks again as `blocker state is unknown` until a fresh check judges the new content, rather than committing on a verdict that no longer describes the file.
428
+
429
+ - **Apply stored dispositions at six more turn-end surfaces (refs #3248)** — knip's blocker and advisory, the dead-code advisory, the call-graph impact advisory, the late-runner drain and the code-quality warnings advisory now filter through the shared disposition/rule policy before rendering, so a finding marked `false-positive` stops re-reporting there; a mark that names no tool — the only spelling those surfaces' own text can carry — is honored too.
430
+
431
+ - **Two case-distinct workspaces no longer share one warm-IPC endpoint (closes #3255)**
432
+ — the per-workspace id behind the warm socket/pipe and the
433
+ `pi-lens-turn-end-*.json` status file lowercased the resolved cwd on every
434
+ platform, so on a case-sensitive filesystem `/repo/Alpha` and `/repo/alpha`
435
+ collided: a PostToolUse or Stop hook in one project could reach the other
436
+ project's warm server, and `pilens_health` reported one workspace's turn-end
437
+ activity under the other's name. The fold now runs only on the platforms
438
+ whose filesystem folds case (Windows, macOS), where it is what lets the
439
+ server and the hook meet. On Linux and the BSDs the id changes for any
440
+ workspace path containing an uppercase letter, so the upgrade is now
441
+ reported rather than silent: a warm endpoint with nothing listening is its
442
+ own skip reason (`no-listener`) instead of the generic `ipc-error`, the Stop
443
+ hook prints and records it for `pilens_health` with the remedy (an MCP
444
+ server that was already running owns the previous endpoint name — restart
445
+ it), and a warm-attached session records it once per session, now also shown
446
+ by `/lens-health`. A status file written before the upgrade is simply left
447
+ alone: it is no longer derived by anything, and on a case-sensitive host that
448
+ name can belong to a live case-variant sibling workspace.
449
+
450
+ - The finding-delivery governance detector no longer lets an arm-shaped
451
+ evidence read borrow a nearby, unrelated gate call; it now follows the
452
+ occurrence's binding chain, while preserving genuine inline gate results
453
+ and existing surface coverage (refs #3266).
454
+
455
+ - `--lens-guard` no longer blocks every `git commit` for the rest of a session
456
+ once pi-lens has reported a blocker. The commit gate validated its own
457
+ persisted blocker record one line at a time while both writers of that record
458
+ render multi-line blocker text, so from the first blocker onward every commit
459
+ was refused with `blocker state is unknown
460
+ (blocking_provenance_untrusted)` — including after the blocker was fixed, and
461
+ with no re-run able to clear it (#3282).
462
+
463
+ - **Restore strictness in the actionable-warnings advisory (refs #3248)** —
464
+ disposition identity fields that are absent from a warning are no longer
465
+ emitted as explicit `undefined` values, keeping the strict optional-property
466
+ ratchet at its existing floor.
467
+
468
+ - Runner parsers now attribute a diagnostic only when the tool-reported path
469
+ matches the dispatched file, resolved from the cwd the tool actually ran in:
470
+ taplo, yamllint, htmlhint, oxlint, shellcheck, trivy-config, stylelint,
471
+ rubocop, eslint, biome, tflint, swiftlint, ktlint, hadolint, actionlint,
472
+ typos, sqlfluff, vale, markdownlint, php -l, and the shared line-parser
473
+ factory behind ruff's text fallback. A second file's finding is no longer
474
+ delivered as the edited file's problem (refs #3295, #3278, #1193).
475
+
476
+ - Gate Terraform and Svelte tool-smoke fixtures with seeded diagnostics, correcting the Terraform defect seed and the Svelte diagnostic wait budget (refs #3311).
477
+
478
+ - Install `cmake-format` as `cmakelang[yaml]` so it can read a
479
+ `.cmake-format.yaml` project config instead of dying with
480
+ `ModuleNotFoundError: No module named 'yaml'`, force a pipx install that would
481
+ otherwise be a silent no-op over an existing venv, and keep a bounded,
482
+ banner-free formatter traceback tail for actionable failures (refs #3312).
483
+
484
+ - **Align shown-inline telemetry with deleted-path filtering (closes #3361)** — Retracted blockers are no longer recorded as shown to the agent.
485
+
486
+ - Make the hook-await exemption rekey script work on Node builds without built-in TypeScript support.
487
+
488
+ - **Nightly LSP docs refreshes now ignore date-only changes and never open refresh PRs from branch dispatches (closes #3380).**
489
+
490
+ - Bounded every remaining stream `data` handler that grew a string without a
491
+ ceiling (#3383): the forked analyze worker's pipes, the unref'd process-table
492
+ collector, the installer's two interpreter user-base probes, and all four
493
+ newline-framing readers (the MCP host's stdin loop, the warm IPC clients and
494
+ the shared server-side reader). A peer that never sends a newline, or a child
495
+ that never stops writing, now ends in a bounded failure with a ledger row
496
+ instead of an uncatchable `RangeError` raised inside the handler. The
497
+ self-signal re-raise at host shutdown is likewise total.
498
+
499
+ - Scope clean-signal probe publishes to the server measured by each row, and reset the vue capability-matrix row that had counted another server's publishes.
500
+
501
+ - Prevented the Elixir smoke row from reporting a fallback Expert handshake as ElixirLS success.
502
+
503
+ - Files over the shared LSP content bound now receive an explicit message from `lens_diagnostics` (`source=lsp`) instead of being synced in full.
504
+
505
+ - The installer now gives users the same jscpd 5.3.0 version used by pi-lens itself.
506
+
507
+ - Await in-flight jscpd scans before test fixture cleanup so deferred report-directory work cannot leave temporary roots behind.
508
+
509
+ - Await LSP workspace-diagnostics service shutdown before removing sweep test fixtures.
510
+
511
+ - Keep nightly fish runner provisioning resilient to distro package version bumps by installing the apt package unpinned.
512
+
513
+ - **Ignore abbreviations, versions, and file paths in the PR-body Why sentence check (refs #3262)** — The gate now recognizes sentence boundaries by following context instead of counting every terminator.
514
+
515
+ - The tool-smoke workflow's SonarCloud master-gate read now runs only on the nightly schedule and on master, so a branch dispatch no longer goes red on master's Sonar state; the advisory targeted-tests CI job installs lockfile-locked and script-free (refs #3319, #3346).
516
+
19
517
  ## [4.2.1] - 2026-09-17
20
518
 
21
519
  ### Added
@@ -86,6 +86,8 @@ flowchart TD
86
86
 
87
87
  SESSION["Session lifecycle<br/>primary, sequential replacement, concurrent secondary"]
88
88
  SINKS["Observability sinks<br/>latency.log, degradation ledger, bounded telemetry,<br/>cache observability, cascade and tree-sitter logs"]
89
+ CONFIG["Config-core<br/>resolve, merge, deny"]
90
+ REVIEWGRAPH["Review-graph intelligence<br/>call graph, reverse deps, import resolution"]
89
91
 
90
92
  HOST --> WRAP
91
93
  HOST -->|tool_call, raw| RG
@@ -100,6 +102,10 @@ flowchart TD
100
102
  PLAN --> STRUCT
101
103
  PIPE --> POOL
102
104
  PIPE --> BUS
105
+ CONFIG --> PIPE
106
+ CONFIG --> PLAN
107
+ STRUCT --> REVIEWGRAPH
108
+ REVIEWGRAPH --> CASC
103
109
  POOL --> DIAGS
104
110
  DIAGS --> CASC
105
111
  RUN --> STORES
@@ -130,8 +136,8 @@ flowchart TD
130
136
  Architecture-level view, updated when a lane changes. Per-tool inventories live
131
137
  in [features](docs/features.md) and
132
138
  [language coverage](docs/language-coverage.md). Today the edit-time lane carries
133
- 45+ runner modules over 35+ file kinds, and the LSP lane speaks to a dozen-plus
134
- language servers.
139
+ 45+ runner modules over 35+ file kinds, and the LSP lane speaks to 46
140
+ language server definitions.
135
141
 
136
142
  The gating box is an abstraction, not a call order. Freshness covers several
137
143
  independent mechanisms: path freshness against scan time, past-EOF line checks,
@@ -194,6 +200,8 @@ expects to be operated.
194
200
  - [Custom rules](docs/custom-rules.md) — project ast-grep and tree-sitter rules
195
201
  - [MCP server](docs/mcp.md) — experimental MCP server for Claude Code and
196
202
  other MCP clients
203
+ - [Architecture decision records](docs/adr/README.md) — settled design
204
+ decisions, linked from the engineering-defect catalog
197
205
 
198
206
  ## Contributing
199
207
 
@@ -349,6 +357,41 @@ Thanks goes to these wonderful people:
349
357
  <tr>
350
358
  <td align="center" valign="top" width="14.28%"><a href="https://github.com/amasen02"><img src="https://avatars.githubusercontent.com/u/97525823?v=4" width="100px;" alt=""/><br /><sub><b>Ama Senevirathne</b></sub></a><br /><a href="#code-amasen02" title="Code">💻</a></td>
351
359
  <td align="center" valign="top" width="14.28%"><a href="https://github.com/AcademyKUB8"><img src="https://avatars.githubusercontent.com/u/217174854?v=4" width="100px;" alt=""/><br /><sub><b>AcademyKUB8</b></sub></a><br /><a href="#code-AcademyKUB8" title="Code">💻</a></td>
360
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/soulocker"><img src="https://avatars.githubusercontent.com/u/9692331?v=4" width="100px;" alt=""/><br /><sub><b>soulocker</b></sub></a><br /><a href="#bug-soulocker" title="Bug reports">🐛</a></td>
361
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/sonic182"><img src="https://avatars.githubusercontent.com/u/4070466?v=4" width="100px;" alt=""/><br /><sub><b>Johanderson Mogollon</b></sub></a><br /><a href="#ideas-sonic182" title="Ideas & Planning">🤔</a></td>
362
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/kurihada"><img src="https://avatars.githubusercontent.com/u/63988604?v=4" width="100px;" alt=""/><br /><sub><b>kurihada</b></sub></a><br /><a href="#bug-kurihada" title="Bug reports">🐛</a></td>
363
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/Giraffe224"><img src="https://avatars.githubusercontent.com/u/193071526?v=4" width="100px;" alt=""/><br /><sub><b>Giraffe224</b></sub></a><br /><a href="#bug-Giraffe224" title="Bug reports">🐛</a></td>
364
+ <td align="center" valign="top" width="14.28%"><a href="http://www.crshman.com"><img src="https://avatars.githubusercontent.com/u/39948?v=4" width="100px;" alt=""/><br /><sub><b>Robert Navarro</b></sub></a><br /><a href="#bug-rnavarro" title="Bug reports">🐛</a> <a href="#code-rnavarro" title="Code">💻</a></td>
365
+ </tr>
366
+ <tr>
367
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/kapkema"><img src="https://avatars.githubusercontent.com/u/774573?v=4" width="100px;" alt=""/><br /><sub><b>Marc</b></sub></a><br /><a href="#bug-kapkema" title="Bug reports">🐛</a></td>
368
+ <td align="center" valign="top" width="14.28%"><a href="https://javymarmol.com"><img src="https://avatars.githubusercontent.com/u/3501269?v=4" width="100px;" alt=""/><br /><sub><b>Heyner Javier Marmol Verbel</b></sub></a><br /><a href="#bug-javymarmol" title="Bug reports">🐛</a></td>
369
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/Stijnus"><img src="https://avatars.githubusercontent.com/u/72551117?v=4" width="100px;" alt=""/><br /><sub><b>Stijnus</b></sub></a><br /><a href="#bug-Stijnus" title="Bug reports">🐛</a></td>
370
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/Macrox"><img src="https://avatars.githubusercontent.com/u/571851?v=4" width="100px;" alt=""/><br /><sub><b>macroxing</b></sub></a><br /><a href="#bug-Macrox" title="Bug reports">🐛</a></td>
371
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/jwtracy"><img src="https://avatars.githubusercontent.com/u/58991818?v=4" width="100px;" alt=""/><br /><sub><b>John Tracy</b></sub></a><br /><a href="#code-jwtracy" title="Code">💻</a></td>
372
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/woyxiang"><img src="https://avatars.githubusercontent.com/u/119841810?v=4" width="100px;" alt=""/><br /><sub><b>woyxiang</b></sub></a><br /><a href="#ideas-woyxiang" title="Ideas & Planning">🤔</a></td>
373
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/Juncanthia"><img src="https://avatars.githubusercontent.com/u/17041926?v=4" width="100px;" alt=""/><br /><sub><b>Juncanthia</b></sub></a><br /><a href="#bug-Juncanthia" title="Bug reports">🐛</a></td>
374
+ </tr>
375
+ <tr>
376
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/kevinkirkup"><img src="https://avatars.githubusercontent.com/u/38406?v=4" width="100px;" alt=""/><br /><sub><b>Kevin S Kirkup</b></sub></a><br /><a href="#bug-kevinkirkup" title="Bug reports">🐛</a></td>
377
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/georgeharker"><img src="https://avatars.githubusercontent.com/u/2169888?v=4" width="100px;" alt=""/><br /><sub><b>George Harker</b></sub></a><br /><a href="#code-georgeharker" title="Code">💻</a> <a href="#bug-georgeharker" title="Bug reports">🐛</a> <a href="#ideas-georgeharker" title="Ideas & Planning">🤔</a></td>
378
+ <td align="center" valign="top" width="14.28%"><a href="https://zuey.me"><img src="https://avatars.githubusercontent.com/u/6857382?v=4" width="100px;" alt=""/><br /><sub><b>Duy /zuey/</b></sub></a><br /><a href="#ideas-mrgoonie" title="Ideas & Planning">🤔</a> <a href="#code-mrgoonie" title="Code">💻</a></td>
379
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/luw2007"><img src="https://avatars.githubusercontent.com/u/1415541?v=4" width="100px;" alt=""/><br /><sub><b>luw2007</b></sub></a><br /><a href="#bug-luw2007" title="Bug reports">🐛</a></td>
380
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/WrongWorry"><img src="https://avatars.githubusercontent.com/u/188037855?v=4" width="100px;" alt=""/><br /><sub><b>WrongWorry</b></sub></a><br /><a href="#bug-WrongWorry" title="Bug reports">🐛</a></td>
381
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/nanaxx131-gif"><img src="https://avatars.githubusercontent.com/u/310295749?v=4" width="100px;" alt=""/><br /><sub><b>nanaxx131-gif</b></sub></a><br /><a href="#bug-nanaxx131-gif" title="Bug reports">🐛</a></td>
382
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/CIECJS"><img src="https://avatars.githubusercontent.com/u/130489549?v=4" width="100px;" alt=""/><br /><sub><b>MateusCx</b></sub></a><br /><a href="#bug-CIECJS" title="Bug reports">🐛</a></td>
383
+ </tr>
384
+ <tr>
385
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/HughZadora"><img src="https://avatars.githubusercontent.com/u/32498443?v=4" width="100px;" alt=""/><br /><sub><b>Hugh Zadora</b></sub></a><br /><a href="#bug-HughZadora" title="Bug reports">🐛</a></td>
386
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/cxh0312"><img src="https://avatars.githubusercontent.com/u/10069427?v=4" width="100px;" alt=""/><br /><sub><b>撑伞的鱼</b></sub></a><br /><a href="#bug-cxh0312" title="Bug reports">🐛</a></td>
387
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/LvGitHub-9"><img src="https://avatars.githubusercontent.com/u/69997168?v=4" width="100px;" alt=""/><br /><sub><b>Lv</b></sub></a><br /><a href="#bug-LvGitHub-9" title="Bug reports">🐛</a></td>
388
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/apmcodes"><img src="https://avatars.githubusercontent.com/u/13360356?v=4" width="100px;" alt=""/><br /><sub><b>apmcodes</b></sub></a><br /><a href="#bug-apmcodes" title="Bug reports">🐛</a></td>
389
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/jondong"><img src="https://avatars.githubusercontent.com/u/700736?v=4" width="100px;" alt=""/><br /><sub><b>Jonathan Dong</b></sub></a><br /><a href="#bug-jondong" title="Bug reports">🐛</a></td>
390
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/alchemistklk"><img src="https://avatars.githubusercontent.com/u/56862773?v=4" width="100px;" alt=""/><br /><sub><b>Caprika</b></sub></a><br /><a href="#bug-alchemistklk" title="Bug reports">🐛</a></td>
391
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/dmontague-crwv"><img src="https://avatars.githubusercontent.com/u/289768493?v=4" width="100px;" alt=""/><br /><sub><b>dmontague-crwv</b></sub></a><br /><a href="#bug-dmontague-crwv" title="Bug reports">🐛</a></td>
392
+ </tr>
393
+ <tr>
394
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/glyh"><img src="https://avatars.githubusercontent.com/u/12870742?v=4" width="100px;" alt=""/><br /><sub><b>Yihang "Corvo" Liu</b></sub></a><br /><a href="#bug-glyh" title="Bug reports">🐛</a></td>
352
395
  </tr>
353
396
  </tbody>
354
397
  </table>