pi-lens 4.1.2 → 4.1.4
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 +1308 -0
- package/README.md +3 -0
- package/config/biome/core.jsonc +11 -2
- package/config/dependency-cruiser-eager-allowlist.json +91 -0
- package/dist/clients/actionable-warnings-logger.js +18 -3
- package/dist/clients/actionable-warnings.js +927 -80
- package/dist/clients/agent-behavior-client.js +13 -4
- package/dist/clients/ast-grep-client.js +136 -9
- package/dist/clients/ast-grep-rule-manager.js +60 -5
- package/dist/clients/ast-grep-tool-logger.js +2 -2
- package/dist/clients/availability-probe-flight.js +12 -0
- package/dist/clients/bash-file-access.js +1 -3
- package/dist/clients/biome-client.js +16 -2
- package/dist/clients/blocker-freshness.js +34 -0
- package/dist/clients/bootstrap.js +509 -73
- package/dist/clients/bounded-cache.js +152 -12
- package/dist/clients/bounded-telemetry.js +115 -23
- package/dist/clients/build-identity.js +95 -0
- package/dist/clients/bundled-resource-health.js +113 -0
- package/dist/clients/bus-events-logger.js +2 -2
- package/dist/clients/bus-publish.js +3 -0
- package/dist/clients/cache/rule-cache.js +33 -5
- package/dist/clients/cache-manager.js +105 -6
- package/dist/clients/cache-observability.js +46 -19
- package/dist/clients/cargo-manifest.js +422 -0
- package/dist/clients/cascade-logger.js +17 -3
- package/dist/clients/code-quality-warnings.js +13 -3
- package/dist/clients/complexity-client.js +16 -5
- package/dist/clients/config-core/deny.js +221 -0
- package/dist/clients/config-core/index.js +47 -0
- package/dist/clients/config-core/merge.js +357 -0
- package/dist/clients/config-core/normalize.js +340 -0
- package/dist/clients/config-core/process-spec.js +248 -0
- package/dist/clients/config-core/provenance.js +171 -0
- package/dist/clients/config-core/records.js +218 -0
- package/dist/clients/config-core/resolve.js +125 -0
- package/dist/clients/config-core/safe-object.js +78 -0
- package/dist/clients/config-core/schema.js +167 -0
- package/dist/clients/config-diagnostic-codes.js +281 -0
- package/dist/clients/config-locations.js +160 -0
- package/dist/clients/config-resolve.js +789 -0
- package/dist/clients/config-schema.js +197 -0
- package/dist/clients/config-warn.js +407 -0
- package/dist/clients/dead-code-client.js +33 -25
- package/dist/clients/dead-code-logger.js +2 -2
- package/dist/clients/deadline-utils.js +178 -0
- package/dist/clients/debug-handles.js +2 -2
- package/dist/clients/debug-heap.js +3 -3
- package/dist/clients/deferred-lsp-work.js +106 -0
- package/dist/clients/degradation-ledger.js +166 -8
- package/dist/clients/demoted-finding-render.js +14 -0
- package/dist/clients/dependency-checker.js +28 -22
- package/dist/clients/deps/minimatch.js +1 -1
- package/dist/clients/diagnostic-line-freshness.js +12 -9
- package/dist/clients/diagnostic-logger.js +14 -4
- package/dist/clients/dispatch/collect-later-tier.js +39 -0
- package/dist/clients/dispatch/dispatcher.js +304 -19
- package/dist/clients/dispatch/fact-store.js +328 -9
- package/dist/clients/dispatch/integration.js +214 -107
- package/dist/clients/dispatch/pending-runner-findings.js +87 -0
- package/dist/clients/dispatch/plan.js +21 -5
- package/dist/clients/dispatch/runners/actionlint.js +0 -1
- package/dist/clients/dispatch/runners/ast-grep-napi.js +550 -77
- package/dist/clients/dispatch/runners/biome-check.js +0 -1
- package/dist/clients/dispatch/runners/cpp-check.js +0 -1
- package/dist/clients/dispatch/runners/credo.js +0 -1
- package/dist/clients/dispatch/runners/cue-vet.js +0 -1
- package/dist/clients/dispatch/runners/dart-analyze.js +0 -1
- package/dist/clients/dispatch/runners/detekt.js +0 -1
- package/dist/clients/dispatch/runners/dotnet-build.js +0 -1
- package/dist/clients/dispatch/runners/elixir-check.js +0 -1
- package/dist/clients/dispatch/runners/eslint.js +5 -2
- package/dist/clients/dispatch/runners/fact-rules.js +0 -1
- package/dist/clients/dispatch/runners/fish-indent.js +0 -1
- package/dist/clients/dispatch/runners/gleam-check.js +0 -1
- package/dist/clients/dispatch/runners/go-vet.js +1 -3
- package/dist/clients/dispatch/runners/golangci-lint.js +0 -1
- package/dist/clients/dispatch/runners/hadolint.js +0 -1
- package/dist/clients/dispatch/runners/helm-lint.js +16 -4
- package/dist/clients/dispatch/runners/helm-render.js +54 -10
- package/dist/clients/dispatch/runners/htmlhint.js +0 -1
- package/dist/clients/dispatch/runners/index.js +1 -3
- package/dist/clients/dispatch/runners/javac.js +0 -1
- package/dist/clients/dispatch/runners/ktlint.js +0 -1
- package/dist/clients/dispatch/runners/lsp.js +37 -2
- package/dist/clients/dispatch/runners/markdownlint.js +4 -2
- package/dist/clients/dispatch/runners/mypy.js +0 -1
- package/dist/clients/dispatch/runners/oxlint.js +24 -22
- package/dist/clients/dispatch/runners/php-lint.js +0 -1
- package/dist/clients/dispatch/runners/phpstan.js +0 -1
- package/dist/clients/dispatch/runners/prisma-validate.js +0 -1
- package/dist/clients/dispatch/runners/psscriptanalyzer.js +4 -1
- package/dist/clients/dispatch/runners/pyright.js +7 -4
- package/dist/clients/dispatch/runners/rubocop.js +0 -1
- package/dist/clients/dispatch/runners/ruff.js +0 -1
- package/dist/clients/dispatch/runners/rust-clippy.js +9 -6
- package/dist/clients/dispatch/runners/shellcheck.js +0 -1
- package/dist/clients/dispatch/runners/shfmt.js +0 -1
- package/dist/clients/dispatch/runners/spellcheck.js +0 -1
- package/dist/clients/dispatch/runners/spotbugs.js +0 -1
- package/dist/clients/dispatch/runners/sqlfluff.js +0 -1
- package/dist/clients/dispatch/runners/stylelint.js +0 -1
- package/dist/clients/dispatch/runners/swiftlint.js +0 -1
- package/dist/clients/dispatch/runners/taplo.js +0 -1
- package/dist/clients/dispatch/runners/terragrunt.js +0 -1
- package/dist/clients/dispatch/runners/tflint.js +0 -1
- package/dist/clients/dispatch/runners/tree-sitter.js +0 -1
- package/dist/clients/dispatch/runners/trivy-config.js +0 -1
- package/dist/clients/dispatch/runners/utils/availability-policy.js +3 -0
- package/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +3 -4
- package/dist/clients/dispatch/runners/utils/runner-helpers.js +270 -105
- package/dist/clients/dispatch/runners/utils/spawn-outcome.js +4 -0
- package/dist/clients/dispatch/runners/utils/tool-failure.js +19 -7
- package/dist/clients/dispatch/runners/utils/toolchain-availability.js +89 -26
- package/dist/clients/dispatch/runners/vale.js +0 -1
- package/dist/clients/dispatch/runners/yaml-rule-parser.js +51 -17
- package/dist/clients/dispatch/runners/yamllint.js +0 -1
- package/dist/clients/dispatch/runners/zig-check.js +0 -1
- package/dist/clients/disposition-logger.js +3 -3
- package/dist/clients/effective-config.js +403 -0
- package/dist/clients/error-class.js +23 -0
- package/dist/clients/event-loop-hold.js +274 -0
- package/dist/clients/event-loop-monitor.js +66 -1
- package/dist/clients/extension-log.js +2 -2
- package/dist/clients/feature-hints.js +2 -1
- package/dist/clients/file-time.js +6 -1
- package/dist/clients/file-utils.js +266 -13
- package/dist/clients/finding-delivery-gate.js +55 -13
- package/dist/clients/format-service.js +6 -1
- package/dist/clients/formatters.js +303 -104
- package/dist/clients/freshness-cadence.js +17 -0
- package/dist/clients/generated-artifacts.js +114 -19
- package/dist/clients/generation-guard.js +4 -9
- package/dist/clients/git-guard.js +198 -48
- package/dist/clients/git-tracked-ignore.js +32 -1
- package/dist/clients/go-client.js +39 -0
- package/dist/clients/govulncheck-client.js +5 -1
- package/dist/clients/gradle-ktfmt-style.js +252 -0
- package/dist/clients/gzip-stage-write.js +7 -0
- package/dist/clients/hashline-anchor.js +424 -0
- package/dist/clients/hook-budgets.js +76 -0
- package/dist/clients/host-edit-normalize.js +5 -2
- package/dist/clients/host-ports.js +1 -1
- package/dist/clients/installer/index.js +200 -84
- package/dist/clients/installer/managed-tool-refresh.js +3 -1
- package/dist/clients/instance-reaper.js +108 -243
- package/dist/clients/instance-registry-lock.js +145 -0
- package/dist/clients/instance-registry.js +435 -122
- package/dist/clients/jscpd-client.js +6 -1
- package/dist/clients/language-profile.js +4 -2
- package/dist/clients/language-registry.js +595 -0
- package/dist/clients/latency-logger.js +179 -3
- package/dist/clients/ledger-bounds.js +13 -0
- package/dist/clients/lens-config.js +141 -30
- package/dist/clients/lens-engine.js +30 -37
- package/dist/clients/lens-flag-registry.js +67 -6
- package/dist/clients/lens-map.js +3 -1
- package/dist/clients/log-cleanup.js +2 -2
- package/dist/clients/lsp/aggregation.js +3 -1
- package/dist/clients/lsp/cascade-tier.js +4 -0
- package/dist/clients/lsp/client.js +672 -49
- package/dist/clients/lsp/config.js +504 -156
- package/dist/clients/lsp/diagnostic-binding.js +10 -5
- package/dist/clients/lsp/edits.js +15 -2
- package/dist/clients/lsp/index.js +1179 -247
- package/dist/clients/lsp/jvm-runtime.js +5 -0
- package/dist/clients/lsp/language.js +18 -169
- package/dist/clients/lsp/launch.js +38 -9
- package/dist/clients/lsp/pending-aux-coverage.js +113 -12
- package/dist/clients/lsp/server.js +426 -160
- package/dist/clients/lsp/session-roots.js +192 -0
- package/dist/clients/lsp/spawn-history.js +2 -7
- package/dist/clients/lsp/tsserver-sync.js +9 -2
- package/dist/clients/lsp/workspace-diagnostics-cache.js +17 -7
- package/dist/clients/lsp/workspace-sweep-hold.js +32 -23
- package/dist/clients/lsp-mutation.js +179 -23
- package/dist/clients/map-with-concurrency.js +36 -0
- package/dist/clients/mcp/analyze.js +45 -10
- package/dist/clients/mcp/session.js +19 -17
- package/dist/clients/memory-sampler.js +28 -1
- package/dist/clients/message-end-attribution.js +30 -0
- package/dist/clients/metrics-history.js +21 -4
- package/dist/clients/middle-man-analysis.js +2 -4
- package/dist/clients/module-report.js +20 -45
- package/dist/clients/mutating-tool.js +651 -0
- package/dist/clients/mutation-attribution.js +368 -0
- package/dist/clients/mutation-bridge.js +240 -0
- package/dist/clients/ndjson-logger.js +247 -20
- package/dist/clients/observed-mutation-sources.js +101 -0
- package/dist/clients/observed-mutation.js +1215 -0
- package/dist/clients/opaque-mutation-scan.js +300 -40
- package/dist/clients/opengrep-config.js +4 -0
- package/dist/clients/package-manager.js +201 -44
- package/dist/clients/partial-edit-apply.js +359 -80
- package/dist/clients/path-attribution-telemetry.js +23 -7
- package/dist/clients/path-keyed-map.js +35 -3
- package/dist/clients/path-utils.js +460 -13
- package/dist/clients/persist-debounce.js +8 -1
- package/dist/clients/php-cs-fixer-config.js +114 -0
- package/dist/clients/pipeline.js +77 -24
- package/dist/clients/probe-home-state.js +228 -0
- package/dist/clients/process-bridge.js +66 -0
- package/dist/clients/process-singletons.js +210 -0
- package/dist/clients/process-snapshot.js +68 -0
- package/dist/clients/project-diagnostics/fresh-fetch.js +4 -3
- package/dist/clients/project-diagnostics/runner-adapters/madge.js +2 -1
- package/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +27 -3
- package/dist/clients/project-diagnostics/scanner.js +61 -24
- package/dist/clients/project-lens-config.js +393 -122
- package/dist/clients/project-snapshot.js +85 -35
- package/dist/clients/python-environment.js +217 -0
- package/dist/clients/python-provenance.js +639 -0
- package/dist/clients/quiet-window.js +6 -1
- package/dist/clients/read-bridge.js +13 -21
- package/dist/clients/read-expansion.js +9 -46
- package/dist/clients/read-guard-logger.js +39 -6
- package/dist/clients/read-guard-tool-lines.js +364 -212
- package/dist/clients/read-guard.js +95 -7
- package/dist/clients/recent-touches.js +17 -5
- package/dist/clients/resource-sampler.js +195 -96
- package/dist/clients/review-graph/builder.js +752 -132
- package/dist/clients/review-graph/service.js +55 -3
- package/dist/clients/review-graph/shared-extraction-ir.js +7 -15
- package/dist/clients/review-graph/tsconfig-paths.js +5 -1
- package/dist/clients/review-graph/workspace-modules.js +10 -74
- package/dist/clients/review-graph-logger.js +30 -4
- package/dist/clients/runtime-agent-end.js +18 -2
- package/dist/clients/runtime-context.js +49 -16
- package/dist/clients/runtime-coordinator.js +93 -6
- package/dist/clients/runtime-session.js +169 -34
- package/dist/clients/runtime-tool-call.js +183 -35
- package/dist/clients/runtime-tool-result.js +894 -241
- package/dist/clients/runtime-turn.js +1264 -74
- package/dist/clients/rust-client.js +31 -0
- package/dist/clients/safe-spawn.js +191 -28
- package/dist/clients/sanitize.js +7 -1
- package/dist/clients/scratch-tree-policy.js +1 -3
- package/dist/clients/security-scan-client.js +51 -17
- package/dist/clients/session-lifecycle.js +238 -42
- package/dist/clients/session-start-observability.js +79 -0
- package/dist/clients/sessionstart-logger.js +12 -3
- package/dist/clients/sg-runner.js +27 -15
- package/dist/clients/sgconfig.js +6 -1
- package/dist/clients/shared-checkout-guard.js +338 -0
- package/dist/clients/skills-resolver.js +105 -0
- package/dist/clients/smells-rollup.js +2 -2
- package/dist/clients/spawn-output-cap.js +37 -0
- package/dist/clients/spawn-timeout-cooldown.js +15 -1
- package/dist/clients/startup-scan.js +6 -7
- package/dist/clients/startup-timing.js +69 -4
- package/dist/clients/string-utils.js +33 -0
- package/dist/clients/subagent-mode.js +17 -4
- package/dist/clients/test-runner-client.js +586 -118
- package/dist/clients/test-runner-delivery.js +239 -0
- package/dist/clients/tool-definition.js +41 -1
- package/dist/clients/tool-policy.js +92 -22
- package/dist/clients/tree-sitter-cache.js +40 -40
- package/dist/clients/tree-sitter-client.js +97 -53
- package/dist/clients/tree-sitter-logger.js +19 -3
- package/dist/clients/tree-sitter-query-loader.js +81 -1
- package/dist/clients/tree-sitter-shared.js +51 -46
- package/dist/clients/tree-sitter-symbol-extractor.js +28 -0
- package/dist/clients/typos-config.js +5 -0
- package/dist/clients/user-notify.js +6 -2
- package/dist/clients/warm-attach.js +8 -10
- package/dist/clients/widget-state.js +191 -26
- package/dist/clients/word-index-logger.js +15 -3
- package/dist/clients/word-index-store.js +463 -0
- package/dist/clients/word-index.js +815 -125
- package/dist/clients/workspace-topology.js +25 -1
- package/dist/clients/zizmor-config.js +29 -6
- package/dist/index.js +68577 -53950
- package/dist/mcp/analyze-cli.js +3 -1
- package/dist/mcp/build-staleness.js +8 -7
- package/dist/mcp/cli.js +3 -1
- package/dist/mcp/server.js +133 -6
- package/dist/scripts/lib/process-scan.mjs +583 -0
- package/dist/scripts/lib/skills-predicate.mjs +129 -0
- package/dist/tools/effective-config.js +89 -0
- package/dist/tools/lens-diagnostics.js +99 -24
- package/dist/tools/lsp-diagnostics.js +81 -85
- package/dist/tools/lsp-navigation.js +40 -17
- package/dist/tools/shared.js +0 -1
- package/docs/agent-guide.md +68 -3
- package/docs/ast-grep_rules_catalog.md +41 -10
- package/docs/astplayground.md +26 -17
- package/docs/configuration.md +222 -0
- package/docs/dependencies.md +3 -3
- package/docs/features.md +44 -5
- package/docs/language-coverage.md +2 -2
- package/docs/pi-lens-fixer.md +25 -0
- package/docs/pi-lens-investigator.md +25 -0
- package/docs/pi-lens-reviewer.md +27 -0
- package/docs/pi-lens-subagent.md +38 -0
- package/docs/pi-lens-warden.md +55 -0
- package/docs/public-api-stability.md +359 -0
- package/docs/release-qa-baseline.md +182 -0
- package/docs/subagent-compat.md +110 -30
- package/docs/tree-sitter_rules_catalog.md +2 -2
- package/package.json +17 -21
- package/rules/ast-grep-rules/rule-tests/c-no-malloc-free-test.yml +20 -0
- package/rules/ast-grep-rules/rule-tests/cpp-no-malloc-free-test.yml +25 -0
- package/rules/ast-grep-rules/rule-tests/cpp-no-printf-test.yml +19 -0
- package/rules/ast-grep-rules/rule-tests/go-no-fmt-println-test.yml +19 -0
- package/rules/ast-grep-rules/rule-tests/go-no-panic-in-lib-test.yml +17 -0
- package/rules/ast-grep-rules/rule-tests/go-no-underscore-func-name-test.yml +18 -0
- package/rules/ast-grep-rules/rule-tests/go-prefer-errors-is-test.yml +15 -0
- package/rules/ast-grep-rules/rule-tests/go-prefer-string-builder-test.yml +21 -0
- package/rules/ast-grep-rules/rule-tests/hyphenated-svg-attribute-test.yml +11 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-no-lateinit-test.yml +17 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-no-nullable-boolean-test.yml +15 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-no-println-test.yml +19 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-no-unnecessary-let-test.yml +7 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-no-unsafe-cast-test.yml +7 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-no-var-test.yml +7 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-prefer-data-class-test.yml +25 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-prefer-is-empty-test.yml +11 -0
- package/rules/ast-grep-rules/rule-tests/no-important-test.yml +21 -0
- package/rules/ast-grep-rules/rule-tests/no-raw-types-test.yml +30 -0
- package/rules/ast-grep-rules/rule-tests/no-string-concat-in-loop-test.yml +100 -0
- package/rules/ast-grep-rules/rule-tests/no-system-out-println-test.yml +7 -0
- package/rules/ast-grep-rules/rule-tests/prefer-string-is-empty-test.yml +7 -0
- package/rules/ast-grep-rules/rule-tests/python-optional-type-test.yml +9 -0
- package/rules/ast-grep-rules/rule-tests/reducible-list-comprehension-test.yml +11 -0
- package/rules/ast-grep-rules/rule-tests/ruby-symbol-to-proc-candidate-test.yml +10 -0
- package/rules/ast-grep-rules/rule-tests/rust-no-chars-enumerate-test.yml +7 -0
- package/rules/ast-grep-rules/rules/c-no-malloc-free.yml +39 -0
- package/rules/ast-grep-rules/rules/cpp-no-malloc-free.yml +45 -0
- package/rules/ast-grep-rules/rules/cpp-no-printf.yml +41 -0
- package/rules/ast-grep-rules/rules/go-no-fmt-println.yml +43 -0
- package/rules/ast-grep-rules/rules/go-no-panic-in-lib.yml +41 -0
- package/rules/ast-grep-rules/rules/go-no-underscore-func-name.yml +35 -0
- package/rules/ast-grep-rules/rules/go-prefer-errors-is.yml +40 -0
- package/rules/ast-grep-rules/rules/go-prefer-string-builder.yml +43 -0
- package/rules/ast-grep-rules/rules/hyphenated-svg-attribute.yml +32 -0
- package/rules/ast-grep-rules/rules/kotlin-no-lateinit.yml +37 -0
- package/rules/ast-grep-rules/rules/kotlin-no-nullable-boolean.yml +33 -0
- package/rules/ast-grep-rules/rules/kotlin-no-println.yml +42 -0
- package/rules/ast-grep-rules/rules/kotlin-no-unnecessary-let.yml +28 -0
- package/rules/ast-grep-rules/rules/kotlin-no-unsafe-cast.yml +28 -0
- package/rules/ast-grep-rules/rules/kotlin-no-var.yml +35 -0
- package/rules/ast-grep-rules/rules/kotlin-prefer-data-class.yml +66 -0
- package/rules/ast-grep-rules/rules/kotlin-prefer-is-empty.yml +35 -0
- package/rules/ast-grep-rules/rules/no-important.yml +21 -0
- package/rules/ast-grep-rules/rules/no-raw-types.yml +33 -0
- package/rules/ast-grep-rules/rules/no-string-concat-in-loop.yml +90 -0
- package/rules/ast-grep-rules/rules/no-system-out-println.yml +16 -0
- package/rules/ast-grep-rules/rules/prefer-string-is-empty.yml +11 -0
- package/rules/ast-grep-rules/rules/python-optional-type.yml +22 -0
- package/rules/ast-grep-rules/rules/reducible-list-comprehension.yml +29 -0
- package/rules/ast-grep-rules/rules/ruby-symbol-to-proc-candidate.yml +25 -0
- package/rules/ast-grep-rules/rules/rust-no-chars-enumerate.yml +19 -0
- package/rules/tree-sitter-queries/python/python-cross-language-method.yml +3 -3
- package/rules/tree-sitter-queries/python/python-hallucinated-import.yml +2 -2
- package/rules/tree-sitter-queries/python/python-sql-injection.yml +12 -2
- package/scripts/analyze-pi-lens-logs.mjs +192 -1
- package/scripts/install-selftest.mjs +58 -2
- package/scripts/lib/skills-predicate.mjs +129 -0
- package/scripts/rpc-load-check.mjs +3 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LEDGER_FIELD_MAX } from "../../../ledger-bounds.js";
|
|
2
|
+
import { truncatedByOutputCap } from "../../../spawn-output-cap.js";
|
|
2
3
|
/**
|
|
3
4
|
* The first non-empty line of a tool's output, bounded.
|
|
4
5
|
*
|
|
@@ -58,6 +59,9 @@ export function classifyRunOutcome(input) {
|
|
|
58
59
|
firstOutputLine: firstOutputLine(result.stderr) ||
|
|
59
60
|
firstOutputLine(result.stdout) ||
|
|
60
61
|
firstOutputLine(result.error?.message),
|
|
62
|
+
// Evidence carried alongside the verdict, never part of deciding it — the
|
|
63
|
+
// branches below are byte-for-byte the pre-#2100 rules.
|
|
64
|
+
outputCapped: truncatedByOutputCap(result),
|
|
61
65
|
};
|
|
62
66
|
if (result.error || signal)
|
|
63
67
|
return { kind: "did-not-run", ...base };
|
|
@@ -5,6 +5,23 @@
|
|
|
5
5
|
import { incrementDegradationCount } from "../../../degradation-ledger.js";
|
|
6
6
|
import { truncateForLedger } from "../../../ledger-bounds.js";
|
|
7
7
|
import { classifyRunOutcome, firstOutputLine, } from "./spawn-outcome.js";
|
|
8
|
+
/**
|
|
9
|
+
* What ended this run, as one clause, most specific cause first.
|
|
10
|
+
*
|
|
11
|
+
* A signal is a stronger explanation than the exit status, so it displaces a
|
|
12
|
+
* `null` one rather than sitting beside it. Our own output cap is stronger
|
|
13
|
+
* still: the signal it killed with is a detail of OUR decision, and naming that
|
|
14
|
+
* signal alone reads as the tool misbehaving (#2100).
|
|
15
|
+
*/
|
|
16
|
+
function describeEnding(input) {
|
|
17
|
+
if (input.outputCapped)
|
|
18
|
+
return "was stopped at its output cap";
|
|
19
|
+
if (input.signal)
|
|
20
|
+
return `was killed by ${input.signal}`;
|
|
21
|
+
if (input.status === null)
|
|
22
|
+
return "did not run (spawn failure)";
|
|
23
|
+
return `exited ${input.status}`;
|
|
24
|
+
}
|
|
8
25
|
/**
|
|
9
26
|
* One wording for "this tool did not produce a usable result" (#1816).
|
|
10
27
|
*
|
|
@@ -21,13 +38,7 @@ export function formatToolFailure(input) {
|
|
|
21
38
|
const identity = entries.length
|
|
22
39
|
? `${input.tool} (${entries.map(([key, value]) => `${key}=${value}`).join(", ")})`
|
|
23
40
|
: input.tool;
|
|
24
|
-
|
|
25
|
-
// status rather than sitting beside a `null` one.
|
|
26
|
-
const what = input.signal
|
|
27
|
-
? `was killed by ${input.signal}`
|
|
28
|
-
: input.status === null
|
|
29
|
-
? "did not run (spawn failure)"
|
|
30
|
-
: `exited ${input.status}`;
|
|
41
|
+
const what = describeEnding(input);
|
|
31
42
|
const missing = input.reportMissing ? "no report file" : "no output";
|
|
32
43
|
const detail = firstOutputLine(input.stderr) ||
|
|
33
44
|
firstOutputLine(input.stdout) ||
|
|
@@ -44,6 +55,7 @@ export function formatRunOutcomeFailure(tool, outcome, fields) {
|
|
|
44
55
|
tool,
|
|
45
56
|
status: outcome.status,
|
|
46
57
|
signal: outcome.signal,
|
|
58
|
+
outputCapped: outcome.outputCapped,
|
|
47
59
|
stderr: outcome.firstOutputLine,
|
|
48
60
|
reportMissing: outcome.kind === "report-missing",
|
|
49
61
|
fields: { outcome: outcome.kind, ...fields },
|
|
@@ -13,6 +13,40 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { createAvailabilityLatch, logAvailabilityDecision, } from "./availability-policy.js";
|
|
15
15
|
import { probeAvailabilityCandidates } from "./candidate-probe.js";
|
|
16
|
+
import { createAvailabilityProbeFlight } from "../../../availability-probe-flight.js";
|
|
17
|
+
import { createGenerationSource } from "../../../generation-guard.js";
|
|
18
|
+
import { createSingleFlight } from "../../../single-flight.js";
|
|
19
|
+
/**
|
|
20
|
+
* Session generation for toolchain-availability resets (#2455 fix round 3, F2).
|
|
21
|
+
*
|
|
22
|
+
* `reset()` clears the memo and the latch, but a probe that was ALREADY in
|
|
23
|
+
* flight when it ran knows nothing about that. Without a generation, a
|
|
24
|
+
* post-reset caller joins the pre-reset flight and, worse, that flight's own
|
|
25
|
+
* settlement writes its stale verdict back into the freshly cleared latch — so
|
|
26
|
+
* a tool installed between sessions still read "missing" for the whole new
|
|
27
|
+
* session. That is #1674's defect read from both the sharing side and the
|
|
28
|
+
* settle side, and the reason `SingleFlightOptions.generation` exists.
|
|
29
|
+
*
|
|
30
|
+
* This is the repo's extracted primitive (`clients/generation-guard.ts`, #1754)
|
|
31
|
+
* rather than a hand-rolled counter (#2455 fix round 4, F1). Two things come
|
|
32
|
+
* with it that the hand-rolled form did not have: the source is declared by
|
|
33
|
+
* construction, so the #1754 ratchet can see it; and `guardedWrite` puts every
|
|
34
|
+
* superseded write in the degradation ledger as a bounded
|
|
35
|
+
* `generation-guard-stale-write` record per (source, subject), so a dogfood
|
|
36
|
+
* session can tell "the guard is working" from "the guard never fires" instead
|
|
37
|
+
* of watching superseded writes vanish silently (F4).
|
|
38
|
+
*
|
|
39
|
+
* A module-level source rather than a per-instance one, deliberately. It is a
|
|
40
|
+
* counter, so nothing has to be hand-maintained and no flight is retained for
|
|
41
|
+
* the sake of superseding it. And `toolchainProbeFlights` is itself shared
|
|
42
|
+
* ACROSS instances (#2131: two clients probing the same candidate list run one
|
|
43
|
+
* sweep), so its supersede signal cannot live on any one instance. The cost is
|
|
44
|
+
* that resetting one toolchain also supersedes another's in-flight sweep; that
|
|
45
|
+
* is at worst one extra probe, never a wrong verdict, and every registered
|
|
46
|
+
* reset fires in the same `session_start` block anyway.
|
|
47
|
+
*/
|
|
48
|
+
const toolchainGenerations = createGenerationSource("toolchain-availability");
|
|
49
|
+
const toolchainProbeFlights = createAvailabilityProbeFlight({ generation: toolchainGenerations.current });
|
|
16
50
|
/**
|
|
17
51
|
* Own one toolchain's availability: sweep the platform candidate list, memoize
|
|
18
52
|
* the path that answered, and park the verdict behind the shared latch so a
|
|
@@ -28,33 +62,61 @@ export function createToolchainAvailability(config) {
|
|
|
28
62
|
let sweepHostStallMs = 0;
|
|
29
63
|
/** What the last classified candidate returned, for the decision record. */
|
|
30
64
|
let sweepEvidence;
|
|
31
|
-
|
|
65
|
+
const ensureFlight = createSingleFlight({
|
|
66
|
+
generation: toolchainGenerations.current,
|
|
67
|
+
});
|
|
32
68
|
async function findPath() {
|
|
33
69
|
if (toolPath)
|
|
34
70
|
return toolPath;
|
|
35
71
|
const paths = process.platform === "win32" ? config.windowsPaths : config.unixPaths;
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
72
|
+
const handle = toolchainGenerations.capture();
|
|
73
|
+
const shared = toolchainProbeFlights.run(`toolchain:${config.tool}|${config.probeArgs.join("|")}|${config.windowsPaths.join("|")}|${config.unixPaths.join("|")}`, () => probeAvailabilityCandidates(paths, config.probeArgs, config.budgetMs));
|
|
74
|
+
const sweep = await shared.promise;
|
|
75
|
+
// A reset may have landed while this sweep was running. Its answer then
|
|
76
|
+
// describes the session that just ended, so it must not write itself back
|
|
77
|
+
// into the memo `reset()` just cleared, nor into the classification fields
|
|
78
|
+
// the replacement flight reads to build ITS verdict. The memo is the
|
|
79
|
+
// sharper half: a superseded sweep that writes `foundPath` latches a path
|
|
80
|
+
// the reset may have been called BECAUSE it went away, so the seam that
|
|
81
|
+
// exists to un-latch a false "missing" would instead latch a false
|
|
82
|
+
// "found". Callers already holding this promise still get what the sweep
|
|
83
|
+
// actually found.
|
|
84
|
+
handle.guardedWrite(`findPath:${config.tool}`, () => {
|
|
85
|
+
sweepSawTransient = sweep.sawTransient;
|
|
86
|
+
sweepTransientCause = sweep.transientCause;
|
|
87
|
+
sweepHostStallMs = sweep.hostStallMs;
|
|
88
|
+
sweepEvidence = sweep.evidence;
|
|
89
|
+
if (sweep.foundPath)
|
|
90
|
+
toolPath = sweep.foundPath;
|
|
91
|
+
});
|
|
43
92
|
return sweep.foundPath;
|
|
44
93
|
}
|
|
45
94
|
async function resolveAvailability() {
|
|
46
95
|
const startedAt = Date.now();
|
|
47
|
-
const
|
|
96
|
+
const handle = toolchainGenerations.capture();
|
|
97
|
+
const resolvedPath = await findPath();
|
|
98
|
+
const found = resolvedPath !== null;
|
|
99
|
+
// #2455 fix round 3, F2: a reset during the probe opened a NEW session.
|
|
100
|
+
// This flight answers the OLD one, so writing its verdict into the
|
|
101
|
+
// freshly cleared latch would re-latch a stale "missing" for the whole
|
|
102
|
+
// new session — the bug the reset exists to prevent, reintroduced by the
|
|
103
|
+
// reset's own timing. Report the verdict to this flight's own callers;
|
|
104
|
+
// do not latch it, and say so in the decision record's `latched`.
|
|
105
|
+
const superseded = !handle.isCurrent();
|
|
48
106
|
if (found) {
|
|
49
|
-
availabilityLatch.noteAvailable();
|
|
50
|
-
|
|
107
|
+
handle.guardedWrite(`latch:${config.tool}`, () => availabilityLatch.noteAvailable());
|
|
108
|
+
// The RESOLVED path, not the memo (#2455 fix round 4, F6). A superseded
|
|
109
|
+
// sweep is forbidden from writing `toolPath`, so reading the memo here
|
|
110
|
+
// logged `Go found: null` on exactly the branch this line exists to
|
|
111
|
+
// explain.
|
|
112
|
+
config.log(`${config.label} found: ${resolvedPath}`);
|
|
51
113
|
logAvailabilityDecision({
|
|
52
114
|
tool: config.tool,
|
|
53
115
|
verdict: "available",
|
|
54
116
|
outcome: "success",
|
|
55
117
|
cause: "ok",
|
|
56
118
|
elapsedMs: Date.now() - startedAt,
|
|
57
|
-
latched:
|
|
119
|
+
latched: !superseded,
|
|
58
120
|
hostStallMs: sweepHostStallMs,
|
|
59
121
|
budgetMs: config.budgetMs,
|
|
60
122
|
classifiedBy: "probe",
|
|
@@ -66,14 +128,14 @@ export function createToolchainAvailability(config) {
|
|
|
66
128
|
// whether the toolchain is installed; it expires instead of latching.
|
|
67
129
|
const outcome = sweepSawTransient ? "transient" : "missing";
|
|
68
130
|
const cause = sweepSawTransient ? sweepTransientCause : "not-found";
|
|
69
|
-
const retryAfterMs = availabilityLatch.noteUnavailable(outcome, cause);
|
|
131
|
+
const retryAfterMs = handle.guardedWrite(`latch:${config.tool}`, () => availabilityLatch.noteUnavailable(outcome, cause)) ?? 0;
|
|
70
132
|
logAvailabilityDecision({
|
|
71
133
|
tool: config.tool,
|
|
72
134
|
verdict: "unavailable",
|
|
73
135
|
outcome,
|
|
74
136
|
cause,
|
|
75
137
|
elapsedMs: Date.now() - startedAt,
|
|
76
|
-
latched: outcome !== "transient",
|
|
138
|
+
latched: !superseded && outcome !== "transient",
|
|
77
139
|
hostStallMs: sweepHostStallMs,
|
|
78
140
|
...(retryAfterMs > 0 && { retryAfterMs }),
|
|
79
141
|
budgetMs: config.budgetMs,
|
|
@@ -90,17 +152,18 @@ export function createToolchainAvailability(config) {
|
|
|
90
152
|
const memo = availabilityLatch.read();
|
|
91
153
|
if (memo !== null)
|
|
92
154
|
return memo;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
155
|
+
return ensureFlight.run("availability", resolveAvailability);
|
|
156
|
+
}
|
|
157
|
+
function reset() {
|
|
158
|
+
toolPath = null;
|
|
159
|
+
availabilityLatch.reset();
|
|
160
|
+
// Supersede every sweep and availability flight already running, in this
|
|
161
|
+
// instance and in the shared registry. No `clear()`: the generation bump
|
|
162
|
+
// is what makes a stale flight unjoinable and unable to re-latch, and
|
|
163
|
+
// `SingleFlight.release`'s identity check already stops it from evicting
|
|
164
|
+
// its successor. Clearing the SHARED registry here would additionally
|
|
165
|
+
// drop another toolchain's live claim and cost it a duplicate sweep.
|
|
166
|
+
toolchainGenerations.bump();
|
|
104
167
|
}
|
|
105
|
-
return { findPath, isAvailable };
|
|
168
|
+
return { findPath, isAvailable, reset };
|
|
106
169
|
}
|
|
@@ -16,6 +16,7 @@ import * as fs from "node:fs";
|
|
|
16
16
|
import * as path from "node:path";
|
|
17
17
|
import yaml from "../../deps/js-yaml.js";
|
|
18
18
|
import { BoundedLruCache } from "../../bounded-cache.js";
|
|
19
|
+
import { compareOrdinal } from "../../string-utils.js";
|
|
19
20
|
// --- Constants ---
|
|
20
21
|
/** Overly broad patterns that match everything (cause false positive explosions) */
|
|
21
22
|
export const OVERLY_BROAD_PATTERNS = [
|
|
@@ -28,18 +29,22 @@ export const OVERLY_BROAD_PATTERNS = [
|
|
|
28
29
|
];
|
|
29
30
|
/** Maximum complexity score for rules in blockingOnly mode */
|
|
30
31
|
export const MAX_BLOCKING_RULE_COMPLEXITY = 8;
|
|
32
|
+
/**
|
|
33
|
+
* `getCachedRules` re-stats a rule directory's files at most once per this
|
|
34
|
+
* window (#2262 round 2). `scanner.ts:235` calls `loadYamlRules` per file
|
|
35
|
+
* scanned; a full metadata sweep (readdir + stat every rule file) on every
|
|
36
|
+
* warmed call measured 250x master's per-call cost on a mid-size catalog.
|
|
37
|
+
* Same value and same shape as `file-utils.ts`'s
|
|
38
|
+
* `PROJECT_IGNORE_FRESHNESS_CADENCE_MS` (#2159/#2071): 2s is the accepted
|
|
39
|
+
* staleness bound for a rule tree pi-lens itself did not just write.
|
|
40
|
+
*/
|
|
41
|
+
export const RULES_CACHE_FRESHNESS_CADENCE_MS = 2_000;
|
|
31
42
|
// --- Caches ---
|
|
32
43
|
const rulesCache = new BoundedLruCache(64);
|
|
33
44
|
const blockingRulesCache = new BoundedLruCache(64);
|
|
34
45
|
const contentRulesCache = new BoundedLruCache(64);
|
|
35
46
|
const contentBlockingRulesCache = new BoundedLruCache(64);
|
|
36
47
|
// --- Public API ---
|
|
37
|
-
export function clearRulesCache() {
|
|
38
|
-
rulesCache.clear();
|
|
39
|
-
blockingRulesCache.clear();
|
|
40
|
-
contentRulesCache.clear();
|
|
41
|
-
contentBlockingRulesCache.clear();
|
|
42
|
-
}
|
|
43
48
|
export function loadYamlRules(ruleDir, severityFilter) {
|
|
44
49
|
return getCachedRules(ruleDir, severityFilter);
|
|
45
50
|
}
|
|
@@ -48,7 +53,14 @@ function findYamlRuleFiles(ruleDir) {
|
|
|
48
53
|
try {
|
|
49
54
|
entries = fs
|
|
50
55
|
.readdirSync(ruleDir, { withFileTypes: true })
|
|
51
|
-
|
|
56
|
+
// Ordinal (code-unit), not locale, comparison: this order feeds the
|
|
57
|
+
// index-aligned file-stamp identity compared in `getCachedRules` and
|
|
58
|
+
// the hash input order in `loadYamlRulesFresh`. `localeCompare` can
|
|
59
|
+
// order the same file set differently across locales/processes,
|
|
60
|
+
// which would desync the two and mint spurious cache misses or
|
|
61
|
+
// (worse) content-hash collisions across differently-ordered runs.
|
|
62
|
+
// Same reasoning as `rule-cache.ts`'s `computeRuleHash` (#2155/#2165).
|
|
63
|
+
.sort((a, b) => compareOrdinal(a.name, b.name));
|
|
52
64
|
}
|
|
53
65
|
catch {
|
|
54
66
|
return [];
|
|
@@ -118,20 +130,42 @@ export function getCachedRules(ruleDir, severityFilter) {
|
|
|
118
130
|
if (!fs.existsSync(ruleDir)) {
|
|
119
131
|
return [];
|
|
120
132
|
}
|
|
121
|
-
let currentMtime = 0;
|
|
122
|
-
try {
|
|
123
|
-
currentMtime = fs.statSync(ruleDir).mtimeMs;
|
|
124
|
-
}
|
|
125
|
-
catch {
|
|
126
|
-
return [];
|
|
127
|
-
}
|
|
128
133
|
const cache = severityFilter === "error" ? blockingRulesCache : rulesCache;
|
|
129
134
|
const cached = cache.get(ruleDir);
|
|
130
|
-
|
|
135
|
+
const now = Date.now();
|
|
136
|
+
if (cached && now - cached.checkedAtMs < RULES_CACHE_FRESHNESS_CADENCE_MS) {
|
|
137
|
+
// Inside the cadence window: no stat sweep at all. `getCachedRules` is
|
|
138
|
+
// the bundled-catalog path only (project rules route through
|
|
139
|
+
// `loadYamlRulesFresh` — see `ast-grep-napi.ts`'s route split), and
|
|
140
|
+
// `scanner.ts:235` calls it once per scanned file, so a per-call
|
|
141
|
+
// `readdirSync`+`statSync*N` sweep is the hot-path cost this window
|
|
142
|
+
// exists to remove.
|
|
131
143
|
return cached.rules;
|
|
132
144
|
}
|
|
133
|
-
const
|
|
134
|
-
|
|
145
|
+
const files = findYamlRuleFiles(ruleDir);
|
|
146
|
+
const fileStamps = files.flatMap((file) => {
|
|
147
|
+
try {
|
|
148
|
+
const stat = fs.statSync(file);
|
|
149
|
+
return [{ path: file, mtimeMs: stat.mtimeMs, size: stat.size }];
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
const metadataMatches = cached?.files.length === fileStamps.length &&
|
|
156
|
+
cached.files.every((file, index) => file.path === fileStamps[index].path &&
|
|
157
|
+
file.mtimeMs === fileStamps[index].mtimeMs &&
|
|
158
|
+
file.size === fileStamps[index].size);
|
|
159
|
+
if (cached && metadataMatches) {
|
|
160
|
+
// Nothing drifted. Re-arm the window from *now* regardless — otherwise
|
|
161
|
+
// the next call re-stats immediately instead of waiting a full cadence
|
|
162
|
+
// (the #2260/#2071 "checkedAtMs must re-arm on every check, not only on
|
|
163
|
+
// a miss" lesson).
|
|
164
|
+
cached.checkedAtMs = now;
|
|
165
|
+
return cached.rules;
|
|
166
|
+
}
|
|
167
|
+
const rules = loadYamlRuleFiles(files, severityFilter);
|
|
168
|
+
cache.set(ruleDir, { rules, files: fileStamps, checkedAtMs: now });
|
|
135
169
|
return rules;
|
|
136
170
|
}
|
|
137
171
|
export function isOverlyBroadPattern(pattern) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* NDJSON telemetry log for diagnostic disposition marks (#690). Same shape as
|
|
3
|
-
* clients/actionable-warnings-logger.ts: machine-global (
|
|
3
|
+
* clients/actionable-warnings-logger.ts: machine-global (getGlobalPiLensLogDir),
|
|
4
4
|
* size-capped with a single `.log.1` backup, isTestMode no-op.
|
|
5
5
|
*
|
|
6
6
|
* Why a log at all: #181 identified false-positive marks flowing to telemetry
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import * as path from "node:path";
|
|
15
15
|
import { isTestMode } from "./env-utils.js";
|
|
16
|
-
import {
|
|
16
|
+
import { getGlobalPiLensLogDir } from "./probe-home-state.js";
|
|
17
17
|
import { createNdjsonLogger } from "./ndjson-logger.js";
|
|
18
|
-
const DISPOSITION_LOG_DIR =
|
|
18
|
+
const DISPOSITION_LOG_DIR = getGlobalPiLensLogDir();
|
|
19
19
|
const DISPOSITION_LOG_FILE = path.join(DISPOSITION_LOG_DIR, "dispositions.log");
|
|
20
20
|
const DISPOSITION_LOG_BACKUP_FILE = path.join(DISPOSITION_LOG_DIR, "dispositions.log.1");
|
|
21
21
|
const MAX_LOG_BYTES = Math.max(128 * 1024, Number.parseInt(process.env.PI_LENS_DISPOSITION_LOG_MAX_BYTES ?? "1048576", 10) || 1048576);
|