pi-lens 4.1.5 → 4.2.0
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.
- package/CHANGELOG.md +415 -0
- package/README.md +13 -8
- package/config/dependency-cruiser-eager-allowlist.json +6 -1
- package/dist/clients/actionable-warnings.js +30 -4
- package/dist/clients/agent-behavior-client.js +2 -0
- package/dist/clients/analysed-root.js +1 -0
- package/dist/clients/ast-grep-tool-logger.js +1 -1
- package/dist/clients/bash-file-access.js +26 -0
- package/dist/clients/biome-client.js +13 -2
- package/dist/clients/blocker-freshness.js +403 -35
- package/dist/clients/bootstrap.js +6 -1
- package/dist/clients/bounded-telemetry.js +2 -0
- package/dist/clients/cache-observability.js +122 -1
- package/dist/clients/cascade-format.js +200 -4
- package/dist/clients/code-quality-warnings.js +10 -2
- package/dist/clients/complexity-client.js +5 -0
- package/dist/clients/config-diagnostic-codes.js +2 -0
- package/dist/clients/config-resolve.js +6 -3
- package/dist/clients/config-schema.js +17 -0
- package/dist/clients/config-warn.js +2 -2
- package/dist/clients/dead-code-client.js +8 -1
- package/dist/clients/degradation-ledger.js +16 -1
- package/dist/clients/dependency-checker.js +5 -1
- package/dist/clients/diagnostic-dispositions.js +12 -1
- package/dist/clients/dispatch/ast-grep-catalog.js +65 -0
- package/dist/clients/dispatch/auxiliary-lsp.js +28 -0
- package/dist/clients/dispatch/dispatcher.js +47 -17
- package/dist/clients/dispatch/facts/function-facts.js +1 -1
- package/dist/clients/dispatch/facts/import-facts.js +5 -1
- package/dist/clients/dispatch/finding-policy.js +222 -0
- package/dist/clients/dispatch/indent-detect.js +389 -13
- package/dist/clients/dispatch/integration.js +175 -25
- package/dist/clients/dispatch/rule-ignores.js +62 -0
- package/dist/clients/dispatch/runners/actionlint.js +2 -1
- package/dist/clients/dispatch/runners/ast-grep-napi.js +21 -41
- package/dist/clients/dispatch/runners/biome-check.js +8 -4
- package/dist/clients/dispatch/runners/cpp-check.js +4 -3
- package/dist/clients/dispatch/runners/credo.js +2 -1
- package/dist/clients/dispatch/runners/cue-vet.js +2 -1
- package/dist/clients/dispatch/runners/dart-analyze.js +2 -1
- package/dist/clients/dispatch/runners/detekt.js +2 -1
- package/dist/clients/dispatch/runners/dotnet-build.js +2 -1
- package/dist/clients/dispatch/runners/elixir-check.js +2 -1
- package/dist/clients/dispatch/runners/eslint.js +2 -1
- package/dist/clients/dispatch/runners/fish-indent.js +2 -1
- package/dist/clients/dispatch/runners/gleam-check.js +2 -1
- package/dist/clients/dispatch/runners/go-vet.js +2 -1
- package/dist/clients/dispatch/runners/golangci-lint.js +2 -1
- package/dist/clients/dispatch/runners/hadolint.js +2 -1
- package/dist/clients/dispatch/runners/helm-lint.js +3 -1
- package/dist/clients/dispatch/runners/helm-render.js +2 -1
- package/dist/clients/dispatch/runners/htmlhint.js +2 -1
- package/dist/clients/dispatch/runners/javac.js +2 -1
- package/dist/clients/dispatch/runners/ktlint.js +2 -1
- package/dist/clients/dispatch/runners/lsp.js +19 -1
- package/dist/clients/dispatch/runners/markdownlint.js +3 -2
- package/dist/clients/dispatch/runners/mypy.js +2 -1
- package/dist/clients/dispatch/runners/oxlint.js +2 -1
- package/dist/clients/dispatch/runners/php-lint.js +2 -1
- package/dist/clients/dispatch/runners/phpstan.js +2 -1
- package/dist/clients/dispatch/runners/prisma-validate.js +2 -1
- package/dist/clients/dispatch/runners/psscriptanalyzer.js +12 -12
- package/dist/clients/dispatch/runners/pyright.js +2 -1
- package/dist/clients/dispatch/runners/rubocop.js +2 -1
- package/dist/clients/dispatch/runners/ruff.js +2 -2
- package/dist/clients/dispatch/runners/rust-clippy.js +12 -11
- package/dist/clients/dispatch/runners/shellcheck.js +2 -1
- package/dist/clients/dispatch/runners/shfmt.js +4 -13
- package/dist/clients/dispatch/runners/spellcheck.js +2 -2
- package/dist/clients/dispatch/runners/spotbugs.js +2 -1
- package/dist/clients/dispatch/runners/sqlfluff.js +3 -2
- package/dist/clients/dispatch/runners/stylelint.js +3 -2
- package/dist/clients/dispatch/runners/swiftlint.js +2 -1
- package/dist/clients/dispatch/runners/taplo.js +2 -1
- package/dist/clients/dispatch/runners/terragrunt.js +2 -1
- package/dist/clients/dispatch/runners/tflint.js +2 -1
- package/dist/clients/dispatch/runners/tree-sitter.js +2 -21
- package/dist/clients/dispatch/runners/trivy-config.js +2 -1
- package/dist/clients/dispatch/runners/utils/availability-policy.js +5 -0
- package/dist/clients/dispatch/runners/utils/candidate-probe.js +2 -2
- package/dist/clients/dispatch/runners/utils/runner-helpers.js +8 -2
- package/dist/clients/dispatch/runners/vale.js +2 -1
- package/dist/clients/dispatch/runners/yamllint.js +4 -3
- package/dist/clients/dispatch/runners/zig-check.js +2 -1
- package/dist/clients/dispatch/types.js +4 -1
- package/dist/clients/disposition-publish.js +1 -0
- package/dist/clients/effective-config.js +12 -2
- package/dist/clients/extension-log.js +2 -0
- package/dist/clients/file-kinds.js +26 -1
- package/dist/clients/file-role.js +23 -6
- package/dist/clients/file-utils.js +156 -10
- package/dist/clients/finding-delivery-gate.js +60 -9
- package/dist/clients/format-service.js +39 -21
- package/dist/clients/formatters.js +113 -200
- package/dist/clients/generation-guard.js +6 -2
- package/dist/clients/git-guard.js +11 -3
- package/dist/clients/gitleaks-client.js +42 -6
- package/dist/clients/govulncheck-client.js +48 -7
- package/dist/clients/indent-retarget.js +78 -8
- package/dist/clients/installer/index.js +524 -161
- package/dist/clients/instance-reaper-state.js +7 -0
- package/dist/clients/instance-reaper.js +20 -10
- package/dist/clients/jscpd-client.js +9 -1
- package/dist/clients/knip-client.js +9 -2
- package/dist/clients/language-profile.js +35 -3
- package/dist/clients/language-registry.js +5 -1
- package/dist/clients/latency-logger.js +6 -1
- package/dist/clients/lens-config.js +4 -0
- package/dist/clients/lens-engine.js +12 -3
- package/dist/clients/lens-events.js +2 -1
- package/dist/clients/lens-flag-registry.js +79 -0
- package/dist/clients/live-bus-emitter.js +2 -1
- package/dist/clients/lsp/client.js +25 -21
- package/dist/clients/lsp/config.js +36 -7
- package/dist/clients/lsp/diagnostic-binding.js +24 -1
- package/dist/clients/lsp/document-drift.js +78 -0
- package/dist/clients/lsp/index.js +288 -45
- package/dist/clients/lsp/launch.js +16 -4
- package/dist/clients/lsp/server.js +79 -15
- package/dist/clients/lsp/workspace-diagnostics-cache.js +39 -5
- package/dist/clients/lsp-mutation.js +5 -3
- package/dist/clients/map-with-concurrency.js +12 -8
- package/dist/clients/mcp/analyze.js +3 -0
- package/dist/clients/mcp/session.js +38 -4
- package/dist/clients/ndjson-logger.js +24 -7
- package/dist/clients/observed-mutation.js +40 -2
- package/dist/clients/opaque-mutation-scan.js +23 -3
- package/dist/clients/opengrep-client.js +111 -14
- package/dist/clients/package-manager.js +2 -1
- package/dist/clients/path-utils.js +123 -3
- package/dist/clients/performance-report.js +5 -1
- package/dist/clients/php-cs-fixer-config.js +8 -3
- package/dist/clients/pipeline.js +77 -7
- package/dist/clients/project-diagnostics/cache.js +37 -5
- package/dist/clients/project-diagnostics/fresh-fetch.js +173 -18
- package/dist/clients/project-diagnostics/runner-adapters/dead-code.js +4 -1
- package/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +10 -9
- package/dist/clients/project-diagnostics/scanner.js +59 -3
- package/dist/clients/project-lens-config.js +48 -2
- package/dist/clients/read-guard-logger.js +2 -0
- package/dist/clients/read-guard.js +70 -11
- package/dist/clients/resource-sampler.js +147 -9
- package/dist/clients/review-graph-logger.js +2 -0
- package/dist/clients/ruff-client.js +18 -9
- package/dist/clients/runtime-agent-end.js +42 -5
- package/dist/clients/runtime-context.js +6 -2
- package/dist/clients/runtime-coordinator.js +82 -7
- package/dist/clients/runtime-session.js +33 -5
- package/dist/clients/runtime-tool-call.js +43 -12
- package/dist/clients/runtime-tool-result.js +334 -86
- package/dist/clients/runtime-turn.js +195 -18
- package/dist/clients/safe-spawn.js +258 -5
- package/dist/clients/scratch-tree-policy.js +6 -0
- package/dist/clients/security-scan-client.js +2 -2
- package/dist/clients/session-event-guard.js +119 -6
- package/dist/clients/sg-runner.js +25 -12
- package/dist/clients/sgconfig.js +79 -2
- package/dist/clients/situational-tool-telemetry.js +108 -0
- package/dist/clients/test-runner-client.js +258 -78
- package/dist/clients/test-runner-delivery.js +43 -0
- package/dist/clients/tool-agreement.js +423 -0
- package/dist/clients/tool-config.js +249 -0
- package/dist/clients/tool-cwd.js +270 -0
- package/dist/clients/tool-policy.js +163 -7
- package/dist/clients/tool-probe.js +53 -0
- package/dist/clients/tool-set-policy.js +42 -0
- package/dist/clients/tree-sitter-client.js +120 -0
- package/dist/clients/tree-sitter-query-loader.js +127 -189
- package/dist/clients/trivy-client.js +9 -1
- package/dist/clients/turn-context.js +52 -0
- package/dist/clients/widget-state.js +102 -3
- package/dist/clients/word-index.js +5 -2
- package/dist/clients/write-ordering-guard.js +7 -0
- package/dist/index.js +26968 -21741
- package/dist/mcp/analyze-cli.js +3 -2
- package/dist/mcp/server.js +140 -161
- package/dist/tools/activate-tools.js +10 -7
- package/dist/tools/ast-grep-outline.js +2 -13
- package/dist/tools/ast-grep-replace.js +8 -13
- package/dist/tools/ast-grep-search.js +83 -39
- package/dist/tools/effective-config.js +4 -3
- package/dist/tools/lens-diagnostic-mark.js +8 -11
- package/dist/tools/lens-diagnostics.js +516 -133
- package/dist/tools/lsp-diagnostics.js +265 -182
- package/dist/tools/lsp-navigation.js +20 -44
- package/dist/tools/module-report.js +5 -9
- package/dist/tools/project-report.js +9 -17
- package/dist/tools/render-compact.js +280 -1
- package/dist/tools/symbol-search.js +2 -2
- package/docs/agent-guide.md +3 -4
- package/docs/agent-tools.md +46 -22
- package/docs/audit1.md +5 -5
- package/docs/configuration.md +9 -0
- package/docs/dependencies.md +11 -0
- package/docs/dispositions.md +52 -0
- package/docs/environment-variables.md +2 -7
- package/docs/features.md +7 -7
- package/docs/globalconfig.md +14 -2
- package/docs/language-coverage.md +1 -0
- package/docs/mcp.md +13 -1
- package/docs/pi-lens-fixer.md +48 -18
- package/docs/pi-lens-investigator.md +38 -18
- package/docs/pi-lens-monitor.md +88 -0
- package/docs/pi-lens-retro.md +97 -0
- package/docs/pi-lens-reviewer.md +53 -31
- package/docs/pi-lens-subagent.md +12 -4
- package/docs/public-api-stability.md +1 -0
- package/docs/real-harness.md +46 -0
- package/docs/release-qa-baseline.md +27 -1
- package/docs/servercapabilities.md +1 -6
- package/docs/settings.md +44 -6
- package/docs/tools_improvement2.md +4 -4
- package/docs/usage.md +15 -3
- package/package.json +10 -1
- package/rules/tree-sitter-queries/typescript/sql-injection.yml +23 -7
- package/scripts/analyze-pi-lens-logs.mjs +40 -0
- package/skills/pi-lens-ast-grep/SKILL.md +23 -6
- package/skills/pi-lens-lsp-navigation/SKILL.md +26 -9
- package/dist/tools/ast-dump.js +0 -103
package/CHANGELOG.md
CHANGED
|
@@ -16,6 +16,421 @@ All notable changes to pi-lens will be documented in this file.
|
|
|
16
16
|
|
|
17
17
|
### Security
|
|
18
18
|
|
|
19
|
+
## [4.2.0] - 2026-09-16
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- **Add Typst language support (refs #3037)** — Tinymist provides LSP diagnostics and typstyle provides formatting for `.typ` and `.typc`, with managed GitHub-release installation for both tools.
|
|
24
|
+
|
|
25
|
+
- **Opt-in compact LSP status line (`ui.compactLspStatus`, `--lens-compact-lsp-status`) (refs #3099)** — the `pi-lens-lsp` footer status can now render one state glyph per group (`LSP ✓` green, `LSP ✗` red, dim `LSP ✗` when nothing is warm) instead of the active server names (#267). Default off: the published string is byte-identical when the flag is unset. Server ids stay reachable through `/lens-tools` and `lens_health`.
|
|
26
|
+
|
|
27
|
+
- **Opt-in off mode for the LSP status line (`ui.hideLspStatus`, `--lens-hide-lsp-status`) (closes #3099)** — the `pi-lens-lsp` footer status can now be suppressed entirely: `updateLspStatus` publishes `undefined` instead of any text, so a host that renders extension statuses stops showing the key at all. Outranks `ui.compactLspStatus` when both are set (nothing to render compactly once the key itself is gone). Default off: unset, the published string is unchanged. Server ids stay reachable through `/lens-tools` and `lens_health`.
|
|
28
|
+
|
|
29
|
+
- **Allow an explicit analysis root below the home ceiling (closes #2053)** — `lens_diagnostics mode=full` can run heavyweight analyzers for a named, existing project directory when the session cwd is `$HOME` or higher, while retaining the #749 refusal for unsafe roots.
|
|
30
|
+
|
|
31
|
+
- **Detect tests that drive the registry against the run-shared `PI_LENS_HOME` (refs #3042, #3050)** — `tests/clients/pi-lens-home-hermeticity.test.ts` now walks `clients/` for best-effort-locked `getGlobalPiLensDir()` writers and `tests/` for files that call one of their hazardous exports (or reach a producer's target file directly) without pinning their own home or mocking the writer away; it caught `tests/clients/instance-reaper-backstop.test.ts` racing the shared `orphan-backstop.json`/`.lock` on a stale per-worker-isolation assumption, fixed in the same change.
|
|
32
|
+
|
|
33
|
+
- **Opt Typst (tinymist) into the nightly LSP clean-gate (refs #3037, #3164)** — `tests/fixtures/tool-smoke/typst/main.typ` now carries a deliberate `#undefined_function(answer)` call and the `typst` `LSP_FIXTURES` entry in `scripts/smoke-tools.mjs` sets `lspGate: true` with `lspGateMarker: "#undefined_function"`, following the typescript entry's shape; `--lsp-gate typst` now proves tinymist surfaces a real primary diagnostic instead of only handshaking, and `tests/scripts/smoke-tools-lsp-gate-fixtures.test.ts` was updated for the seven-fixture gated population.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- **Auto-rerun the second infra kill on one head, and sweep master-only workflow lanes (refs #2042, #3043)** — the infra-kill rerun lane now classifies attempts 1 and 2 (`run_attempt <= 2`) and keys its once-per-head rerun guard on sha *and* run attempt with a two-rerun cap, so a runner that 137-kills the same head twice no longer needs a hand-pressed attempt 3; `finalize-rerun` moves to the terminal attempt. A new registered-or-fail sweep over every `.github/workflows/*.yml` fails any job whose `if:` no pull_request context can satisfy unless it is registered with a reason, and `install-smoke`'s `smoke` and `mise-repro` each gain one pull_request cell instead of their master-only gate.
|
|
38
|
+
|
|
39
|
+
- **Record per-cgroup memory/PID/PSI samples and a 200ms watch cadence in CI (refs #2042)** — the Unit-tests job now writes a bounded on-disk tail of `memory.current`/`memory.peak`/`pids.current` and PSI stall totals every 200ms, read back by an `if: always()` step so a killed run still yields its last ~60s of samples; the `Runner capacity` step now resolves its own cgroup for `memory.max` instead of the unpopulated root.
|
|
40
|
+
|
|
41
|
+
- **Clarify merge-time defect-shape catalog allocation and add two contract screens (refs #2996)** — Keep concurrent documentation lanes from creating invalid numbered-list gaps, and record retry-list and fallback-direction failure shapes for future reviews.
|
|
42
|
+
|
|
43
|
+
- **Merge-train and reviewer contracts gain five rules from the 2026-09-12 train (refs #2996)** — catalog numbers are reserved at dispatch instead of discovered at write time; `action_required` on a fork PR is distinguished from an absent check; advisory rows are read before merge even though they never gate; a verify probes the inverted direction whenever a round retunes a threshold or tier; and a clean local run is recorded as "did not reproduce", not "fixed", when a deferred producer or another worker is involved.
|
|
44
|
+
|
|
45
|
+
- **External reports are assessed against the code and their load-bearing questions answered before dispatch (refs #3001)** — AGENTS.md's issue-triage rule now requires verifying a report's claims in the source, asking the facts that would change the design, not dispatching work that depends on an unanswered one, mirroring the answer on the issue so a worker can verify it, and citing the reporter's reasoning as authority rather than paraphrasing it.
|
|
46
|
+
|
|
47
|
+
- **Deny `TMPDIR=.probe-home` in the Bash guard hook and forbid raw commit-ish Git spawns under `tests/` (refs #3026, #3050)** — `scripts/hooks/guard-bash.mjs` gains a fifth deny rule (`tmpdirCollision`) that rejects a `TMPDIR`/`TMP`/`TEMP` assignment aimed at the vitest harness's own `PI_LENS_HOME`, the mistake that produced a false "16 suites red on origin/master" report; `tests/config/git-fixture-governance.test.ts` gains a row that fails any `tests/` Git spawn naming a commit from this repository's history, which CI's depth-1 checkout cannot reach.
|
|
48
|
+
|
|
49
|
+
- **Skip unchanged PR-body lint on synchronize (refs #3030)** — the advisory PR-body validator now runs on body-relevant pull-request events instead of every source-code update.
|
|
50
|
+
|
|
51
|
+
- **Fold the ast-grep catalog source-walk onto one effective-catalog helper (refs #3053)** — The NAPI runner and the LSP-seam rule-ignore matcher now both read `buildEffectiveAstGrepCatalog`, replacing two independent copies of the same source-walk and precedence resolution that had already drifted once (#3046).
|
|
52
|
+
|
|
53
|
+
- **Fold the tree-sitter query loader onto `js-yaml` (refs #3054)** — replace the hand-rolled line-regex YAML scanner in `clients/tree-sitter-query-loader.ts` with `yaml.load`, the same real parser `clients/dispatch/runners/yaml-rule-parser.ts` already uses for ast-grep rules, removing the duplicated inline-array/multi-line-list/nested-object branches that caused #3046's `ignore_paths` quoting bug.
|
|
54
|
+
|
|
55
|
+
- **Cut the two heaviest test files' peak memory by 8.4 GB and gate the budget (refs #3058)** — `bounded-container-guard` re-parsed and re-walked each source up to six times per container occurrence (9,207 → 812 MB peak RSS) and the `vi.mock` export detector re-parsed production modules once per call site (5,646 → 1,800 MB); the `[mem-file]` hook now fails any file that exceeds `WORKER_PEAK_RSS_BUDGET_MB` without a measured admission.
|
|
56
|
+
|
|
57
|
+
- **Enforce the DegradationKind union against every emitted kind (refs #3071)** — a new governance sweep (`tests/config/degradation-kind-coverage.test.ts`) scans every `recordDegradationOnce` / `incrementDegradationCount` / `logDurableDegradation` call site for its `kind:` literal and fails if it is not a declared `DegradationKind` union member; `clients/degradation-ledger.ts` now declares the 15 kinds that were emitted in production without a union member (`actionable-warnings-cap`, `gitleaks_classification_timeout`, `gradle-ktlint-scan-budget-exceeded`, `instance-registry-identity-fallback`, `instance-registry-lock-timeout`, `instance-registry-registration-missing`, `lsp-document-drift`, `lsp-probe-finding-policy`, `managed-tool-install`, `pipeline-post-write-hash-unavailable`, `review-graph-memory-cap`, `review-graph-memory-cap-floor`, `sgconfig-baseline-cap-evict`, `test-runner-batch-capped`, `tree-sitter-query-parse-failed`).
|
|
58
|
+
|
|
59
|
+
- **Bound the Unit tests job with `timeout-minutes` (refs #3111)** — `test` (Unit tests) in `.github/workflows/ci.yml` carried no `timeout-minutes`, so a reader that is alive but never drains (e.g. an unbounded stdout stall in the mem-watch wrapper, #3106) could hold the runner for GitHub's 360-minute default; the job now caps at 26 minutes, sized at 2x the measured longest Unit run (12m42s) across 9 successful master runs.
|
|
60
|
+
|
|
61
|
+
- **Bound every remaining workflow job with a measured `timeout-minutes` (refs #3111, closes #3123)** — the ~50 jobs across 18 workflow files left unbounded by #3111's `test` (Unit tests) fix now each carry a `timeout-minutes` sized from the job's own measured history (last 10 successful runs, `~2x max + margin`, floored at 10 minutes), so a stalled-but-alive job can no longer hold a runner for GitHub's 360-minute default; a job with no successful run in the search window (a `pull_request`/`repository_dispatch`-gated job that the master-branch sample structurally excludes, or one that has never once succeeded) gets a stated default with the reason recorded in a YAML comment above the line.
|
|
62
|
+
|
|
63
|
+
- **Close two gaps in the DegradationKind coverage sweep (refs #3071, #3138, closes #3140)** — `clients/degradation-ledger.ts` now declares `process-singleton-reset` (live at `getDegradationSummary()`'s read-time fold since #2146, previously undeclared); `tests/config/degradation-kind-coverage.test.ts` now also walks that same read-time fold's own `summary.push({ kind: ... })` emitters in `degradation-ledger.ts`, and resolves a bare-identifier `kind:` value (`TRUST_REFUSAL_KIND` at `clients/config-core/process-spec.ts`) against a same-file `const` binding rather than silently returning `[]` — an identifier the scan still cannot resolve now fails loud as `"<file>:<line> kind: <identifier> is unresolvable"` unless the site is named in the new `AUDITED_IDENTIFIER_KIND_SITES` list.
|
|
64
|
+
|
|
65
|
+
- **Check PR-body claims with Markdown-aware sentence units (refs #2904)** — Block-level Markdown and bounded source evidence keep citations and master-red claims tied to the text they describe.
|
|
66
|
+
|
|
67
|
+
- **Verify PR-body code citations (refs #2904)** — Check cited source lines, offered evidence, lexer-derived test identifiers, and master-red transcripts against the head tree. Reject backwards citation ranges before source resolution, keep valid-table master claims outside transcript checks, shield regex literals at every expression-start boundary, reuse only immutable HEAD-tree corpora within a bounded cache, and rebuild working-tree corpora so mutable trees cannot launder removed or fabricated titles.
|
|
68
|
+
|
|
69
|
+
- **Recognise PR test references by shape and placement (refs #2904)** — Test IDs, test paths, citations, and titles are checked only where their Markdown placement identifies them as test evidence. Malformed pipe blocks remain visible Markdown, while valid tables retain column-specific filtering. Local body lint tolerates shallow or single-commit histories by requiring Test assessment when changed-file scope is unavailable.
|
|
70
|
+
|
|
71
|
+
- **Keep PR-body fixture setup in process (refs #3016)** — short-ID coverage uses the existing Git callback seam without adding real child processes to the checker test.
|
|
72
|
+
|
|
73
|
+
- **Contracts from the 2026-09-10 merge train.** AGENTS.md catalog shapes 46 (a parser fed by a double whose shape the tool never emits), 47 (a detector whose corpus includes its own fixtures), 48 (check-then-act on a shared durable directory) and test screen 13 (a detector pinned only in the accept direction); the fixer contract adds "authority words are claims", "facts about master come from a fetched origin/master", "never assert CI", "a detector test has two directions", "tool output is a fixture, not a guess"; the merge-train skill adds five mistake rows (design question left to the fixer; guessed branch names; the ignored-path exclude in `git add`; `ci-verdict` exit 70 on API outages; a registry-membership guard changing other lanes' requirements). (refs #2900, #2913, #2921, #2924, #2929, #2930)
|
|
74
|
+
|
|
75
|
+
- **Retro 2026-09-15 outputs**: eleven mistake rows in the merge-train skill, a reviewer rule for net-count folds, a routing rule for governance-sweep briefs, and five steering lines deleted from the role contracts and `AGENTS.md` because they had an occasion this session and changed no decision (record in `HISTORY.md`).
|
|
76
|
+
|
|
77
|
+
- **Retrospective contract**: `docs/pi-lens-retro.md` and the `retro` skill turn a session, an incident, or a merged bug fix into environment changes — checks with a red transcript for mechanical mistakes, one contract line for judgement calls, deletion for steering lines that changed no decision — and `AGENTS.md` names the triggers.
|
|
78
|
+
|
|
79
|
+
- **Keep incomplete observation evidence separate from mutation attribution (refs #2984)** — hashless observational captures, including size-only changes, remain visible as unverifiable and never enter mutation replay, attribution, or the clean-observation latch.
|
|
80
|
+
|
|
81
|
+
- Pin the availability gate's three known text-anchoring blind spots.
|
|
82
|
+
|
|
83
|
+
### Removed
|
|
84
|
+
|
|
85
|
+
- **`decideClassifierAction` in `ci-failure-classifier.mjs` (refs #3086)** — a pure duplicate of `runClassifier`'s carry-forward rule (rerun eligibility, `rerunState`, which run attempt a carried-forward marker belongs to) with no production caller; #3079 had needed a twin test case to keep both writers honest after a mutation on `runClassifier`'s copy alone stayed green. `runClassifier` is now the sole writer of the rule, and its existing test covers the mutation on its own.
|
|
86
|
+
|
|
87
|
+
### Fixed
|
|
88
|
+
|
|
89
|
+
- **Never signal a pid pi-lens does not own (refs #2042)** — `safeSpawnAsync` verified nothing before putting a child's pid into the process-wide lifetime registry, so a `node:child_process` test double's invented pid (`2468`) was registered, never removed, and SIGKILLed at host exit; on ~10 % of CI runners that pid belonged to the job itself, which is where the unexplained exit-137 "infra kills" came from. One ownership predicate (`isOwnLiveChild`) now gates registration, the timeout process-group kill, and the LSP POSIX/Windows tree kills — folding away three sibling sign/exited guards — reading `/proc/<pid>/status` on Linux and requiring `PPid` to be this process; a live pid under another parent is refused and recorded once per call site in the degradation ledger.
|
|
90
|
+
|
|
91
|
+
- **Bind cached project findings to the bytes they were scanned from (refs #2154)** — a cheap-tier finding whose file changed while the scan was running used to be served as a current blocking error in every later session; rows now carry the size and hash of the content the rules read, so a second session sees the finding retired instead.
|
|
92
|
+
|
|
93
|
+
- **Keep advisory tool names host-aware (refs #2535)** — Agent-facing diagnostics and guidance use callable pi or MCP tool names, including generated-skip and quick-mode notices.
|
|
94
|
+
|
|
95
|
+
- **Add explicit fake LSP pull/push ordering controls (refs #2824)** — LSP tests can drive push ordering, document versions, pull outcomes, and completion through the real fixture wire.
|
|
96
|
+
|
|
97
|
+
- **Scope project-diagnostics runner retirement by analyzed file coverage (refs #2887)** — opengrep findings retire only when the retained file appears in its reported `paths.scanned` set; other runners keep the conservative runner-id fallback.
|
|
98
|
+
|
|
99
|
+
- **Isolate registry consumers and restore probe-home state (refs #2912)** — prevent later tests from inheriting the #2992 probe registry and keep PID-scoped root assertions independent of worker history.
|
|
100
|
+
|
|
101
|
+
- **Drain and track lens-map fixtures (refs #2912)** — route lens-map temporary roots through the shared environment registry so deferred graph writes cannot recreate an untracked `/tmp` root after teardown.
|
|
102
|
+
|
|
103
|
+
- **Handle PEP 668 pip hosts (closes #2916)** — pip-backed tools use pipx, a pi-lens-owned venv, or a private user base before falling back to a normal user install.
|
|
104
|
+
|
|
105
|
+
- **Complete slice-2 session guard cleanup (refs #2939)** — tool-result edit classification uses one predicate, and stale-handler crash forwarding no longer carries dead options.
|
|
106
|
+
|
|
107
|
+
- **Guard long-lived containers against unbounded growth (refs #2981, refs #2988)** — add an AST-backed registered-or-fail inventory with nested-root coverage, content-keyed exemptions, and honest key-axis classification.
|
|
108
|
+
|
|
109
|
+
- **Keep bridge bookkeeping alive across session transitions (closes #2992)** — stale extension contexts no longer make read or mutation bridge flag checks throw or drop their records.
|
|
110
|
+
|
|
111
|
+
- **Discover monorepo-root ESLint and Oxlint configs across nested `package.json` boundaries (refs #3017)** — flat `eslint.config.{js,mjs,cjs,ts,mts,cts}` files and Oxlint configs now resolve from every ancestor directory like the tools themselves, so the jsts lint lane defers Biome with the machine-readable `configured-non-biome-linter` skip reason instead of running the smart default; legacy `.eslintrc.*` and `package.json#eslintConfig` keep per-package semantics.
|
|
112
|
+
|
|
113
|
+
- **Install managed archive tools with format-aware extraction (refs #3020)** — ZIP-based clangd and PowerShell bundles, plus platform-varying Lua archives, now use the matching extractor while failed replacements preserve the installed tree.
|
|
114
|
+
|
|
115
|
+
- **Make the formatter's no-config indentation fallback stable (refs #3038)** — infer the unit from structural lines only (block-comment interiors are alignment, not nesting), decline ambiguous nested-only evidence, and honor ancestor `.editorconfig` files, so repeated formatting neither amplifies nor shrinks a file's indentation.
|
|
116
|
+
|
|
117
|
+
- **Honor a rule's own `ignores` paths over LSP (refs #3041)** — `lens_diagnostics` with `source: "lsp"` or `mode: "full"` no longer re-surfaces ast-grep findings on paths the rule's own YAML carves out, and a stored disposition now matches an auxiliary finding in `mode: "full"`.
|
|
118
|
+
|
|
119
|
+
- Fixed `tests/index-vanished-instance-wiring.test.ts` reddening in the CI Unit
|
|
120
|
+
lane on unrelated pull-request heads. The test drove the run-shared
|
|
121
|
+
`PI_LENS_HOME` registry, so the reaper's best-effort prune had to win a 500 ms
|
|
122
|
+
`withInstanceRegistryLock` deadline against every other concurrently running
|
|
123
|
+
test file; losing it is silent, left the seeded dead-pid entry in place, and
|
|
124
|
+
leaked it into the next case. Each case now runs against its own
|
|
125
|
+
`PI_LENS_HOME` (refs #3042).
|
|
126
|
+
|
|
127
|
+
- **Stop indent-retarget from basing deeper nesting on a block comment's alignment (closes #3052)** — the edit-tool indentation-mismatch autopatch no longer picks its extrapolation base unit from a JSDoc/block-comment continuation line; it now excludes comment interiors from that pick with the same lexer `indent-detect.ts` uses (#3039), so a comment's alignment ratio can no longer silently mis-scale a deeper-nested replacement. A comment-interior indent still resolves by direct lookup when a replacement reintroduces it.
|
|
128
|
+
|
|
129
|
+
- **Exclude template-literal interiors from the formatter's indentation fallback (closes #3059)** — a multi-line template literal's interior lines (a help-text string, say) were still counted as indentation evidence after #3039 excluded block-comment interiors for the same alignment-not-nesting shape, so a 4-space file whose template literal happened to be 2-space indented got re-indented to 2 on its first format. `indent-detect.ts` now excludes template interiors with the same lexical discipline as the block-comment rule, tracking `${ … }` nesting and escaped backticks so neither closes the tracked template early.
|
|
130
|
+
|
|
131
|
+
- **Re-arm the tree-sitter query parse-failure record every session (refs #3070)** — `TreeSitterQueryLoader.loadQueries` short-circuited on its `loaded`/`loadedRoot` memo before re-parsing, so a broken custom rule's `tree-sitter-query-parse-failed` degradation record only ever reached the first session that parsed it; the shared loader instance is deliberately kept across sessions, so every later session's `resetDegradationLedger()` silently dropped the row to zero. Per-file parse failures are now remembered and replayed once per ledger generation on a memoized reload, and cleared whenever a rule is actually re-parsed so a fix on disk stops replaying a stale failure. Also adds regression coverage for the existing `str()` guard against a mapping-valued `message` field, which had none.
|
|
132
|
+
|
|
133
|
+
- **Governance sweeps no longer race a scratch test file written into `tests/` (closes #3082, closes #3092)** — the `#3050` registry-isolation detector's red-first proof wrote a real `tests/scratch-3050-pre-3048-vanished-wiring.test.ts` into the repo's own test tree for the length of one assertion, so any sibling sweep that was mid-enumeration died with `ENOENT` on a file that exists on no branch (`sweep-floor-coverage`, `vacuous-skip-coverage`, `latency-logger-mock-shape` and `lsp-spawn-heavy-coverage` took the hit on rotating runs). The proof now walks a private `mkdtemp` root through the same walker and options; every walk-then-read over the `tests/` tree goes through sweep-kit's new `readWalkedFile`, which drops a path that vanished between the walk and the read, records it once, and never counts it as a finding; and a recursive-watch guard installed from `globalSetup` (once per activated Vitest project, not per test-file fork) fails the run, naming the path, if any test creates a source file inside `tests/` again.
|
|
134
|
+
|
|
135
|
+
- **Isolate test orphan-backstop state (refs #3083)** — pin the backstop lock and cooldown stamp once in the test harness so real session-start timers cannot contaminate the run-shared home, while preserving explicit test homes and production machine-wide coordination.
|
|
136
|
+
|
|
137
|
+
- **`lens_diagnostics source=lsp` honors your marks, rule policy and inline ignores (closes #3088, closes #3041, refs #3047)** — the probe lane behind `source: "lsp"`, the legacy `lsp_diagnostics` tool and the MCP `pilens_lsp_diagnostics` shim returned raw LSP findings with no stored-disposition filter, no `.pi-lens.json` `rules.<id>.disable`/`select` policy and no inline `pi-lens-ignore` suppression, while `mode=delta` and `mode=full` applied all three — so a finding an agent marked `false-positive` was hidden everywhere except the lane `skills/pi-lens-lsp-navigation` steers agents to as PRIMARY, and the probe's footer reconcile then overwrote the mark-time demotion on every check. `mode=full`'s three open-coded filter calls are folded onto one shared `clients/dispatch/finding-policy.ts` stack that the probe lane now calls once per file, for the rendered text, the counts and the widget footer alike. A mark converges whichever surface it was made from (the probe renders `[source] (code)` where the footer renders the canonical `tool`/`rule`), a `blocking` finding still drops only on a strict content-bound match, cache replays filter exactly like fresh probes, and every drop is stated as a `suppressed by disposition: N` count instead of reading as clean.
|
|
138
|
+
|
|
139
|
+
- **Drain stdin to EOF in the guard-bash PreToolUse hook instead of a single `readFileSync(0)` (#3089)** — `spawnSync`'s own `input` pump sets the child's stdin pipe non-blocking, so a `read(2)` issued before the next chunk lands can throw `EAGAIN`; `readFileSync(0, "utf8")` does not retry that, the throw was swallowed as "no payload", and the hook failed OPEN (exit 0) on a command — like `git stash` — it would otherwise have denied, measured reproducible from ~500 KB of JSON payload. `readStdin` now loops `fs.readSync` into a buffer list, retrying `EAGAIN` with an `Atomics.wait` park (the same mechanism `scripts/with-memory-watch.mjs` uses on its write side) and stopping only on a true EOF (`bytesRead === 0`); a read error or a genuinely empty stream is still "no payload".
|
|
140
|
+
|
|
141
|
+
- **`with-memory-watch` survives a full stdout pipe instead of dying on it (closes #3097)** — the wrapper writes every record with `fs.writeSync` so `process.exit` cannot discard it, but fd 1 is not reliably blocking: `stdio: "inherit"` shares one open file description with the wrapped command, and libuv sets `O_NONBLOCK` on it as soon as that child initialises its own `process.stdout` on a pipe. A sampler tick that landed while a slow log reader held the 64 KB pipe full then threw `EAGAIN` out of the `setInterval` callback with nothing to catch it, and the wrapper exited 1 under exactly the backpressure it exists to survive (CI run 35067086859). `emit` now retries on `EAGAIN`, parking 5 ms per attempt, which is what a blocking write would have done; any other write error is permanent, so it is recorded once on stderr and the child's exit code is still forwarded.
|
|
142
|
+
|
|
143
|
+
- **A mis-cased path and a normalized read now share one anchor on POSIX (refs #3098, refs #3090)** — `normalizeFilePath` canonicalized casing only on Windows, so on a case-insensitive POSIX filesystem (macOS APFS, `nocase` vfat/ntfs3/cifs) a raw mis-cased `lens_diagnostic_mark` write and the dispatcher's `normalizeMapKey` read derived two anchors for one file and the agent's own false-positive/flagged mark silently never applied — the #1024 defect, live on every macOS install. The POSIX arm now adopts the on-disk casing `realpath(3)` reports for the trailing segments of an existing path, stopping at the first segment that differs by more than case, so casing is fixed while a symlinked prefix (macOS's `/var/folders` tmpdir, a symlinked monorepo package) is still never resolved; a path that does not exist stays case-preserving, because on a case-sensitive filesystem `SUB/a.ts` and `sub/a.ts` are two different files. A rewrite is adopted only once the filesystem confirms it still names the file the caller held, so a symlink whose name is a case variant of its target's (`node_modules/Foo` → `../pkgs/foo`) keeps its own spelling rather than keying under a sibling — or under nothing. The #1024 regression test now runs unskipped on the Linux CI lane instead of skipping there and failing on macOS.
|
|
144
|
+
|
|
145
|
+
- **The turn-end late-auxiliary advisory and the cold-neighbour cascade run honor a disposition mark (#3102)** — both rendered LSP findings straight to the agent with no stored-disposition filter, no `.pi-lens.json` `rules.<id>.disable`/`select` policy and no inline `pi-lens-ignore`, so a finding marked `false-positive` came back on every turn that drained a late auxiliary pair and on every quiet-window cascade reconcile. Both now take the same `clients/dispatch/finding-policy.ts` stack the per-edit dispatcher, `mode=full` and the `source=lsp` probe lane apply, after their own freshness gate and over the canonical identity (`convertLspDiagnostics` + `retagAuxiliaryDiagnostics`) every other surface anchors a mark against — so an auxiliary finding is filtered under its real tool id instead of a hardcoded `lsp`, and its own native `# nosemgrep`-style suppression is honored here too. Each lane reads the cited file once per delivery, only when there is something to render; an unreadable file degrades to the content-free half rather than hiding a finding. Drops are counted in the `late_auxiliary_findings` and `cascade_finding_policy` latency records, and a delivery that still has findings states `suppressed by disposition: N finding(s)`.
|
|
146
|
+
|
|
147
|
+
- **Vanished-file diagnostic reaches the run log on a passing suite (refs #3107)** — `tests/support/sweep-kit.ts`'s `readWalkedFile` and its `scripts/lib/win32-gate-population.mjs` twin recorded a walked file that vanished between list and read with `console.warn`, but Vitest's default reporter (every `npm test` script uses it) intercepts and can drop a worker's `console.warn` on a green run, so the record never actually reached CI's job log despite the docstring's claim that it does. Both sites now emit through a raw `process.stderr.write`, matching the same interception fix already documented at `tests/support/vitest-setup.ts`'s peak-RSS reporting. The `.mjs` twin's hand-rolled `Set` with `clear()`-on-overflow eviction — which its own parity comment falsely claimed was "identical" to the TypeScript seam — now shares the TypeScript seam's `BoundedSet` (oldest-first eviction), making the claim true.
|
|
148
|
+
|
|
149
|
+
- **Reclaim stale `tmp-hygiene-baseline-<run>.json` files on the existing backstop stale arm (refs #3109, #2912, #3100)** — the #2912 hygiene owner writes one baseline record per run and only ever removed the one it wrote itself, so every owner-less (targeted) run in a persistent dev-box checkout left its own file behind for ever; `removeRunBackstopDirs` now reclaims a `tmp-hygiene-baseline-*` file older than the same six-hour `BACKSTOP_STALE_MS` window it already used for abandoned backstop directories and root-level `orphan-backstop*` residue, leaving the active run's own file untouched until the owner consumes it.
|
|
150
|
+
|
|
151
|
+
- **Retry the mem-watch write-failure note through the same channel that retries every other line, with a bound of its own (closes #3110, closes #3115)** — `noteWriteFailureOnce` used to write its once-only stderr note with a raw, un-retried `fs.writeSync` in a bare catch, so a stdout that had died (EPIPE) alongside a stderr that was merely full (EAGAIN, a slow reader) dropped the note for good; it now goes through the wrapper's own retrying `emit()`, capped at 400ms for the note only (the verdict line keeps its unbounded retry) so a stderr that never drains at all degrades the note instead of hanging the wrapper and losing the wrapped command's exit code, when the wrapped command outlives the retry window (a command that exits inside it still blocks on the inherited fd; see #3141). `tests/scripts/with-memory-watch.test.ts`'s `runWrapper` helper also pins `PI_LENS_MEM_WATCH_SAMPLE_FILE` to a scratch path by default, so the file's cases stop leaking the wrapper's shared `pi-lens-mem-watch-samples.log` fallback into TMPDIR and redding `tmp-fixture-hygiene` in a shared batch.
|
|
152
|
+
|
|
153
|
+
- **Nested agent-data trees no longer cost a project its startup scan, and a project-scope `tools.<name>.enabled` is no longer called invalid (fixes #3112)** — `.omp` (the same coding agent's rebranded config directory: pi derives its config-dir name as `pkg.piConfig?.configDir || ".pi"`, so `.omp/agent/sessions` is the history `.pi/agent/sessions` holds) joins the shared scan-exclusion list, so a project-local session history is no longer walked entry-by-entry against the startup entry budget — a small source tree beside one came back `too-many-entries` and never warmed its caches. Excluding it by NAME during recursion leaves a `.omp` directory chosen as the workspace root scanned as usual, and an ordinary oversized tree keeps its `too-many-entries`/`too-many-source-files` verdict. Separately, the project-config loader derived its accepted top-level keys from the flag registry alone, so the documented project-scope `tools.<name>.enabled` override was announced as `"tools" is a global-only pi-lens setting … ignored` (`PILENS_CFG_0001`) on every load; `tools` is now a recognized project section, and the one genuinely global key under it (`tools.lazy`, `--no-lazy-tools`) is still reported at project scope by a registry-derived mixed-scope sub-key scan rather than being dropped in silence.
|
|
154
|
+
|
|
155
|
+
- **Stop indent-retarget from basing deeper nesting on a template literal's alignment (closes #3116)** — the edit-tool indentation-mismatch autopatch no longer picks its extrapolation base unit from a multi-line template literal's interior line; it now excludes template interiors from that pick with the same lexer `indent-detect.ts` uses (#3059), so a template literal's own alignment ratio can no longer silently mis-scale a deeper-nested replacement, the same way #3052 fixed the analogous block-comment case. A template-interior indent still resolves by direct lookup when a replacement reintroduces it.
|
|
156
|
+
|
|
157
|
+
- **Recognise regex literals in the indentation template-lexer's conservative state (refs #3120)** — `advanceTemplateState` had no notion of a regex literal, so a backtick inside one (`` /`/ ``) opened a phantom template frame and a `/*` inside one (`` /[/*]/ ``) pushed a phantom block frame that never finds its closing `*/`; both silently disabled template-interior masking for the rest of the file. `/` is now recognised as a regex opener only in unambiguous grammar positions (after `(`, `,`, `=`, `:`, `[`, `!`, `&`, `|`, `?`, a `return`/`typeof`/`case` keyword, or line start — `{`, `}`, `;` were tried and dropped as vacuous, `}` additionally grammatically unsound) and skipped whole to a closing `/` that is itself followed by an unescaped `` ` ``-honouring, `[...]`-honouring scan and then zero or more regex flag letters and a non-identifier terminator — division stays division everywhere else, unchanged from before. A 25,553-file corpus re-run (own tree plus `node_modules`, every extension biome/prettier/ruff/shfmt format) shows zero files newly declined and no new `width 1` verdicts; the `decline` count falls from 7 to 4 and 9 files change verdict overall — 8 of those lose all surviving indentation evidence to now-correctly-recognised regex/template spans and fall through to the existing hardcoded default, and one in-tree file (`clients/ast-grep-client.ts`) is a genuine correction from a stray-regex-corrupted `space/2` to its real `space/4`.
|
|
158
|
+
|
|
159
|
+
- **A global-only `actionableWarnings.autoFix.maxFixes` written in a project `.pi-lens.json` is no longer dropped in silence (fixes #3131)** — `#3112`'s mixed-scope sub-key scan derived its population from the flag registry alone (`LENS_FLAGS`, booleans only), so a global-only setting with no CLI flag counterpart — `actionableWarnings.autoFix.maxFixes`, documented global, read only through `getGlobalActionableWarningMaxFixes()` — was invisible to it even though its section (`actionableWarnings`) is recognized at project scope for its sibling `autoFix.enabled`. A new single-source registry, `GLOBAL_ONLY_NON_FLAG_KEYS` beside `LENS_FLAGS` in `clients/lens-flag-registry.ts`, names non-flag global-only dotted keys the same way the flag registry names flag ones; the scan now derives its full population from both, so a project file setting `maxFixes` gets the same one-time `PILENS_CFG_0001` global-only notice `tools.lazy` does, and the value still is not honored at project scope.
|
|
160
|
+
|
|
161
|
+
- **The in-lane per-edit cascade block honors a disposition mark (#3157)** — the four sites that build the agent-facing cascade neighbour list in `clients/dispatch/integration.ts` (passive cold snapshot, fresh in-lane touch, touch-error fallback, degraded fallback) rendered raw language-server findings: no inline `pi-lens-ignore`, no stored disposition, no `.pi-lens.json` `rules.<id>.disable`/`select` policy, and no auxiliary re-tag. So a neighbour ERROR the agent had marked `false-positive` was hidden by `mode=delta`/`mode=full`/the per-edit dispatcher/the probe lane/the quiet-window cascade run (#3102) and still came back on every edit that cascaded to that neighbour. All four now route through one shared `applyCascadeDisplayPolicy` seam — the same `clients/dispatch/finding-policy.ts` stack over the canonical `convertLspDiagnostics` + `retagAuxiliaryDiagnostics` identity — which the quiet-window cascade run now shares too, so the two cascade lanes agree on auxiliary findings as well: both honor the profile's own native `# nosemgrep`-style suppression and `skipTestFiles` gate instead of one lane rendering what the other drops. The disposition store and the rule policy are read from the PROJECT root rather than the cascade's language root, so a mark still applies in a monorepo. The per-neighbour display cap now applies to the SURVIVORS rather than to the raw list, so a suppressed finding can no longer consume a display slot — before this, a neighbour whose first 20 errors were all marked rendered nothing at all while genuine unmarked errors sat below the cap. The neighbour is read once, and only when it actually has an ERROR to render (measured: 0.14 ms per delivering neighbour with no marks in the store, 1.3 ms with marks, worst case 4.7 ms at the bounded input of 80 findings, against the cascade's ~100 ms-per-neighbour walk budget); an unreadable neighbour degrades to the content-free half rather than hiding a finding. Drops are counted in one `cascade_finding_policy` latency row per cascade run, and a block that still has findings states `suppressed by disposition: N finding(s)`. When the per-neighbour policy input bound leaves findings unevaluated, the cascade block says so to the agent (`⚠️ Cascade did not evaluate N finding(s) … no findings does NOT mean clean here`) instead of rendering an empty block that reads as a clean verdict — a single `.pi-lens.json` rule disable over a neighbour with many findings of that rule was enough to reach it.
|
|
162
|
+
|
|
163
|
+
- **A file created under a spelling the filesystem re-cases keeps its write record (refs #3163)** — the read guard announced a pending creation while the file was still absent and looked the announcement up after it landed, both times through `normalizeFilePath`, whose answer depends on that very existence. Any spelling whose key moved as the file appeared (on Windows every new file with an upper-case letter in its name; on POSIX a parent directory reached through a case-variant symlink) orphaned the announcement, so the synthetic creation read was never injected and the just-written file lost the outstanding-read protection that keeps it editable across an idle session. The announcement is now keyed by the existence-independent syntactic spelling the guard already uses for its post-delete index.
|
|
164
|
+
|
|
165
|
+
- **The release workflow's publish toolchain is now exercised before a release (closes #2940)** — `release.yml`'s publish job runs npm through the version pinned in `package.json`'s `packageManager`; since the pin moved to `npx`, a bare `npm publish` silently used the runner's bundled npm, which has no OIDC trusted-publishing support, and the v4.1.6 release created its tag and GitHub release before the registry answered E404. The publish job now asserts the pinned npm's version in the step immediately before publishing, `prepare`'s dry-run publish goes through the same pinned invocation, a governance test reds on any bare `npm <verb>` in either job, and the release-QA matrix gained a `publish-toolchain-pinned` row that runs that toolchain against the candidate before a tag exists. The row treats npm's already-published response as a toolchain witness, records registry-unreachable failures as inconclusive, and reports the underlying npm cause instead of its log path. Round 3 records that the existing `npm()` and `pinnedNpm()` wrappers cannot be folded without changing `execFileSync` buffering: only the pinned wrapper sets `maxBuffer: 10 * 1024 * 1024` (refs #2963).
|
|
166
|
+
|
|
167
|
+
- **PR-body test-reference check stops flagging commands, output, and prose (closes #3013)** — Recognise test references positively instead of treating every backticked phrase as one.
|
|
168
|
+
|
|
169
|
+
- **Keep fork pull requests out of token-capped CI writes (closes #2993)** — stale verdict-label cleanup is advisory and skips fork heads; merged-PR close-keyword comments use the uncapped target trigger.
|
|
170
|
+
|
|
171
|
+
- **Widget disposition reconciliation is admission-ordered (refs #1616)** — Marking a diagnostic disposition immediately reconciles the widget counts and retains suppressed findings as a visible bucket. Reconciliation now shares the same admission-ordered write tokens as dispatch, so a dispatch that was already in flight can no longer commit its pre-mark counts afterwards.
|
|
172
|
+
|
|
173
|
+
- **Share explicit analysis-root validation across heavyweight diagnostics consumers (refs #2053).**
|
|
174
|
+
|
|
175
|
+
- **Preserve the analysed file state across concurrent writes (refs #2499)** — The dispatch latch now records the state the pipeline actually analysed. The LSP batch pool preserves its abort contract: unstarted files do not add placeholder outcomes to aggregate diagnostics.
|
|
176
|
+
|
|
177
|
+
- **Directory-target observed codemods are analysed over rewritten files (closes #2500)** — Dispatch same-turn pipeline analysis for the recorded files inside a directory, with a bounded fan-out and visible truncation counts.
|
|
178
|
+
|
|
179
|
+
- **Measure stale async test-runner verdicts (refs #2542)** — delivered verdicts record their run and delivery file sequences, and the log analyzer reports known-verdict stale rates with unknown counts per session.
|
|
180
|
+
|
|
181
|
+
- **Pin backstop assertions to their sweep row (closes #2669)** — Grace retries no longer make the orphan backstop regression test read metadata from a later sweep.
|
|
182
|
+
|
|
183
|
+
- **Register only the read range the host delivered (refs #2802)** — Read-guard registration now uses the lines the host actually returned, including output-cap and EOF-clamped reads. A native read's provisional record is replaced by correlated tool-call identity rather than by position, so two reads of one file no longer leave the wider requested span behind and an unrelated search credit is never consumed. A delivered file that has disappeared no longer aborts the rest of the result chain, and an opaque write that leaves content identical no longer marks the file agent-authored. When bash observation evidence is unavailable, synthetic writes preserve authoritative content handling without granting read-guard authorship or edit permission.
|
|
184
|
+
|
|
185
|
+
- **Keep CI verdict labels fresh across pushes (refs #2856)** — Clear verdict labels when a pull-request head changes, explicitly scope checkout-free `gh` calls to the repository, and ignore incidental exit-137 log text as test-failure evidence.
|
|
186
|
+
|
|
187
|
+
- **Project data directory slug carries a path hash (closes #2874)** — Projects whose paths differ only in separator placement versus a hyphen no longer share one data directory; on first use after upgrade, the existing directory is renamed once so caches and session state carry over.
|
|
188
|
+
|
|
189
|
+
- **Require a SQL sink signal for TypeScript SQL injection findings (closes #2909)** — Ignore unrelated child-process and task-runner template calls while retaining blocking diagnostics for imported database clients and SQL-leading templates.
|
|
190
|
+
|
|
191
|
+
- **Marker-root freshness (refs #2922)** — Resolve the nearest marker with one synchronous walk per lookup, preserving visibility of nearer, created, and deleted markers without charging a redundant positive-cache rewalk.
|
|
192
|
+
|
|
193
|
+
- **Unify test filename classification (refs #2928)** — Apply the shared classifier to runner skip gates and word-index relevance, including Go, PHP and Ruby test names. Preserve vendor penalties and fixture/mock skip rules; recognize direct children of `__tests__`. `isTestFile` (secrets scanner, dispatcher, tree-sitter runner, and the pass-through-wrappers/async-noise/placeholder-comments rule gates) now also applies file-role's directory policy — `/spec/`, `/specs/`, and a bare `test`/`tests`/`spec`/`specs` directory segment (`my-test/`, `integration-test/`, `e2e_tests/`), case-insensitively — which its own deleted arms never covered.
|
|
194
|
+
|
|
195
|
+
- **Opengrep scan refusal reporting (closes #2943)** — Canonicalize symlinked workspace roots, preserve findings from partial-parsing warnings, classify every report and spawn failure once, and surface refused scans as cold diagnostics with machine-readable reasons. The outcome matrix now pins clean non-zero exits, every report discriminator, and the cached empty-coverage shape.
|
|
196
|
+
|
|
197
|
+
- **Anchor the test-runner child on the module its command runs in (refs #2944)** — every runner's spawn-cwd markers must name one of the runner's own manifests or the launcher its command actually invokes; the contract is now derived and tested over the whole runner table, and the stale claim that Maven's `mvn` command anchors on its `mvnw` wrapper is corrected. The Maven anchor itself landed with the #2965 marker-seam fix.
|
|
198
|
+
|
|
199
|
+
- **Keep side-effect autofixes out of target identity (refs #2957)** — Pipeline results no longer claim a post-write hash for the target when an autofix changed only another file.
|
|
200
|
+
|
|
201
|
+
- **Harden backstop retry regression coverage (refs #2958)** — Seed prior latency rows and await the unref'd grace retry through a bounded completion seam.
|
|
202
|
+
|
|
203
|
+
- **Keep findings when a partial scan has no coverage paths (refs #2962)** — a warning-level partial Opengrep report remains visible with an incomplete-coverage note and never retires retained findings as if it were a clean empty scan.
|
|
204
|
+
|
|
205
|
+
- **Share language markers across runner cwd resolution (closes #2965, closes #2966, closes #2964)** — runner fallbacks use the language marker vocabulary, cwd resolution returns its anchoring marker, and one dispatch pass reuses only its `.git` fallback.
|
|
206
|
+
|
|
207
|
+
- **Bound the spawn usage sampler's concurrency, rate and lifetime (refs #2968)** — a poll tick no longer starts while the previous one is still querying the process table, the interval backs off once a child outlives the short-lived window, and polling stops at a cap derived from the spawn's own deadline; on Windows this ends the `powershell.exe`/`taskkill.exe` pile-up behind a child that never exits, and skipped ticks and capped samplers are counted on the degradation ledger.
|
|
208
|
+
|
|
209
|
+
- **Classify CUDA/HIP `.cuh` headers (refs #2986)** — Route community-standard `.cuh` headers through the C++ language, grammar, file kind, and clangd server projections.
|
|
210
|
+
|
|
211
|
+
- **Decline ktlint autofix when project agreement is unavailable (refs #3000)** — when pi-lens cannot establish agreement with a Gradle- or Spotless-managed CLI, including convention plugins in `buildSrc` or included builds, it leaves the file unchanged and records one bounded session notice. The Gradle convention-plugin ownership walk stops at 10,000 directory entries; an exceeded scan is an indeterminate ownership result, declines before command resolution, and records one `gradle-ktlint-scan-budget-exceeded` notice per session.
|
|
212
|
+
|
|
213
|
+
- **Require project tool agreement before autonomous writes (refs #3005)** — autofix, formatting, and deferred LSP quickfixes use one cached agreement seam and a complete declarative tool population. Node agreement follows each resolver's package identity, including markdownlint-cli2. Missing, unreadable, unparseable, and unsupported project evidence declines conservatively, with bounded degradation records. Lockfile evidence declines with distinct reasons when a version is unparseable or a legal shape is unsupported. LSP quickfix agreement preserves the diagnostic producer from `source`, falling back to `serverId`, while unsupported identities remain fail-closed.
|
|
214
|
+
|
|
215
|
+
- **Confirm same-size all-LSP blocker rewrites (refs #3011)** — all-LSP blocker baselines now use content hashes even when a rewrite does not advance the recorded mtime.
|
|
216
|
+
|
|
217
|
+
- **Adopt NDJSON rotation bounds across extension reloads (closes #3012)** — an older unbounded writer adopts incoming bounds during module-graph re-evaluation, while conflicting bounded policies retain first-writer ownership and emit one bounded health record.
|
|
218
|
+
|
|
219
|
+
- **Compile against `@types/node` 26.5.1 (refs #3026)** — `node:perf_hooks` no longer exports the `IntervalHistogram` type, which broke `tsc` (TS2305) and with it every CI lane that builds. The event-loop monitor now infers the histogram type from `monitorEventLoopDelay`; emitted JavaScript and runtime behaviour are unchanged.
|
|
220
|
+
|
|
221
|
+
- **Require chart topology for Helm YAML classification (refs #3034)** — YAML files under `templates/` remain ordinary YAML unless an ancestor contains `Chart.yaml`; explicit `.tpl` helpers retain their Helm classification.
|
|
222
|
+
|
|
223
|
+
- **Stale inline blockers from non-LSP analyzers (closes #2982)** — The turn-end freshness sweep skipped every record whose provenance was not all-`lsp`, so a tree-sitter or ast-grep blocker was never checked against its own file. Combined with a retire path that can only claim coverage for registered language servers, such a record could not be cleared by any number of clean diagnostics runs and re-served every turn for the rest of the session. The sweep now checks the record's own file for any entry that carries provenance, keeping the forward-import walk for LSP-sourced records only; records with no recorded sources stay fail-closed. The check is confirmed against content at two tiers, size then a content hash, so a `touch`, a checkout restoring identical bytes, or a no-op formatter pass no longer demotes a finding while a same-length edit still does. It re-arms, so bytes that come back un-demote the record, and self-drift demotions sit outside the repeated-delivery cap that retires dependency-drift records, which keeps a security finding from being retired out of turn-end rendering. The content baseline is carried beside the inline-blocker evidence from the pipeline's existing file read and stamped synchronously with the record. All sweep filesystem work is bounded by the turn-end hook's own signal. Missing baselines and expired bounds emit bounded, file-identified ledger records, while aggregate hash-budget exhaustion stays sweep metadata. A demoted record still gates a commit, unchanged.
|
|
224
|
+
|
|
225
|
+
- **Classify YAML files under Helm `templates/` directories as Helm templates** — pi-lens no longer sends raw Helm Go templates to the YAML language server, eliminating cascading false-positive YAML parse diagnostics while retaining Helm lint and render validation.
|
|
226
|
+
|
|
227
|
+
- Put pnpm 11's resolved global bin directory (`$PNPM_HOME/bin`) on PATH in
|
|
228
|
+
install-smoke's pnpm setup, and run one `pi-load` pnpm-global cell on
|
|
229
|
+
pull requests so the lane cannot ship untested again.
|
|
230
|
+
|
|
231
|
+
- Fix install-smoke's pnpm global-bin setup for pnpm 11 projects declaring npm by writing the setting globally in both pi-load and mise-repro.
|
|
232
|
+
|
|
233
|
+
- Restrict install-smoke pull-request runs to the `master` base branch and
|
|
234
|
+
keep the parsed workflow contract aligned with that gate.
|
|
235
|
+
|
|
236
|
+
- Fix per-hook tool-result budget classification, formatter attribution, and deferred-format delivery.
|
|
237
|
+
|
|
238
|
+
- **Keep lifecycle hooks within per-hook wall budgets (refs #2523)** — bound aggregate formatter work and preserve unfinished diagnostics through off-hook delivery.
|
|
239
|
+
|
|
240
|
+
- **Keep latency-logger test mocks faithful to production exports (refs #2281)** — latency-logger mocks preserve the real module surface, and a conformance sweep catches omitted exports.
|
|
241
|
+
|
|
242
|
+
- **vi-mock export sweep reads `importOriginal<T>()` as a pass-through** (closes #2881) — a `vi.mock` factory spreading `await importOriginal<typeof import("…")>()` no longer reports every export as dropped; the parser also recognises `as`/`satisfies` casts, parenthesised spreads, and the two-statement `const actual = await importOriginal<T>()` binding as the same complete pass-through.
|
|
243
|
+
|
|
244
|
+
- **Stop the /lens-perf occupancy guard flaking under CI contention (closes #2886)** — The wall-clock occupancy assertion moves into the serialized `wall-clock-budget` lane with the flake-shape ratchet's admission, and a deterministic yield-count assertion is added beside it: the count pins the parser's cadence, the lane keeps the real measurement honest.
|
|
245
|
+
|
|
246
|
+
- **Keep deferred fixture roots tracked until their final drained cleanup pass (refs #2912)** — shared per-family cleanup drains deferred producers before untracking roots.
|
|
247
|
+
|
|
248
|
+
- **Drain deferred jscpd fixture cleanup (refs #2912)** — jscpd test teardown keeps the shared `pi-lens-jscpd-` owner stem registered across bounded macrotask drains, covering every jscpd output-root prefix in the suite.
|
|
249
|
+
|
|
250
|
+
- **Remove test scratch directories on exit (refs #2912)** — the generic process-wide fixture sweep was attempted twice and reverted after broad suite breakage; proven per-family sweeps remain, and unresolved prefixes enter an environment-independent prefix-set ratchet with named producers. The serialized hygiene owner records and removes newly-created `pi-lens-*` fixtures, the MCP harness removes IPC endpoints after child exit, release-QA accepts an owned `--scratch-root` with signal cleanup, ast-grep baseline scratch is bounded, generated ast-grep scan directories are collision-proof with preparation cleanup on partial setup failure, reverse-deps and agent-end summary fixtures drain several macrotasks before cleanup, and the session-start full-mode producer remains explicitly admitted.
|
|
251
|
+
|
|
252
|
+
- **Spawn-cwd scanner derives one vocabulary from the seam and freezes (closes #2927)** — the scan's site rule and the sweep's population predicate both derive from the `SPAWN_NAMES` / seven-name `NODE_SPAWN_NAMES` tuples, with per-name parity tests; the `// cwd-exempt:` channel is deleted with its self-tests after #2911 removed its last production tags; the scanner header states the simplify-not-extend freeze policy.
|
|
253
|
+
|
|
254
|
+
- **Keep the #2992 regression probe out of system temp (refs #2912)** — the read-bridge lifecycle test stores its ledger home under the ignored worktree-local `.probe-home` directory, so asynchronous bookkeeping cannot recreate a leaked top-level `/tmp` fixture.
|
|
255
|
+
|
|
256
|
+
- **Isolate `instance-reaper-unref` from the run-shared `PI_LENS_HOME` (refs #3050)** — `sweepUntrackedOrphans` takes `<PI_LENS_HOME>/orphan-backstop.lock` and reads the 30-minute `orphan-backstop.json` cooldown stamp before it enumerates anything, so a sibling Vitest fork's real `session_start` (`tests/index-integration.test.ts` arms 37 `scheduleUntrackedOrphanSweep()` timers through `index.js`) left a fresh stamp and the unref test's sweep returned `cooldown` with zero spawns — master 038e28b's only Unit failure. Each case now pins its own `mkdtemp` home, and `#3050`'s detector, which had recorded a two-character body for every declaration with a braced default parameter value and so never saw `sweepUntrackedOrphans`, now finds a body brace past the parameter list.
|
|
257
|
+
|
|
258
|
+
## [4.1.6] - 2026-09-10
|
|
259
|
+
|
|
260
|
+
### Added
|
|
261
|
+
|
|
262
|
+
- **Add an advisory incremental Stryker lane for changed scripts (refs #1844)** — `npm run mutation:diff` and the pull-request mutation lane use Stryker's command runner over changed `scripts/**/*.mjs` files and their related tests, report uncovered files, retain incremental results under ignored paths, and publish the mutation report for review.
|
|
263
|
+
|
|
264
|
+
- **CI failures now distinguish infrastructure outages from real test failures and arm one rerun** (refs #2103, #2784) — the classifier recognizes exit-137 kills, CodeQL/SARIF outages, codeload throttling, and npm CI timeouts, while preserving assertion and TypeScript failures as real. It updates one PR comment, applies the manifest-managed CI label, and lets the verdict report an armed infrastructure rerun as pending; **Terminal CI rerun finalization now covers master push and repository-dispatch runs** (refs #2103, #2784) — the workflow finalizes second-attempt classifications for every supported event and records a bounded step summary when no associated PR exists.
|
|
265
|
+
|
|
266
|
+
- Add an AST governance sweep for `vi.mock` factories that omit production exports. Refs #2281.
|
|
267
|
+
|
|
268
|
+
- **Add an advisory Windows Vitest lane (refs #2536)** — Run the path-shape and configuration test subset on `windows-latest`.
|
|
269
|
+
|
|
270
|
+
- **Add the tool-smoke install lane to the release-QA baseline (refs #2663, #2784)** — a `tool-smoke-install` row installs every npm/pip installer-registry entry through the real smoke harness, so a dead registry entry is a red do-not-ship row, a registry-unreachable lane is INCONCLUSIVE, and neither reads as green.
|
|
271
|
+
|
|
272
|
+
- **Add a TypeScript strictness spike with scratch configs, per-directory counts, a two-sided ratchet, and an advisory report lane (refs #2697)** — the migration remains staged by directory instead of enabling either flag globally.
|
|
273
|
+
|
|
274
|
+
- **Add a cwd-sensitive yamllint tool-smoke row (refs #2697, refs #2691)** — the real runner proves yamllint uses the dispatch cwd instead of the host process cwd for config discovery.
|
|
275
|
+
|
|
276
|
+
- **Add tool-smoke cwd rows for nested discovery (refs #2777)** — the runner, formatter, and LSP smoke shapes assert their resolved cwd and reason log, while missing binaries remain visible skips.
|
|
277
|
+
|
|
278
|
+
- **Add one command for every mechanical PR gate (refs #2790, #2784)** — `npm run preflight` runs the ordered local build, lint, formatting, changelog, lockfile, governance, and PR-document checks.
|
|
279
|
+
|
|
280
|
+
- Stamp one session-scoped turn identifier on observability sink rows (refs #2815).
|
|
281
|
+
|
|
282
|
+
- **Add a real pi RPC harness for load, tool-set restore, and read-guard scenarios (refs #2825)** — the serialized `real-harness` lane drives the built extension with a deterministic scripted provider and hermetic sinks.
|
|
283
|
+
|
|
284
|
+
- **Make Knip a hard local preflight gate and cover every admitted test lane** (refs #2837) — `npm run preflight` runs Knip, while the Knip entry census covers real-harness, Windows, wall-clock, and workflow-invoked script populations with documented same-file export admissions.
|
|
285
|
+
|
|
286
|
+
- **Gate nightly LSP smoke rows on primary diagnostics** (refs #2780) — the tool-smoke workflow now drives opted-in seeded fixtures through the real `lsp_diagnostics` handler, reports unavailable toolchains correctly, and tracks gate failures.
|
|
287
|
+
|
|
288
|
+
- **`complexity (advisory)` CI job dogfoods pi-lens's own complexity client over `clients/`, `tools/` and `mcp/` (refs #2697)** — `npm run complexity` writes a Markdown report (top functions by cyclomatic/cognitive complexity, files over 1,000 lines, split candidates at the dispatch threshold of 15) to the step summary and an artifact; `ComplexityClient` exposes the per-function metrics it already computes, uses the dispatch cyclomatic metric for JS/TS, and fails when analysis produces no files or throws.
|
|
289
|
+
|
|
290
|
+
- **One session-end line names the tools never activated or called (refs #2800)** — a bounded debug record per session on both surfaces, keyed by the tool registry, so roster decisions can read which tools carried no weight in a session; the monitor readout gains the line. MCP owns the row for the connection and emits it at `pilens_session_end` or transport close, whichever comes first; repeated `pilens_session_start` calls refresh without clearing observations. Pi records the row for fresh sessions only: a reload, resume, or fork start suppresses the session's row entirely (conversation-owned accounting tracked in #2858).
|
|
291
|
+
|
|
292
|
+
- **Per-call tool-result observability**: the `usage tokens=<n> elapsed-ms=<n>` contract footer gains `bytes=<delivered payload bytes> truncated=<true|false>` on both host surfaces, the payload byte bound runs before the footer is stamped with the footer's own maximum size reserved so the delivered text (footer included) never exceeds 40 KiB for the single-text-block results production emits (the bound is per text block, inherited from #2852), and the per-turn `cache_usage` latency row aggregates `toolResultBytes` and `toolResultsTruncated` over the turn's tool calls (refs #2800)
|
|
293
|
+
|
|
294
|
+
- **Guard workflow shell portability (refs #2625, #2784)** — Reject bash 4-only constructs in steps that can run on macOS runners, including matrix and reusable-workflow input expansion and executable command substitutions nested inside double quotes.
|
|
295
|
+
|
|
296
|
+
- **Record injected bytes and repeated findings per turn (refs #2838)** — the existing cache attribution row records UTF-8 bytes by model-facing injection source and counts repeated finding identities with bounded per-session state.
|
|
297
|
+
|
|
298
|
+
- **Check lockfile completeness under CI's npm (refs #2803, #2784)** — preflight and `check:lockfile --complete` reject optional dependency drift before merge; **Pin the clean production install to npm@11.18.0 (refs #2803, #2784)** — use
|
|
299
|
+
the repository's exact npm pin so a different npm version cannot rewrite the
|
|
300
|
+
committed lockfile.
|
|
301
|
+
|
|
302
|
+
- **`tools.<name>.enabled` proven through a real pi RPC session (refs #2800)** — real-harness scenarios show a disabled tool absent from the tool list pi reports, the loader and MCP lifecycle tools refusing to be disabled with the config diagnostic, and `--no-tool` winning over config; the roster bytes pi receives per tool are measured on the wire.
|
|
303
|
+
|
|
304
|
+
### Changed
|
|
305
|
+
|
|
306
|
+
- Keep admission registries sorted and enforce declaration order with one shared sweep helper (refs #2671, #2784).
|
|
307
|
+
|
|
308
|
+
- **Skip the PR title, body and close-keyword checks for dependabot PRs (refs #2714)** — the `pr-title-lint` and `pr-body-lint` jobs in lint.yml and the lint job in close-keywords.yml now carry a `dependabot[bot]` skip condition. A bump can never carry an issue ref or the PR-body template, so these checks no longer go red on every bump and no merge-train pass has to ignore them by hand. Lint, Unit tests, the install tests and the advisory lanes still run on bumps.
|
|
309
|
+
|
|
310
|
+
- **Trim the three largest tool parameter schemas and pin both host rosters (refs #2800).**
|
|
311
|
+
|
|
312
|
+
- **Adaptively demote budget-hitting auxiliaries, re-promote fast late answers, and deliver findings through the deferred runner path (refs #2810).**
|
|
313
|
+
|
|
314
|
+
- **Tool probes and hand-derived package roots resolve through shared seams (refs #2894)** — a `probeToolAsync` seam owns the "a presence/version probe never gets a `cwd`" contract for 23 spawn sites that each decided it locally, `rust-clippy` takes its package root from `resolveRunnerCwd` (with `Cargo.toml` registered as its runner marker) instead of an uncapped `findNearestContaining` walk, and `ruff-client`'s autofix resolves its config root through `resolveToolCwd` so a file in a nested package gets that package's `pyproject.toml` rather than the workspace one.
|
|
315
|
+
|
|
316
|
+
- **Preserve per-row auxiliary publication evidence semantics (refs #2892)** — demoted waits accept an advanced per-path stamp alongside a content binding, while sibling and aggregate rows remain binding-only.
|
|
317
|
+
|
|
318
|
+
- **`ast_grep_dump` folded into `ast_grep_search` (refs #2800)** — AST inspection is `ast_grep_search` with `dump=true`, with the same result contract; the retired name answers for one session through a bounded compatibility response on both surfaces (one `ast-grep-dump-compatibility` record per session) and is absent from the tool registry and every tool listing.
|
|
319
|
+
|
|
320
|
+
- **Audit and update user-facing and contributor docs against master for the v4.1.6 cut.** `docs/agent-tools.md` now describes `lens_diagnostics`' `source`/`scope`/`severity`-threshold contract, the one-release MCP compatibility redirect, the `lsp_navigation` 19-operation roster with a link to its SKILL.md parameter reference, and the result-delivery contract (`finalizeToolResult`, 40 KiB bound, `bytes=`/`truncated=` usage footer). `docs/settings.md`, `docs/globalconfig.md`, and `docs/usage.md` now agree on all registry flags, including the seven analyzer toggles. `docs/features.md`'s formatter list is corrected to the current 34-entry autoformat registry (drops `fish_indent`, which is a check-only dispatch runner rather than an entry in that registry, and adds `ktfmt` and `terragrunt-hcl`). `docs/environment-variables.md` drops `PI_LENS_AUTO_INSTALL`, which no longer exists in code. (refs #2800)
|
|
321
|
+
|
|
322
|
+
- **Fold `lsp_diagnostics` into `lens_diagnostics` with source and scope modifiers; retain the MCP compatibility shim for one release.** (refs #2800)
|
|
323
|
+
Deprecated: `pilens_lsp_diagnostics` remains accepted by MCP for one release and maps to `source=lsp`.
|
|
324
|
+
|
|
325
|
+
- **Make the oxfmt format check a CI gate (refs #2790, #2784)** — the workflow job now uses the gating name `oxfmt format check`, matching the hard `fmt:check` preflight gate and keeping formatting drift out of master.
|
|
326
|
+
|
|
327
|
+
- **Fold `allowStamp` out of auxiliary coverage (closes #2914)** — The stamp-union predicate keeps one behaviour; the sibling and aggregate rows call the binding predicate directly.
|
|
328
|
+
|
|
329
|
+
- **One result renderer for pi tool results and the MCP mirror (refs #2800)** — result status, diagnostic severity and usage lines come from one shared renderer on both surfaces, pinned by a both-surfaces governance test that drives real inputs for every paired registry tool and explicitly covers pi-only rows.
|
|
330
|
+
The renderer runs after each tool has set `isError`, then applies stale warnings, and bounds the final MCP payload to 40 KiB.
|
|
331
|
+
|
|
332
|
+
- **Dropped a no-op string replacement in the tsgolint preflight test (refs #2709)** — CodeQL alert 49 (`js/identity-replacement`): the helper replaced `/package.json` with itself; the key lookup now only strips the `/tmp/` prefix.
|
|
333
|
+
|
|
334
|
+
- **Pin the tool-roster description budget and trim paid metadata (refs #2800)** — pi and MCP tool registration now use concise contract-plus-example descriptions, with a two-sided baseline covering descriptions and schemas on both surfaces; the model-facing contract sentences (read coverage, diagnostic suppression ordering, configuration redaction, cold-cache guidance) stay in the paid description and are pinned per surface so a later trim cannot drop them.
|
|
335
|
+
|
|
336
|
+
- **Add per-tool enablement (refs #2800)** — `tools.<name>.enabled` and repeatable `--no-tool=<name>` prevent disabled lens tools from registering on the pi and MCP surfaces. One registry now covers every model-facing tool, keeps the activation and MCP lifecycle tools non-disableable, and emits stable diagnostic code `PILENS_CFG_0009` for unknown or non-disableable keys. The resolved state appears in `effective_config`.
|
|
337
|
+
|
|
338
|
+
- **`sql-injection` (TypeScript tree-sitter rule) is a warning, not a blocking error.** The query keys on the callee name only (`query|execute|exec|run`), so any template literal passed to a same-named non-SQL function fired a blocking finding; it stays as a warning until the rule gains a receiver/type signal.
|
|
339
|
+
|
|
340
|
+
### Fixed
|
|
341
|
+
|
|
342
|
+
- **Windows lane enumerated zero files because six scripts detected "am I the entry module" by string-comparing `import.meta.url` with `file://${process.argv[1]}`** (refs #2536) — on Windows the two spellings never match, so the main block never ran; every gate now compares against `pathToFileURL(process.argv[1]).href`, the win32 gate scanner excludes fixtures by the platform separator, and a governance test rejects the hand-built comparison.
|
|
343
|
+
|
|
344
|
+
- **Bound turn-end Gitleaks classification (refs #2575)** — timed-out classification keeps raw findings fail-open. Thanks @stekman08.
|
|
345
|
+
|
|
346
|
+
- **Prune merged clean worktrees with a per-run cap; never a tree with untracked files (refs #2631, #2538, #2784)** — the agent-worktree hygiene sweep now also removes any registered worktree whose branch tip is an ancestor of `origin/master` and whose checkout is clean, clears `.claude/worktrees/agent-*` leftovers that hold nothing but git's own `.git` gitlink, and caps removals per run with `--max` (default 10). An untracked file counts as a deliverable: only an empty `git status --porcelain` is clean, and the keep record names the first entry it protected.
|
|
347
|
+
|
|
348
|
+
- **The format smoke lane installs the managed formatters it selects (refs #2767)** —
|
|
349
|
+
The `--install` path prefetches configured formatters through `ensureTool`
|
|
350
|
+
and the formatter pipeline resolves those managed binaries, with venv/local
|
|
351
|
+
→ PATH → managed precedence; every managed resolver returns the typed
|
|
352
|
+
unavailable outcome instead of falling through to a bare command; GitHub and
|
|
353
|
+
archive installs record a typed unavailable outcome when no asset exists for
|
|
354
|
+
the host platform and architecture; the formatter-absence tests pin the
|
|
355
|
+
installer's independent PATH lookup so npm's `node_modules/.bin` prefix on
|
|
356
|
+
CI cannot resolve a dev-dependency binary.
|
|
357
|
+
|
|
358
|
+
- **Unify tool cwd and root resolution (refs #2777)** — Runners, formatters, and LSP configuration walks now use one bounded, logged resolution seam.
|
|
359
|
+
|
|
360
|
+
- **Fix runner no-config advisories logging once per tool and root at debug level (refs #2811)** — Dispatch now bounds constant runner advisories to one record per tool and resolved root per session.
|
|
361
|
+
|
|
362
|
+
- **Key vi.mock export admissions by factory content and ratchet missing counts across line moves.** (refs #2816)
|
|
363
|
+
|
|
364
|
+
- Make vi.mock export sweep warnings visible in default Vitest output and keep baseline regeneration successful when admissions change.
|
|
365
|
+
|
|
366
|
+
- **Harden real pi harness waits and isolation (refs #2825)** — consume cursor-based events, honor scenario scripts, pin lane membership, and sweep shared scratch directories.
|
|
367
|
+
|
|
368
|
+
- **Verify explicit existing records in PR-body observability lint (refs #2855)** — require the named kind in the named source file and verify its cited line is within 20 lines of the literal.
|
|
369
|
+
|
|
370
|
+
- **Report peak RSS for exit-137 classifications (refs #2856)** — retain master’s real-failure precedence and include the three heaviest `[mem-file]` peaks in kill details.
|
|
371
|
+
|
|
372
|
+
- **Attribute pi situational-tool observations by session file and emit on replacement.** A shutdown with `targetSessionFile` emits the conversation that is ending; reload re-runs the extension factory but keeps the same file and row. (refs #2858)
|
|
373
|
+
|
|
374
|
+
- **Fail session-start tests when the awaited handler crashes or exceeds its test budget, instead of passing vacuously.** (refs #2859)
|
|
375
|
+
|
|
376
|
+
- **Bound joined tool-result text with one conservative footer reserve, write one complete-result log, and convert rejecting pi tool executions into contract errors.** (refs #2862)
|
|
377
|
+
|
|
378
|
+
- **Derive lens diagnostics limits and scope metadata from the LSP probe, preserve single-file names, and honor session severity tiers.** (refs #2864)
|
|
379
|
+
|
|
380
|
+
- **Test-runner selection is per file kind and per module root, and a go `[setup failed]` result is advisory (refs #2870)** — a repo carrying both a `go.mod` and a Gradle build no longer hands every `.java` file to `go test` (the `RUNNERS` declaration order used to decide), a `README.md` or `.yaml` under a test directory no longer becomes its own test target, a nested Gradle or Go module anchors at its own build while a single-language repo keeps the runner it selected before, and go's `FAIL <pkg> [setup failed]` verdict reports as "could not run tests" instead of a fabricated blocking failure whose classification depended on whether go printed the word "error".
|
|
381
|
+
|
|
382
|
+
- **The test runner spawns its child through `resolveToolCwd`, so a nested module's tests run from that module (refs #2871)** — a `.go` file in a nested `go.mod` module now runs `go test ./internal/…` from the module that owns the package instead of a root-relative path the root module cannot resolve, with the shared seam supplying marker discovery, the `.git`/dispatch-root fallbacks, the `$HOME` ceiling and one `tool-cwd` log line per resolution key; a whole-project build launches from the directory that owns its wrapper (`gradle`, `maven`), a child is never anchored on a config file the detector itself rejected as evidence (`pytest`'s `pyproject.toml`, `phpunit`'s `composer.json`), and the failed-target ledger stays keyed on the dispatch root.
|
|
383
|
+
|
|
384
|
+
- **Make the runner spawn-cwd sweep verify resolver origin across the child-spawn population (refs #2872).** Bindings resolve by the grammar's own scope rather than a list of node kinds, any later rebinding leaves the value unproven, promoted resolvers must be a single seam return, and each admitted site is keyed by its cwd expression and the locals that expression reads, so a laundered or edited cwd cannot inherit an admission.
|
|
385
|
+
|
|
386
|
+
- **Keep diagnostics visible for tolerant MCP severity inputs and apply one threshold across session and LSP sources, including error-only files at the warning threshold.** (refs #2864)
|
|
387
|
+
|
|
388
|
+
- **Surface a crashed pi hook handler instead of swallowing it silently (refs #2884)** — the nine `index.ts` catch sites that absorb a handler crash (`session_start`, `session_before_fork`, `observed_settled_sweep`, `observed_ledger_refresh`, `agent_end`, `turn_end`, the `agent_settled` deferred-mutation drain, `quiet_window`, and `message_end`) now route through one shared `surfaceHandlerCrash` guard: production keeps the swallow but leaves one bounded `hook-handler-crash` degradation row per handler per session, and under the test runner the crash is rethrown so the awaiting test fails instead of resolving as if the handler had completed. The fire-and-forget `quiet_window` catch records the row without rethrowing, so a crash cannot terminate the pi host with an unhandled rejection.
|
|
389
|
+
|
|
390
|
+
- **Resolve aliased child-process bindings (closes #2888)** — Extend the spawn-cwd test sweep to resolve aliased and namespaced child-process bindings.
|
|
391
|
+
|
|
392
|
+
- **Make pi RPC `session_start` idempotent per reason and session ID, with a session-file fallback (refs #2890)** — repeated replacement events no longer repeat tool restoration or session-state resets, while a different session identity starts a new mutation pass.
|
|
393
|
+
|
|
394
|
+
- **Deduplicate repeated RPC session starts (refs #2890)** — use the stable session ID, fall back to the session file, and re-run the restore when the host's active tool posture drifted.
|
|
395
|
+
|
|
396
|
+
- **`lens_diagnostics mode=full` retires a stale finding only when the result that replaces it is genuinely authoritative: an LSP write the ordering guard accepted, or a project runner that actually analysed the root this call. A runner that reported success without running — no project root, no source files, a scan that crashed before writing its report — is reported cold instead of silently deleting the finding, and a retained result that could not be reconciled is labelled stale rather than served as a current blocker (refs #2154).**
|
|
397
|
+
|
|
398
|
+
- **Require Windows-only test gates to name and enter the Windows Vitest lane (refs #2536, #2784)** — one shared population script drives the Windows job and governance sweep, with a job summary recording the executed gate count.
|
|
399
|
+
|
|
400
|
+
- **Require runtime Observability sections to name a diff record (refs #2543, #2784)** — the PR-body checker matches observability record literals against added runtime lines and rejects unsupported no-failure claims.
|
|
401
|
+
|
|
402
|
+
- **Make the merge-train warden label `red-ci` for any failing non-advisory check on the exact pull-request head (refs #2616, #2784)**.
|
|
403
|
+
|
|
404
|
+
- **CI failure classifier: a test timeout beside registry network failures is infra, not real (refs #2839)** — A Unit-tests job whose only test-level failure is a vitest `Test timed out in Nms` line (no `AssertionError`, no compiler diagnostic) now classifies `infra-net` when the log also carries registry-unreachable evidence (`npm error code ECONNRESET` through every `npm-retry` attempt, codeload/SARIF 429/503, `npm ci` ETIMEDOUT, `npm-retry: attempt N network error`), so the once-per-SHA automatic rerun arms instead of the job sitting `ci:real`; PRs #2834 and #2835 both hit this and were re-run by hand. An `AssertionError` or a TypeScript `error TS` line beside the same network noise still classifies `real`, and a lone timeout with no network evidence is unchanged.
|
|
405
|
+
|
|
406
|
+
- **`detectFileRole` recognises Go `_test.go` and the other table conventions (closes #2880)** — Editing `pkg/foo_test.go` produced no test target because the shared role classifier only knew `.test.`/`.spec.`/prefix/dir patterns. It now also matches the `_test.`/`_spec.` suffix infixes and the case-sensitive `*Test(s).<ext>` CamelCase suffix for Java/Kotlin/C#/F#/PHP, informed by the test-runner client's `SOURCE_TO_TEST_PATTERNS` + `RUNNERS` table; hand-written, see #2928 for the single-classifier fold, so an edited Go test file runs its own package tests.
|
|
407
|
+
|
|
408
|
+
- **Route helm-lint through the runner cwd resolver (closes #2882)** — Helm lint now runs with the cwd selected by the shared dispatch runner seam.
|
|
409
|
+
|
|
410
|
+
- **Preserve situational tool activations across pi rebuilds (refs #2889)** — Remember activations by session file so reload, resume, and fork restore the conversation's tool posture.
|
|
411
|
+
|
|
412
|
+
- Run the installed-registry release-QA smoke with a scratch-home pip policy and keep the measured registry deterministic.
|
|
413
|
+
|
|
414
|
+
- **Diagnostic verification guidance (closes #2792).** Agent and MCP descriptions distinguish cache-only reporting from active scans. Use `mode=full` with `paths` when changed files have absent or stale cached findings.
|
|
415
|
+
|
|
416
|
+
- **Partition LSP diagnostics by server provenance (refs #2776)** — custom language servers now render their diagnostics as primary findings even when their server-authored `source` value differs from the registered server id.
|
|
417
|
+
|
|
418
|
+
- **Re-sync LSP diagnostics after Git tree changes (refs #2817)** — open documents and bounded cached import dependencies are touched before scoped full diagnostics serve cached results; files without import-fact coverage receive a fresh requested-file touch; **Prove language-neutral LSP dependency resync through the real server path** (refs #2817); the language matrix derives its facts and fallback rows from the production import-facts predicate; the shared drift pass gives a Git-queued file one resync opportunity even when ordinary stat drift identifies the same file in that pass.
|
|
419
|
+
|
|
420
|
+
- **Bound MCP tool results through the shared pi result seam (refs #2799, #2800)** — oversized results retain head and tail context, report omitted characters, and preserve the complete payload in the session log; the roster budget rejects duplicate tool names; results above the documented 8 MiB input budget keep a bounded head, an explicit incomplete marker and the tail in the session log, with one `mcp-complete-result-budget-exceeded` degradation per session.
|
|
421
|
+
|
|
422
|
+
- **Flake-shape ratchet governs support-helper waits (refs #2563, #2784)** — the contention ratchet scans `.ts` and `.mts` support helpers, resolves aliased timers, and requires every support baseline row to carry a detector-specific reason, header, and serialized-lane proof through its importing tests; **Reduce flake-shape ratchet AST scanning cost (refs #2563, #2784)** — the whole-tree scan uses a comment/string-blanked textual pre-pass and parses each timer-bearing source file at most once per run.
|
|
423
|
+
|
|
424
|
+
- **Label-sync post-merge validation fails when the syncer deletes or loses any label (refs #2614)** — the `labels.yml` sync job now snapshots the live label set before `action-label-syncer` runs and re-validates after: any before-vs-after deletion, or any manifest label missing from the live set, fails the job through `::error::label sync deleted or missing labels:` with the names printed, instead of a silent success while prune deletes labels (the #2553 recurrence, twice for the priority labels).
|
|
425
|
+
|
|
426
|
+
- **Route built-in LSP roots through the tool-cwd seam and expose resolved cwd in `lens_diagnostics` (refs #2777)** — Built-in server marker tables now use the shared bounded resolver, and diagnostic rows identify the cwd used for their language server. Workspace-priority roots keep their complete marker tables through the seam (Go, JSON), and a server-computed root is the seam's input, never replaced (review rounds 2 and 4).
|
|
427
|
+
|
|
428
|
+
- **Run the release-QA tool smoke from the export root (closes #2893)** — the install lane now executes the smoke harness from the archived source tree while loading the installer registry from the installed package, and measures pip entries under its scratch-pinned package policy.
|
|
429
|
+
|
|
430
|
+
- **Docs membership guard asserts list members, not counts (closes #2919).** `tests/docs/features-counts.test.ts` now compares the `docs/features.md` formatter list member-by-member against `ALL_FORMATTERS`, the LSP language list against non-auxiliary `LSP_SERVERS` ids, and the `docs/mcp.md` tool table against `TOOL_REGISTRY`, and reports missing and extra names, so reinstating a removed member or swapping in a stale name fails the suite.
|
|
431
|
+
|
|
432
|
+
- **Route tool configuration diagnostics through the shared note seam (refs #2800)** — unknown and non-disableable tool names use the bounded config notice path, and `tools.<name>.enabled` is documented across the config references.
|
|
433
|
+
|
|
19
434
|
## [4.1.5] - 2026-09-08
|
|
20
435
|
|
|
21
436
|
### Added
|
package/README.md
CHANGED
|
@@ -155,6 +155,14 @@ pi install git:github.com/apmantza/pi-lens
|
|
|
155
155
|
Requires Node.js >=22.19.0 (matches the pi host's own floor — see `engines`
|
|
156
156
|
in `package.json`).
|
|
157
157
|
|
|
158
|
+
Letting a coding agent do it: paste this into its prompt.
|
|
159
|
+
|
|
160
|
+
```text
|
|
161
|
+
Install pi-lens (npm:pi-lens) into pi, then read AGENTS.md in
|
|
162
|
+
https://github.com/apmantza/pi-lens for how the extension works and how it
|
|
163
|
+
expects to be operated.
|
|
164
|
+
```
|
|
165
|
+
|
|
158
166
|
> **npm v12 users:** dependency lifecycle scripts (e.g. `@ast-grep/cli`'s
|
|
159
167
|
> `postinstall`) now require explicit approval — if `npm install` warns about
|
|
160
168
|
> unreviewed install scripts, review and allow them with
|
|
@@ -336,6 +344,11 @@ Thanks goes to these wonderful people:
|
|
|
336
344
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mjfaga"><img src="https://avatars.githubusercontent.com/u/7584015?v=4" width="100px;" alt=""/><br /><sub><b>Mark</b></sub></a><br /><a href="#code-mjfaga" title="Code">💻</a></td>
|
|
337
345
|
<td align="center" valign="top" width="14.28%"><a href="https://aeturnal.io/"><img src="https://avatars.githubusercontent.com/u/219271200?v=4" width="100px;" alt=""/><br /><sub><b>aeturnal</b></sub></a><br /><a href="#code-aeturnal" title="Code">💻</a></td>
|
|
338
346
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/floatGray"><img src="https://avatars.githubusercontent.com/u/44295302?v=4" width="100px;" alt=""/><br /><sub><b>floatGray</b></sub></a><br /><a href="#code-floatGray" title="Code">💻</a></td>
|
|
347
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/thomasfsteeples"><img src="https://avatars.githubusercontent.com/u/32720359?v=4" width="100px;" alt=""/><br /><sub><b>Thomas F Steeples</b></sub></a><br /><a href="#code-thomasfsteeples" title="Code">💻</a></td>
|
|
348
|
+
</tr>
|
|
349
|
+
<tr>
|
|
350
|
+
<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
|
+
<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>
|
|
339
352
|
</tr>
|
|
340
353
|
</tbody>
|
|
341
354
|
</table>
|
|
@@ -344,11 +357,3 @@ Thanks goes to these wonderful people:
|
|
|
344
357
|
<!-- prettier-ignore-end -->
|
|
345
358
|
|
|
346
359
|
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
|
347
|
-
|
|
348
|
-
The following commit identities also appear in `git shortlog -sne HEAD` but are
|
|
349
|
-
not represented in the generated table above: Anas Alsbei, Claude,
|
|
350
|
-
Christopher Patti,
|
|
351
|
-
dependabot[bot], Fabio-D, github-actions[bot], JSup, Kenny McCormick,
|
|
352
|
-
Max Lupus, Moritz Hofmann, ricardo, and root.
|
|
353
|
-
|
|
354
|
-
If you land a pull request or report an issue that gets fixed, we'll add you here.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
[
|
|
2
2
|
"./clients/agent-nudge.js",
|
|
3
3
|
"./clients/ast-grep-client.js",
|
|
4
|
+
"./clients/bash-file-access.js",
|
|
4
5
|
"./clients/bootstrap.js",
|
|
5
6
|
"./clients/bounded-cache.js",
|
|
6
7
|
"./clients/bounded-telemetry.js",
|
|
@@ -11,6 +12,7 @@
|
|
|
11
12
|
"./clients/cache-observability.js",
|
|
12
13
|
"./clients/cascade-format.js",
|
|
13
14
|
"./clients/console-guard-install.js",
|
|
15
|
+
"./clients/deadline-utils.js",
|
|
14
16
|
"./clients/debug-handles.js",
|
|
15
17
|
"./clients/debug-heap.js",
|
|
16
18
|
"./clients/degradation-ledger.js",
|
|
@@ -26,7 +28,7 @@
|
|
|
26
28
|
"./clients/format-events-publish.js",
|
|
27
29
|
"./clients/format-service.js",
|
|
28
30
|
"./clients/formatters-lazy.js",
|
|
29
|
-
|
|
31
|
+
"./clients/hook-budgets.js",
|
|
30
32
|
"./clients/host-ports.js",
|
|
31
33
|
"./clients/installer/index.js",
|
|
32
34
|
"./clients/instance-reaper.js",
|
|
@@ -75,13 +77,16 @@
|
|
|
75
77
|
"./clients/session-start-observability.js",
|
|
76
78
|
"./clients/session-state-store.js",
|
|
77
79
|
"./clients/sessionstart-logger.js",
|
|
80
|
+
"./clients/situational-tool-telemetry.js",
|
|
78
81
|
"./clients/skills-resolver.js",
|
|
79
82
|
"./clients/smells-rollup.js",
|
|
80
83
|
"./clients/startup-timing.js",
|
|
81
84
|
"./clients/test-runner-delivery.js",
|
|
85
|
+
"./clients/tool-config.js",
|
|
82
86
|
"./clients/tool-definition.js",
|
|
83
87
|
"./clients/tool-render.js",
|
|
84
88
|
"./clients/tool-set-policy.js",
|
|
89
|
+
"./clients/turn-context.js",
|
|
85
90
|
"./clients/turn-summary-render.js",
|
|
86
91
|
"./clients/turn-summary.js",
|
|
87
92
|
"./clients/user-notify.js",
|