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
|
@@ -10,7 +10,8 @@ import { detectFileKind } from "../file-kinds.js";
|
|
|
10
10
|
import { getLspCapableKinds, getPrimaryDispatchGroup, } from "../language-policy.js";
|
|
11
11
|
import { formatSlopScoreSummary, } from "../session-summary.js";
|
|
12
12
|
import { clearCoverageNoticeState, clearLatencyReports, createDispatchContext, dispatchForFile, formatLatencyReport, getLatencyReports, RunnerRegistry, } from "./dispatcher.js";
|
|
13
|
-
import {
|
|
13
|
+
import { recordDegradationOnce } from "../degradation-ledger.js";
|
|
14
|
+
import { FactStore, setFactStoreEvictionReporter } from "./fact-store.js";
|
|
14
15
|
import { TOOL_PLANS } from "./plan.js";
|
|
15
16
|
// Re-export latency tracking types and functions
|
|
16
17
|
export { clearLatencyReports, formatLatencyReport, getLatencyReports };
|
|
@@ -37,7 +38,7 @@ import { clearModuleGraphCache } from "../review-graph/workspace-modules.js";
|
|
|
37
38
|
import { releaseWorkspaceTopologyIdleTimers } from "../workspace-topology.js";
|
|
38
39
|
import { RUNTIME_CONFIG } from "../runtime-config.js";
|
|
39
40
|
import { findCompiledClassesDir, hasJavaBuildDescriptor, } from "../tool-policy.js";
|
|
40
|
-
import {
|
|
41
|
+
import { removeWordIndexDocumentAsync, updateWordIndexDocumentForEdit, WORD_INDEX_MAX_BYTES, } from "../word-index.js";
|
|
41
42
|
import { reconcileCascadeNeighborLspErrors } from "../widget-state.js";
|
|
42
43
|
import { findAuxiliaryProfileForSource } from "./auxiliary-lsp.js";
|
|
43
44
|
// Register fact providers. All register eagerly here (the dispatch entry) — the
|
|
@@ -98,8 +99,46 @@ registerRule(commentedCredentialsRule);
|
|
|
98
99
|
export function applyProjectLensConfig(cwd) {
|
|
99
100
|
return loadPiLensProjectConfig(cwd);
|
|
100
101
|
}
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
// "dispatch" labels this store's eviction telemetry (#2243 review round 3,
|
|
103
|
+
// F1) — five other production FactStore instances exist (mcp/analyze.ts,
|
|
104
|
+
// lens-map.ts, project-diagnostics/scanner.ts, runtime-session.ts's
|
|
105
|
+
// session-start call-graph task, mcp/cli.ts); a shared constant subject let
|
|
106
|
+
// runtime-session's session-start review-graph walk consume the once-per-
|
|
107
|
+
// session ledger slot before any dispatch ran, misattributing the record.
|
|
108
|
+
const sessionFacts = new FactStore("dispatch");
|
|
109
|
+
// #2243 item 4: wire the fact-store's capacity-eviction telemetry to the
|
|
110
|
+
// ledger from HERE, not from fact-store.ts. fact-store stays an import leaf so
|
|
111
|
+
// it cannot re-enter the safe-spawn ↔ degradation-ledger cycle (see
|
|
112
|
+
// `setFactStoreEvictionReporter`). The reporter forwards each store's own
|
|
113
|
+
// subject, so the ledger's per-kind+subject dedupe emits exactly ONE record
|
|
114
|
+
// per session PER STORE, re-arming when the ledger resets at session_start.
|
|
115
|
+
//
|
|
116
|
+
// #2247 review F1: the axis was previously dropped here, so
|
|
117
|
+
// `recordDegradationOnce`'s `${kind}\0${subject}` dedupe collapsed a store's
|
|
118
|
+
// count-axis and byte-axis evictions onto the SAME key — whichever axis
|
|
119
|
+
// tripped first (always count, since 1024 typical files land far under 64
|
|
120
|
+
// MiB) silently consumed the once-per-session slot and a later byte-axis
|
|
121
|
+
// eviction on that same store recorded nothing. Fold the axis into the
|
|
122
|
+
// subject (`<store>:<axis>`), matching this ledger's own `<store>:<file>`
|
|
123
|
+
// convention (see `demoted-finding-retired`'s doc comment) rather than
|
|
124
|
+
// growing the `kind` union per axis. `pinned-over-budget` (#2247 review F2)
|
|
125
|
+
// is a distinct condition, not an eviction, so it gets its own kind instead
|
|
126
|
+
// of sharing the eviction kind's subject convention.
|
|
127
|
+
setFactStoreEvictionReporter((subject, axis, reason) => {
|
|
128
|
+
if (axis === "pinned-over-budget") {
|
|
129
|
+
recordDegradationOnce({
|
|
130
|
+
kind: "fact-store-pinned-over-budget",
|
|
131
|
+
subject,
|
|
132
|
+
reason,
|
|
133
|
+
});
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
recordDegradationOnce({
|
|
137
|
+
kind: "fact-store-capacity-eviction",
|
|
138
|
+
subject: `${subject}:${axis}`,
|
|
139
|
+
reason,
|
|
140
|
+
});
|
|
141
|
+
});
|
|
103
142
|
const sessionRunnerRegistry = new RunnerRegistry();
|
|
104
143
|
registerDefaultRunners(sessionRunnerRegistry);
|
|
105
144
|
const LSP_CAPABLE_KINDS = new Set(getLspCapableKinds());
|
|
@@ -137,32 +176,62 @@ function scheduleAstGrepWarningScan(filePath, cwd, pi, logContext) {
|
|
|
137
176
|
const existing = astGrepWarnDebounceTimers.get(filePath);
|
|
138
177
|
if (existing)
|
|
139
178
|
clearTimeout(existing);
|
|
140
|
-
const timer = setTimeout(
|
|
179
|
+
const timer = setTimeout(() => {
|
|
141
180
|
astGrepWarnDebounceTimers.delete(filePath);
|
|
142
|
-
|
|
143
|
-
const ctx = createDispatchContext(filePath, cwd, pi, sessionFacts, false);
|
|
144
|
-
if (ctx.kind !== "jsts")
|
|
145
|
-
return;
|
|
146
|
-
// Single-runner group: ast-grep only, warning mode (blockingOnly=false)
|
|
147
|
-
const group = {
|
|
148
|
-
mode: "all",
|
|
149
|
-
runnerIds: ["ast-grep"],
|
|
150
|
-
filterKinds: ["jsts"],
|
|
151
|
-
};
|
|
152
|
-
const result = await dispatchForFile(ctx, [group], sessionRunnerRegistry);
|
|
153
|
-
if (result.diagnostics.length === 0)
|
|
154
|
-
return;
|
|
155
|
-
const logger = getDiagnosticLogger();
|
|
156
|
-
for (const d of result.diagnostics) {
|
|
157
|
-
logger.logCaught(d, logContext, false);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
catch {
|
|
181
|
+
void runAstGrepWarningScan(filePath, cwd, pi, logContext).catch(() => {
|
|
161
182
|
// Non-critical background scan — swallow errors silently
|
|
162
|
-
}
|
|
183
|
+
});
|
|
163
184
|
}, AST_GREP_WARN_DEBOUNCE_MS);
|
|
164
185
|
astGrepWarnDebounceTimers.set(filePath, timer);
|
|
165
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* Run the debounced ast-grep warning scan for one jsts file.
|
|
189
|
+
*
|
|
190
|
+
* #2243 item 1: this is a dispatch entry point like dispatchLint*, so it must
|
|
191
|
+
* follow the same discipline. It fires ~2 s after the last write, and in that
|
|
192
|
+
* window the review-graph project walk (`buildOrUpdateGraph`) seeds facts for
|
|
193
|
+
* every file into the SAME `sessionFacts`, which can evict this file's
|
|
194
|
+
* `file.content`. `dispatcher.ts` reads that fact back with `?? ""` for inline
|
|
195
|
+
* suppressions, so a stale or evicted read silently stops applying
|
|
196
|
+
* `pi-lens-ignore`. Two guards, matching the other dispatch callers:
|
|
197
|
+
* 1. `beginDispatchFor` pins the file for the dispatch (released in the
|
|
198
|
+
* finally), so a concurrent walk cannot evict it mid-dispatch. This
|
|
199
|
+
* does NOT clear the file's existing facts the way `clearFileFactsFor`
|
|
200
|
+
* does for the other dispatch callers (#2243 review round 3, F5): this
|
|
201
|
+
* scan fires ~2s after the SAME edit's inline dispatch already
|
|
202
|
+
* re-derived every fact for this file, so a second clear+re-derive
|
|
203
|
+
* here bought nothing but ~51ms of redundant re-parsing across all
|
|
204
|
+
* five providers (measured; a warm-cache read is ~1ms).
|
|
205
|
+
* 2. `runProviders` still re-derives `file.content` when it's actually
|
|
206
|
+
* missing (a genuine miss — evicted, or never computed), so the read
|
|
207
|
+
* never depends on staleness.
|
|
208
|
+
* `facts` is injected only for tests; production always uses `sessionFacts`.
|
|
209
|
+
*/
|
|
210
|
+
export async function runAstGrepWarningScan(filePath, cwd, pi, logContext, facts = sessionFacts) {
|
|
211
|
+
const ctx = createDispatchContext(filePath, cwd, pi, facts, false);
|
|
212
|
+
if (ctx.kind !== "jsts")
|
|
213
|
+
return;
|
|
214
|
+
facts.beginDispatchFor(ctx.filePath);
|
|
215
|
+
try {
|
|
216
|
+
// Single-runner group: ast-grep only, warning mode (blockingOnly=false)
|
|
217
|
+
const group = {
|
|
218
|
+
mode: "all",
|
|
219
|
+
runnerIds: ["ast-grep"],
|
|
220
|
+
filterKinds: ["jsts"],
|
|
221
|
+
};
|
|
222
|
+
await runProviders(ctx);
|
|
223
|
+
const result = await dispatchForFile(ctx, [group], sessionRunnerRegistry);
|
|
224
|
+
if (result.diagnostics.length === 0)
|
|
225
|
+
return;
|
|
226
|
+
const logger = getDiagnosticLogger();
|
|
227
|
+
for (const d of result.diagnostics) {
|
|
228
|
+
logger.logCaught(d, logContext, false);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
finally {
|
|
232
|
+
facts.endDispatchFor(ctx.filePath);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
166
235
|
function resetSessionSlopScore() {
|
|
167
236
|
sessionSlopRuleCounts.clear();
|
|
168
237
|
sessionSlopDiagnosticCount = 0;
|
|
@@ -315,7 +384,6 @@ export function resetDispatchBaselines(cwd) {
|
|
|
315
384
|
clearModuleGraphCache();
|
|
316
385
|
recentlyCleanNeighborCache.clear();
|
|
317
386
|
primaryFilesThisTurn.clear();
|
|
318
|
-
cascadeDiagnosticBaselines.clear();
|
|
319
387
|
cascadeSessionStats = {
|
|
320
388
|
runs: 0,
|
|
321
389
|
diagnosticsSurfaced: 0,
|
|
@@ -335,10 +403,16 @@ export function getCascadeSessionStats() {
|
|
|
335
403
|
}
|
|
336
404
|
const recentlyCleanNeighborCache = new Map();
|
|
337
405
|
/** O(1) entry count of this module's turn-bounded cache (#1123 item 2
|
|
338
|
-
* memory attribution) — a `Map.size` read, never iterated.
|
|
406
|
+
* memory attribution) — a `Map.size` read, never iterated.
|
|
407
|
+
* `sessionFactEntries` (#2282) is the dispatch `FactStore`'s own
|
|
408
|
+
* `sessionFacts` footprint: the fixed-vocabulary map (small, unbounded by
|
|
409
|
+
* design) plus the bounded per-file map (capped at `MAX_SESSION_FACT_RECORDS`
|
|
410
|
+
* — see `fact-store.ts`), so this number is now itself bounded rather than
|
|
411
|
+
* invisible growth. */
|
|
339
412
|
export function getDispatchCascadeCacheStats() {
|
|
340
413
|
return {
|
|
341
414
|
recentlyCleanNeighborCacheSize: recentlyCleanNeighborCache.size,
|
|
415
|
+
sessionFactEntries: sessionFacts.getSessionFactEntryCount(),
|
|
342
416
|
};
|
|
343
417
|
}
|
|
344
418
|
const RECENTLY_CLEAN_TTL_TURNS = 5;
|
|
@@ -496,6 +570,11 @@ function touchReverseDepsEntry(key, entry, armIdleTimer = true) {
|
|
|
496
570
|
entry.idleTimer.unref?.();
|
|
497
571
|
}
|
|
498
572
|
function setReverseDepsEntry(key, entry, armIdleTimer = true) {
|
|
573
|
+
const previous = reverseDepsIndexCache.get(key);
|
|
574
|
+
if (previous?.idleTimer !== undefined) {
|
|
575
|
+
clearTimeout(previous.idleTimer);
|
|
576
|
+
previous.idleTimer = undefined;
|
|
577
|
+
}
|
|
499
578
|
const resident = { ...entry, lastUsedAt: Date.now() };
|
|
500
579
|
reverseDepsIndexCache.set(key, resident);
|
|
501
580
|
touchReverseDepsEntry(key, resident, armIdleTimer);
|
|
@@ -604,20 +683,28 @@ function isIgnoredCascadeNeighbor(filePath, cwd) {
|
|
|
604
683
|
* debounced persist (never a synchronous write per edit — same #260
|
|
605
684
|
* discipline as the graph).
|
|
606
685
|
*
|
|
607
|
-
* Race safety against a build-in-progress:
|
|
608
|
-
*
|
|
609
|
-
*
|
|
610
|
-
*
|
|
611
|
-
*
|
|
612
|
-
*
|
|
613
|
-
*
|
|
614
|
-
*
|
|
615
|
-
* and
|
|
616
|
-
*
|
|
617
|
-
*
|
|
618
|
-
*
|
|
686
|
+
* Race safety against a build-in-progress: the update runs cooperatively, on
|
|
687
|
+
* an 8 ms budget, through the word index's own per-index operation queue
|
|
688
|
+
* (#2067 AC4). It used to run synchronously here, on the reasoning that two
|
|
689
|
+
* overlapping cascades (#450's unawaited concurrency) could not interleave
|
|
690
|
+
* mid-mutation if nothing ever yielded — the reviewer's 300-document probe had
|
|
691
|
+
* lost 1,719 postings during an 8 ms yield. That hazard is what the queue
|
|
692
|
+
* closed: every async operation on one index is chained behind the previous
|
|
693
|
+
* one in call order, so overlapping cascades are ordered rather than
|
|
694
|
+
* interleaved, and the cooperative arena compactor is separately proof against
|
|
695
|
+
* a synchronous edit landing mid-copy (#2117 review F2). What the synchronous
|
|
696
|
+
* variant could not fix is its own cost: one replacement on a 2,600-document
|
|
697
|
+
* corpus held the event loop for the whole replacement, which is the block
|
|
698
|
+
* #2067 measured at 1.1 s before interning and still tens of ms after it.
|
|
699
|
+
*
|
|
700
|
+
* The only cross-build hazard is a full session-start rebuild REPLACING
|
|
701
|
+
* `runtime.wordIndex` with a new object between the caller reading
|
|
702
|
+
* `runtime.wordIndex` (in runtime-tool-result.ts, synchronous) and this
|
|
703
|
+
* function receiving it — in that case this call simply mutates whichever
|
|
704
|
+
* index object it was handed (old or new), and the other one is
|
|
705
|
+
* abandoned/superseded, never corrupted.
|
|
619
706
|
*/
|
|
620
|
-
function updateWordIndexForCascade(args) {
|
|
707
|
+
async function updateWordIndexForCascade(args) {
|
|
621
708
|
const { wordIndex, filePath, content, onUpdated, dbg } = args;
|
|
622
709
|
if (!wordIndex || !wordIndex.forward)
|
|
623
710
|
return;
|
|
@@ -625,11 +712,14 @@ function updateWordIndexForCascade(args) {
|
|
|
625
712
|
return;
|
|
626
713
|
const byteLength = Buffer.byteLength(content, "utf-8");
|
|
627
714
|
if (byteLength > WORD_INDEX_MAX_BYTES) {
|
|
628
|
-
|
|
715
|
+
await removeWordIndexDocumentAsync(wordIndex, filePath);
|
|
629
716
|
dbg?.(`word-index per-edit: dropped ${filePath} (over size cap)`);
|
|
630
717
|
}
|
|
631
718
|
else {
|
|
632
|
-
|
|
719
|
+
await updateWordIndexDocumentForEdit(wordIndex, {
|
|
720
|
+
path: filePath,
|
|
721
|
+
content,
|
|
722
|
+
});
|
|
633
723
|
dbg?.(`word-index per-edit: updated ${filePath}`);
|
|
634
724
|
}
|
|
635
725
|
onUpdated?.(wordIndex);
|
|
@@ -689,7 +779,7 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
689
779
|
// The old hazard — this update silently orphaning a SECOND entry next to the
|
|
690
780
|
// walker's original-cased one — is now structurally impossible at the map
|
|
691
781
|
// layer, so this seam no longer has to hand-match the build path's key shape.
|
|
692
|
-
updateWordIndexForCascade({
|
|
782
|
+
await updateWordIndexForCascade({
|
|
693
783
|
wordIndex,
|
|
694
784
|
filePath: nodePath.resolve(filePath),
|
|
695
785
|
content: fileContent,
|
|
@@ -1854,10 +1944,8 @@ function diagnosticDeltaKey(diagnostic) {
|
|
|
1854
1944
|
function applyCascadeDeltaBaselines(neighbors) {
|
|
1855
1945
|
return neighbors.map((neighbor) => {
|
|
1856
1946
|
const baselineKey = `session.baseline.cascade.${normalizeMapKey(neighbor.filePath)}`;
|
|
1857
|
-
const previous =
|
|
1858
|
-
|
|
1859
|
-
cascadeDiagnosticBaselines.set(baselineKey, [...neighbor.diagnostics]);
|
|
1860
|
-
sessionFacts.setSessionFact(baselineKey, [...neighbor.diagnostics]);
|
|
1947
|
+
const previous = sessionFacts.getBoundedSessionFact(baselineKey);
|
|
1948
|
+
sessionFacts.setBoundedSessionFact(baselineKey, [...neighbor.diagnostics]);
|
|
1861
1949
|
if (!previous)
|
|
1862
1950
|
return neighbor;
|
|
1863
1951
|
const before = new Set(previous.map(diagnosticDeltaKey));
|
|
@@ -1982,16 +2070,23 @@ export async function dispatchLint(filePath, cwd, pi, modifiedRanges, projectRoo
|
|
|
1982
2070
|
// pre-existing issues after the first write.
|
|
1983
2071
|
const ctx = createDispatchContext(filePath, cwd, pi, sessionFacts, true, modifiedRanges, projectRoot);
|
|
1984
2072
|
sessionFacts.clearFileFactsFor(ctx.filePath);
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
2073
|
+
// #2243 item 2: release the pin when the dispatch settles, so the pin set
|
|
2074
|
+
// tracks in-flight dispatches rather than the last N files touched.
|
|
2075
|
+
try {
|
|
2076
|
+
const kind = ctx.kind;
|
|
2077
|
+
if (!kind)
|
|
2078
|
+
return "";
|
|
2079
|
+
const groups = withSpotbugsGroup(kind, getDispatchGroupsForKind(kind, pi), ctx);
|
|
2080
|
+
if (groups.length === 0)
|
|
2081
|
+
return "";
|
|
2082
|
+
await runProviders(ctx);
|
|
2083
|
+
const result = await dispatchForFile(ctx, groups, sessionRunnerRegistry);
|
|
2084
|
+
trackSessionSlopStats(ctx, result.diagnostics);
|
|
2085
|
+
return result.output;
|
|
2086
|
+
}
|
|
2087
|
+
finally {
|
|
2088
|
+
sessionFacts.endDispatchFor(ctx.filePath);
|
|
2089
|
+
}
|
|
1995
2090
|
}
|
|
1996
2091
|
/**
|
|
1997
2092
|
* Run linting and return full result (including diagnostics)
|
|
@@ -2002,44 +2097,50 @@ export async function dispatchLintWithResult(filePath, cwd, pi, modifiedRanges,
|
|
|
2002
2097
|
// facade, pass blockingOnly=false to run every runner.
|
|
2003
2098
|
const ctx = createDispatchContext(filePath, cwd, pi, sessionFacts, options?.blockingOnly ?? true, modifiedRanges, options?.projectRoot, options?.writeIndex, options?.telemetryModel, options?.telemetryProvider);
|
|
2004
2099
|
sessionFacts.clearFileFactsFor(ctx.filePath);
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2100
|
+
// #2243 item 2: release the pin when the dispatch settles.
|
|
2101
|
+
try {
|
|
2102
|
+
const kind = ctx.kind;
|
|
2103
|
+
if (!kind) {
|
|
2104
|
+
return {
|
|
2105
|
+
diagnostics: [],
|
|
2106
|
+
blockers: [],
|
|
2107
|
+
warnings: [],
|
|
2108
|
+
baselineWarningCount: 0,
|
|
2109
|
+
fixed: [],
|
|
2110
|
+
resolvedCount: 0,
|
|
2111
|
+
output: "",
|
|
2112
|
+
blockerOutput: "",
|
|
2113
|
+
hasBlockers: false,
|
|
2114
|
+
};
|
|
2115
|
+
}
|
|
2116
|
+
const groups = withSpotbugsGroup(kind, getDispatchGroupsForKind(kind, pi), ctx);
|
|
2117
|
+
if (groups.length === 0) {
|
|
2118
|
+
return {
|
|
2119
|
+
diagnostics: [],
|
|
2120
|
+
blockers: [],
|
|
2121
|
+
warnings: [],
|
|
2122
|
+
baselineWarningCount: 0,
|
|
2123
|
+
fixed: [],
|
|
2124
|
+
resolvedCount: 0,
|
|
2125
|
+
output: "",
|
|
2126
|
+
blockerOutput: "",
|
|
2127
|
+
hasBlockers: false,
|
|
2128
|
+
};
|
|
2129
|
+
}
|
|
2130
|
+
await runProviders(ctx);
|
|
2131
|
+
const result = await dispatchForFile(ctx, groups, sessionRunnerRegistry);
|
|
2132
|
+
trackSessionSlopStats(ctx, result.diagnostics);
|
|
2133
|
+
// Schedule debounced ast-grep warning scan for jsts files.
|
|
2134
|
+
// Runs 2s after the last write — collapses rapid sequential edits into one scan.
|
|
2135
|
+
// Results are logged only, never surfaced to the agent.
|
|
2136
|
+
if (kind === "jsts" && logContext) {
|
|
2137
|
+
scheduleAstGrepWarningScan(filePath, cwd, pi, logContext);
|
|
2138
|
+
}
|
|
2139
|
+
return result;
|
|
2032
2140
|
}
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
trackSessionSlopStats(ctx, result.diagnostics);
|
|
2036
|
-
// Schedule debounced ast-grep warning scan for jsts files.
|
|
2037
|
-
// Runs 2s after the last write — collapses rapid sequential edits into one scan.
|
|
2038
|
-
// Results are logged only, never surfaced to the agent.
|
|
2039
|
-
if (kind === "jsts" && logContext) {
|
|
2040
|
-
scheduleAstGrepWarningScan(filePath, cwd, pi, logContext);
|
|
2141
|
+
finally {
|
|
2142
|
+
sessionFacts.endDispatchFor(ctx.filePath);
|
|
2041
2143
|
}
|
|
2042
|
-
return result;
|
|
2043
2144
|
}
|
|
2044
2145
|
/**
|
|
2045
2146
|
* Same real dispatch path as {@link dispatchLintWithResult} (real context, real
|
|
@@ -2064,20 +2165,26 @@ export async function dispatchLintDetailed(filePath, cwd, pi, options) {
|
|
|
2064
2165
|
};
|
|
2065
2166
|
const ctx = createDispatchContext(filePath, cwd, pi, sessionFacts, options?.blockingOnly ?? false, options?.modifiedRanges, options?.projectRoot);
|
|
2066
2167
|
sessionFacts.clearFileFactsFor(ctx.filePath);
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
runners
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2168
|
+
// #2243 item 2: release the pin when the dispatch settles.
|
|
2169
|
+
try {
|
|
2170
|
+
const kind = ctx.kind;
|
|
2171
|
+
if (!kind)
|
|
2172
|
+
return { result: empty, runners: [] };
|
|
2173
|
+
const groups = withSpotbugsGroup(kind, getDispatchGroupsForKind(kind, pi), ctx);
|
|
2174
|
+
if (groups.length === 0)
|
|
2175
|
+
return { result: empty, runners: [] };
|
|
2176
|
+
const runners = [];
|
|
2177
|
+
const sink = (runnerId, result) => {
|
|
2178
|
+
runners.push({ runnerId, result });
|
|
2179
|
+
};
|
|
2180
|
+
await runProviders(ctx);
|
|
2181
|
+
const result = await dispatchForFile(ctx, groups, sessionRunnerRegistry, sink);
|
|
2182
|
+
trackSessionSlopStats(ctx, result.diagnostics);
|
|
2183
|
+
return { result, runners };
|
|
2184
|
+
}
|
|
2185
|
+
finally {
|
|
2186
|
+
sessionFacts.endDispatchFor(ctx.filePath);
|
|
2187
|
+
}
|
|
2081
2188
|
}
|
|
2082
2189
|
/**
|
|
2083
2190
|
* Check if a file should be processed by the dispatcher
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/** Turn-end handoff for runners moved off the post-write critical path. */
|
|
2
|
+
import { incrementDegradationCount } from "../degradation-ledger.js";
|
|
3
|
+
const pending = [];
|
|
4
|
+
export const MAX_PENDING_RUNNER_FINDINGS = 50;
|
|
5
|
+
export function deferRunnerFindings(entry) {
|
|
6
|
+
const tracked = { ...entry, settled: false };
|
|
7
|
+
// Attach exactly once at ownership time. Re-attaching at every turn end
|
|
8
|
+
// accumulates handlers on a promise that may never settle (#2122 F8).
|
|
9
|
+
void tracked.promise.then((result) => {
|
|
10
|
+
tracked.result = result;
|
|
11
|
+
tracked.settled = true;
|
|
12
|
+
}, (error) => {
|
|
13
|
+
tracked.result = {
|
|
14
|
+
status: "failed",
|
|
15
|
+
diagnostics: [],
|
|
16
|
+
semantic: "warning",
|
|
17
|
+
failureKind: "exception",
|
|
18
|
+
failureMessage: String(error).slice(0, 200),
|
|
19
|
+
};
|
|
20
|
+
tracked.settled = true;
|
|
21
|
+
});
|
|
22
|
+
pending.push(tracked);
|
|
23
|
+
if (pending.length > MAX_PENDING_RUNNER_FINDINGS) {
|
|
24
|
+
const evicted = pending.shift();
|
|
25
|
+
if (evicted) {
|
|
26
|
+
incrementDegradationCount({
|
|
27
|
+
kind: "runner-findings-evicted",
|
|
28
|
+
subject: `${evicted.runnerId}:${evicted.filePath}`,
|
|
29
|
+
reason: `pending runner cap ${MAX_PENDING_RUNNER_FINDINGS}`,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Resolve already-finished runner work for this turn. Unfinished work remains
|
|
36
|
+
* owned by the store and is retried at the next turn boundary.
|
|
37
|
+
*/
|
|
38
|
+
export async function drainPendingRunnerFindings(maxWaitMs = 2_000) {
|
|
39
|
+
if (pending.length === 0)
|
|
40
|
+
return [];
|
|
41
|
+
const current = pending.splice(0, pending.length);
|
|
42
|
+
const results = [];
|
|
43
|
+
// Give already-resolved promises one microtask turn without introducing a
|
|
44
|
+
// wall-clock wait. This observes completed work while preserving the F5
|
|
45
|
+
// zero-budget contract for in-flight runners.
|
|
46
|
+
if (maxWaitMs === 0)
|
|
47
|
+
await Promise.resolve();
|
|
48
|
+
if (maxWaitMs > 0 && current.some((entry) => !entry.settled)) {
|
|
49
|
+
await new Promise((resolve) => {
|
|
50
|
+
const timer = setTimeout(resolve, maxWaitMs);
|
|
51
|
+
timer.unref?.();
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
for (const entry of current) {
|
|
55
|
+
if (entry.settled && entry.result) {
|
|
56
|
+
results.push({
|
|
57
|
+
filePath: entry.filePath,
|
|
58
|
+
cwd: entry.cwd,
|
|
59
|
+
projectRoot: entry.projectRoot,
|
|
60
|
+
runnerId: entry.runnerId,
|
|
61
|
+
markedAtMs: entry.markedAtMs,
|
|
62
|
+
writeIndex: entry.writeIndex,
|
|
63
|
+
result: entry.result,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
pending.push(entry);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return results;
|
|
71
|
+
}
|
|
72
|
+
/** Drop a stale answer and record the lost re-run coverage. */
|
|
73
|
+
export function dropStaleRunnerFindings(entry) {
|
|
74
|
+
if (!entry.result)
|
|
75
|
+
return;
|
|
76
|
+
incrementDegradationCount({
|
|
77
|
+
kind: "runner-findings-stale",
|
|
78
|
+
subject: `${entry.runnerId}:${entry.filePath}`,
|
|
79
|
+
reason: "completed result was older than the latest file edit",
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
export function resetPendingRunnerFindings() {
|
|
83
|
+
pending.length = 0;
|
|
84
|
+
}
|
|
85
|
+
export function pendingRunnerFindingsSize() {
|
|
86
|
+
return pending.length;
|
|
87
|
+
}
|
|
@@ -95,7 +95,10 @@ export const LANGUAGE_CAPABILITY_MATRIX = {
|
|
|
95
95
|
json: {
|
|
96
96
|
name: "JSON Processing",
|
|
97
97
|
capabilities: ["format", "lint"],
|
|
98
|
-
writeGroups: [
|
|
98
|
+
writeGroups: [
|
|
99
|
+
primary("json"),
|
|
100
|
+
{ mode: "all", runnerIds: ["trivy-config"], filterKinds: ["json"] },
|
|
101
|
+
],
|
|
99
102
|
},
|
|
100
103
|
markdown: {
|
|
101
104
|
name: "Markdown Processing",
|
|
@@ -108,7 +111,11 @@ export const LANGUAGE_CAPABILITY_MATRIX = {
|
|
|
108
111
|
css: {
|
|
109
112
|
name: "CSS Processing",
|
|
110
113
|
capabilities: ["format", "lint"],
|
|
111
|
-
writeGroups: [
|
|
114
|
+
writeGroups: [
|
|
115
|
+
primary("css"),
|
|
116
|
+
{ mode: "all", runnerIds: ["tree-sitter"], filterKinds: ["css"] },
|
|
117
|
+
{ mode: "all", runnerIds: ["ast-grep-napi"], filterKinds: ["css"] },
|
|
118
|
+
],
|
|
112
119
|
},
|
|
113
120
|
yaml: {
|
|
114
121
|
name: "YAML Processing",
|
|
@@ -131,7 +138,10 @@ export const LANGUAGE_CAPABILITY_MATRIX = {
|
|
|
131
138
|
html: {
|
|
132
139
|
name: "HTML Linting",
|
|
133
140
|
capabilities: ["lint"],
|
|
134
|
-
writeGroups: [
|
|
141
|
+
writeGroups: [
|
|
142
|
+
primary("html"),
|
|
143
|
+
{ mode: "all", runnerIds: ["ast-grep-napi"], filterKinds: ["html"] },
|
|
144
|
+
],
|
|
135
145
|
},
|
|
136
146
|
docker: {
|
|
137
147
|
name: "Dockerfile Linting",
|
|
@@ -141,7 +151,10 @@ export const LANGUAGE_CAPABILITY_MATRIX = {
|
|
|
141
151
|
php: {
|
|
142
152
|
name: "PHP Linting",
|
|
143
153
|
capabilities: ["types", "lint"],
|
|
144
|
-
writeGroups: [
|
|
154
|
+
writeGroups: [
|
|
155
|
+
primary("php"),
|
|
156
|
+
{ mode: "all", runnerIds: ["tree-sitter"], filterKinds: ["php"] },
|
|
157
|
+
],
|
|
145
158
|
},
|
|
146
159
|
powershell: {
|
|
147
160
|
name: "PowerShell Linting",
|
|
@@ -156,7 +169,10 @@ export const LANGUAGE_CAPABILITY_MATRIX = {
|
|
|
156
169
|
csharp: {
|
|
157
170
|
name: "C# Linting",
|
|
158
171
|
capabilities: ["types", "lint"],
|
|
159
|
-
writeGroups: [
|
|
172
|
+
writeGroups: [
|
|
173
|
+
primary("csharp"),
|
|
174
|
+
{ mode: "all", runnerIds: ["tree-sitter"], filterKinds: ["csharp"] },
|
|
175
|
+
],
|
|
160
176
|
},
|
|
161
177
|
fsharp: {
|
|
162
178
|
name: "F# Linting",
|