code-auditor-mcp 5.0.2 → 5.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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +184 -3
- package/README.md +24 -2
- package/configs/hhra-compat.json +12 -118
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js +11 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +57 -52
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -1
- package/dist/analyzers/provenance.d.ts +9 -4
- package/dist/analyzers/provenance.d.ts.map +1 -1
- package/dist/analyzers/provenance.js +8 -25
- package/dist/analyzers/provenance.js.map +1 -1
- package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/reactAnalyzer.js +0 -5
- package/dist/analyzers/reactAnalyzer.js.map +1 -1
- package/dist/analyzers/ruleRegistry.js +4 -4
- package/dist/analyzers/ruleRegistry.js.map +1 -1
- package/dist/analyzers/ruleTiming.d.ts +1 -1
- package/dist/analyzers/ruleTiming.js +2 -2
- package/dist/analyzers/ruleTiming.js.map +1 -1
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +22 -26
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +32 -15
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +189 -115
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts +0 -2
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +0 -5
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +2 -10
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js +7 -49
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/schema/config.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/config.js +0 -2
- package/dist/analyzers/universal/schema/config.js.map +1 -1
- package/dist/analyzers/universal/schema/types.d.ts +0 -2
- package/dist/analyzers/universal/schema/types.d.ts.map +1 -1
- package/dist/analyzers/vendorSurfaces.d.ts +126 -0
- package/dist/analyzers/vendorSurfaces.d.ts.map +1 -0
- package/dist/analyzers/vendorSurfaces.js +580 -0
- package/dist/analyzers/vendorSurfaces.js.map +1 -0
- package/dist/auditRunner.d.ts.map +1 -1
- package/dist/auditRunner.js +136 -142
- package/dist/auditRunner.js.map +1 -1
- package/dist/churn/churnExtractor.d.ts +0 -1
- package/dist/churn/churnExtractor.d.ts.map +1 -1
- package/dist/churn/churnExtractor.js +2 -4
- package/dist/churn/churnExtractor.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +195 -26
- package/dist/cli.js.map +1 -1
- package/dist/codeIndex/migrations.d.ts.map +1 -1
- package/dist/codeIndex/migrations.js +4 -3
- package/dist/codeIndex/migrations.js.map +1 -1
- package/dist/codeIndexDB.d.ts +11 -2
- package/dist/codeIndexDB.d.ts.map +1 -1
- package/dist/codeIndexDB.js +27 -2
- package/dist/codeIndexDB.js.map +1 -1
- package/dist/config/configLoader.d.ts +60 -7
- package/dist/config/configLoader.d.ts.map +1 -1
- package/dist/config/configLoader.js +252 -60
- package/dist/config/configLoader.js.map +1 -1
- package/dist/config/defaults.d.ts +77 -18
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +105 -43
- package/dist/config/defaults.js.map +1 -1
- package/dist/constants.d.ts +2 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +4 -0
- package/dist/constants.js.map +1 -1
- package/dist/daemon/main.d.ts +0 -1
- package/dist/daemon/main.d.ts.map +1 -1
- package/dist/daemon/main.js +11 -4
- package/dist/daemon/main.js.map +1 -1
- package/dist/dataPaths.d.ts +17 -11
- package/dist/dataPaths.d.ts.map +1 -1
- package/dist/dataPaths.js +51 -16
- package/dist/dataPaths.js.map +1 -1
- package/dist/hookLog.d.ts +54 -0
- package/dist/hookLog.d.ts.map +1 -0
- package/dist/hookLog.js +105 -0
- package/dist/hookLog.js.map +1 -0
- package/dist/hookLogPath.generated.d.ts +3 -0
- package/dist/hookLogPath.generated.d.ts.map +1 -0
- package/dist/hookLogPath.generated.js +8 -0
- package/dist/hookLogPath.generated.js.map +1 -0
- package/dist/index.d.ts +0 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -7
- package/dist/index.js.map +1 -1
- package/dist/installConfig.d.ts +15 -2
- package/dist/installConfig.d.ts.map +1 -1
- package/dist/installConfig.js +17 -2
- package/dist/installConfig.js.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +18 -3
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
- package/dist/mcp-tools/workflowGuide.js +8 -8
- package/dist/mcp-tools/workflowGuide.js.map +1 -1
- package/dist/mcp-tools-shared.d.ts.map +1 -1
- package/dist/mcp-tools-shared.js +5 -2
- package/dist/mcp-tools-shared.js.map +1 -1
- package/dist/mcp-ui-simple.d.ts +5 -0
- package/dist/mcp-ui-simple.d.ts.map +1 -1
- package/dist/mcp-ui-simple.js +9 -2
- package/dist/mcp-ui-simple.js.map +1 -1
- package/dist/mcp.d.ts +0 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +36 -33
- package/dist/mcp.js.map +1 -1
- package/dist/mcpAuditJobs.d.ts +10 -0
- package/dist/mcpAuditJobs.d.ts.map +1 -1
- package/dist/mcpAuditJobs.js +10 -1
- package/dist/mcpAuditJobs.js.map +1 -1
- package/dist/mcpAutoIndex.d.ts.map +1 -1
- package/dist/mcpAutoIndex.js +2 -1
- package/dist/mcpAutoIndex.js.map +1 -1
- package/dist/phase/colorMath.d.ts +18 -5
- package/dist/phase/colorMath.d.ts.map +1 -1
- package/dist/phase/colorMath.js +88 -5
- package/dist/phase/colorMath.js.map +1 -1
- package/dist/phase/dataAccessCallsCandidates.js +1 -1
- package/dist/phase/dataAccessCallsCandidates.js.map +1 -1
- package/dist/phase/goFunctionAnalysis.d.ts +2 -2
- package/dist/phase/goFunctionAnalysis.js +2 -2
- package/dist/phase/goFunctions.d.ts +2 -2
- package/dist/phase/goFunctions.js +2 -2
- package/dist/phase/goImports.d.ts +2 -2
- package/dist/phase/goImports.js +2 -2
- package/dist/phase/goSwitches.d.ts +2 -2
- package/dist/phase/goSwitches.js +2 -2
- package/dist/phase/loopQueryCandidates.js +1 -1
- package/dist/phase/loopQueryCandidates.js.map +1 -1
- package/dist/phase/phaseModel.d.ts +37 -8
- package/dist/phase/phaseModel.d.ts.map +1 -1
- package/dist/phase/phaseModel.js +33 -8
- package/dist/phase/phaseModel.js.map +1 -1
- package/dist/phase/producers.d.ts.map +1 -1
- package/dist/phase/producers.js +24 -17
- package/dist/phase/producers.js.map +1 -1
- package/dist/phase/reachability.d.ts +44 -1
- package/dist/phase/reachability.d.ts.map +1 -1
- package/dist/phase/reachability.js +79 -0
- package/dist/phase/reachability.js.map +1 -1
- package/dist/phase/rules/crossDomain.d.ts.map +1 -1
- package/dist/phase/rules/crossDomain.js +16 -6
- package/dist/phase/rules/crossDomain.js.map +1 -1
- package/dist/phase/rules/dry.d.ts.map +1 -1
- package/dist/phase/rules/dry.js +0 -1
- package/dist/phase/rules/dry.js.map +1 -1
- package/dist/phase/rules/goRules.d.ts +2 -2
- package/dist/phase/rules/goRules.js +2 -2
- package/dist/phase/rules/secrets.d.ts +22 -8
- package/dist/phase/rules/secrets.d.ts.map +1 -1
- package/dist/phase/rules/secrets.js +48 -67
- package/dist/phase/rules/secrets.js.map +1 -1
- package/dist/phase/rules/styles.d.ts +23 -9
- package/dist/phase/rules/styles.d.ts.map +1 -1
- package/dist/phase/rules/styles.js +47 -15
- package/dist/phase/rules/styles.js.map +1 -1
- package/dist/phase/rules/unreferencedModule.d.ts.map +1 -1
- package/dist/phase/rules/unreferencedModule.js +16 -5
- package/dist/phase/rules/unreferencedModule.js.map +1 -1
- package/dist/phase/runner.d.ts +16 -7
- package/dist/phase/runner.d.ts.map +1 -1
- package/dist/phase/runner.js +32 -20
- package/dist/phase/runner.js.map +1 -1
- package/dist/phase/stylesCss.d.ts.map +1 -1
- package/dist/phase/stylesCss.js +4 -0
- package/dist/phase/stylesCss.js.map +1 -1
- package/dist/phase/stylesMarkup.d.ts.map +1 -1
- package/dist/phase/stylesMarkup.js +4 -0
- package/dist/phase/stylesMarkup.js.map +1 -1
- package/dist/phase/stylesSource.d.ts.map +1 -1
- package/dist/phase/stylesSource.js +6 -0
- package/dist/phase/stylesSource.js.map +1 -1
- package/dist/phase/testProgram.d.ts +57 -0
- package/dist/phase/testProgram.d.ts.map +1 -0
- package/dist/phase/testProgram.js +244 -0
- package/dist/phase/testProgram.js.map +1 -0
- package/dist/phase/typeDeclarations.d.ts +2 -2
- package/dist/phase/typeDeclarations.js +2 -2
- package/dist/phase/types.d.ts +46 -0
- package/dist/phase/types.d.ts.map +1 -1
- package/dist/pipelineAdapters.d.ts +1 -171
- package/dist/pipelineAdapters.d.ts.map +1 -1
- package/dist/pipelineAdapters.js +47 -1281
- package/dist/pipelineAdapters.js.map +1 -1
- package/dist/reporting/csvReportGenerator.d.ts.map +1 -1
- package/dist/reporting/csvReportGenerator.js +0 -6
- package/dist/reporting/csvReportGenerator.js.map +1 -1
- package/dist/reporting/htmlReportGenerator.d.ts.map +1 -1
- package/dist/reporting/htmlReportGenerator.js +0 -4
- package/dist/reporting/htmlReportGenerator.js.map +1 -1
- package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
- package/dist/reporting/jsonReportGenerator.js +0 -1
- package/dist/reporting/jsonReportGenerator.js.map +1 -1
- package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
- package/dist/reporting/sarifReportGenerator.js +0 -7
- package/dist/reporting/sarifReportGenerator.js.map +1 -1
- package/dist/sqlite/driver.d.ts +17 -1
- package/dist/sqlite/driver.d.ts.map +1 -1
- package/dist/sqlite/driver.js +28 -5
- package/dist/sqlite/driver.js.map +1 -1
- package/dist/styles/cssInJsHarvest.d.ts +36 -0
- package/dist/styles/cssInJsHarvest.d.ts.map +1 -0
- package/dist/styles/cssInJsHarvest.js +78 -0
- package/dist/styles/cssInJsHarvest.js.map +1 -0
- package/dist/styles/definedClasses.d.ts +95 -0
- package/dist/styles/definedClasses.d.ts.map +1 -0
- package/dist/styles/definedClasses.js +129 -0
- package/dist/styles/definedClasses.js.map +1 -0
- package/dist/styles/markupStyleHarvest.d.ts +33 -0
- package/dist/styles/markupStyleHarvest.d.ts.map +1 -0
- package/dist/styles/markupStyleHarvest.js +73 -0
- package/dist/styles/markupStyleHarvest.js.map +1 -0
- package/dist/styles/styleExtractor.d.ts +37 -0
- package/dist/styles/styleExtractor.d.ts.map +1 -1
- package/dist/styles/styleExtractor.js +89 -33
- package/dist/styles/styleExtractor.js.map +1 -1
- package/dist/styles/styleIndexer.d.ts.map +1 -1
- package/dist/styles/styleIndexer.js +11 -10
- package/dist/styles/styleIndexer.js.map +1 -1
- package/dist/styles/tailwindExpander.d.ts.map +1 -1
- package/dist/styles/tailwindExpander.js +31 -10
- package/dist/styles/tailwindExpander.js.map +1 -1
- package/dist/telemetry.d.ts +67 -1
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +100 -20
- package/dist/telemetry.js.map +1 -1
- package/dist/types/crossLanguage.d.ts +1 -0
- package/dist/types/crossLanguage.d.ts.map +1 -1
- package/dist/types.d.ts +23 -56
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -4
- package/dist/types.js.map +1 -1
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.js +1 -1
- package/examples/.auditrc.example.json +7 -22
- package/examples/.auditrc.json +12 -155
- package/examples/.code-auditor.json +8 -51
- package/examples/nextjs.auditrc.json +7 -34
- package/examples/node-api.auditrc.json +6 -46
- package/examples/react.auditrc.json +8 -25
- package/examples/tolerant-solid.auditrc.json +12 -44
- package/package.json +6 -5
- package/plugin/.claude-plugin/manifest.json +10 -9
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/hooks/hooks.json +4 -4
- package/plugin/scripts/hook-audit.sh +9 -0
- package/plugin/scripts/hook-common.sh +8 -2
- package/plugin/scripts/hook-drift-check.sh +10 -1
- package/plugin/scripts/hook-log-path.sh +7 -0
- package/plugin/scripts/hook-self-audit.sh +9 -0
- package/plugin/scripts/hook-warm.sh +17 -11
- package/plugin/skills/code-auditor/SKILL.md +26 -3
- package/dist/__tests__/fixtures/spec-20/scripts/tool.d.ts +0 -6
- package/dist/__tests__/fixtures/spec-20/scripts/tool.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-20/scripts/tool.js +0 -12
- package/dist/__tests__/fixtures/spec-20/scripts/tool.js.map +0 -1
- package/dist/__tests__/fixtures/spec-20/src/demo-module.d.ts +0 -7
- package/dist/__tests__/fixtures/spec-20/src/demo-module.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-20/src/demo-module.js +0 -13
- package/dist/__tests__/fixtures/spec-20/src/demo-module.js.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.d.ts +0 -14
- package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.js +0 -16
- package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.js.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.d.ts +0 -15
- package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.js +0 -17
- package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.js.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts +0 -14
- package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js +0 -19
- package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts +0 -18
- package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js +0 -19
- package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.d.ts +0 -19
- package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.js +0 -19
- package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.js.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts +0 -17
- package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.js +0 -19
- package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.js.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts +0 -25
- package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.js +0 -21
- package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.js.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.d.ts +0 -15
- package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.js +0 -19
- package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.js.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.d.ts +0 -14
- package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.js +0 -14
- package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.js.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts +0 -15
- package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.js +0 -16
- package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.js.map +0 -1
- package/dist/languages/go/analyzer-darwin-amd64 +0 -0
- package/dist/languages/go/analyzer-darwin-arm64 +0 -0
- package/dist/languages/go/analyzer-linux-amd64 +0 -0
- package/dist/languages/go/analyzer-linux-arm64 +0 -0
- package/dist/languages/go/analyzer-src/analyzer.go +0 -643
- package/dist/languages/go/analyzer-src/dataaccess.go +0 -388
- package/dist/languages/go/analyzer-src/go.mod +0 -3
- package/dist/languages/go/analyzer-src/indexer.go +0 -268
- package/dist/languages/go/analyzer-src/parser.go +0 -461
- package/dist/languages/go/analyzer-src/solid.go +0 -311
- package/dist/languages/go/analyzer-src/testconventions.go +0 -25
- package/dist/languages/go/analyzer-src/types.go +0 -156
- package/dist/languages/go/analyzer-windows-amd64.exe +0 -0
- package/dist/languages/go/go.mod +0 -7
- package/dist/languages/go/main.go +0 -203
- package/dist/native/ast-grep-napi.darwin-arm64.node +0 -0
- package/dist/native/ast-grep-napi.darwin-x64.node +0 -0
- package/dist/native/ast-grep-napi.linux-arm64-gnu.node +0 -0
- package/dist/native/ast-grep-napi.linux-arm64-musl.node +0 -0
- package/dist/native/ast-grep-napi.linux-x64-gnu.node +0 -0
- package/dist/native/ast-grep-napi.linux-x64-musl.node +0 -0
- package/dist/native/ast-grep-napi.win32-x64-msvc.node +0 -0
- package/dist/native-bootstrap.d.ts +0 -15
- package/dist/native-bootstrap.d.ts.map +0 -1
- package/dist/native-bootstrap.js +0 -75
- package/dist/native-bootstrap.js.map +0 -1
package/dist/auditRunner.js
CHANGED
|
@@ -35,12 +35,12 @@ import { writeAuditToLedger, detectRunInput } from './ledger.js';
|
|
|
35
35
|
import { runPipeline, makeVisitorStatus, getFilesProcessed, isVisitorStatus } from './pipeline.js';
|
|
36
36
|
import { getRuleTimingSortedDesc } from './analyzers/ruleTiming.js';
|
|
37
37
|
import { createFunctionIndexVisitor, createStylesCssVisitor, createStylesSourceVisitor, createStylesReducer, createInvariantsReducer, createSchemaCodeVisitor, } from './pipelineAdapters.js';
|
|
38
|
-
import { computeSizeDistributions } from './reporting/sizeDistribution.js';
|
|
39
|
-
import { splitRoutes, attributeRoutes, enabledMigratedRules } from './phase/routing.js';
|
|
40
38
|
import { runPhaseModel } from './phase/phaseModel.js';
|
|
39
|
+
import { resolveVendorSurfaces } from './analyzers/vendorSurfaces.js';
|
|
41
40
|
import { resolvePhaseThresholds } from './phase/config.js';
|
|
42
41
|
import { deriveCoverage, presentFormatsOf } from './phase/coverage.js';
|
|
43
42
|
import { MIGRATED_RULES, RULE_ANALYZER } from './phase/rules/registry.js';
|
|
43
|
+
import { computeReachabilityScores } from './phase/reachability.js';
|
|
44
44
|
import { seedDryPairs, resolveBlockConfig } from './phase/rules/dry.js';
|
|
45
45
|
import { checkUnresolvedReceiverImports, checkUnprovenQueryReceivers, checkUnresolvedQueries, checkUnparseableSql, dedupeCannotFireByReceiver } from './analyzers/universal/schema/codeAnalysis.js';
|
|
46
46
|
import { readProjectManifest, computeManifestStaleness } from './analyzers/manifestStaleness.js';
|
|
@@ -200,7 +200,7 @@ async function discoverAuditFiles(mergedOptions, root, fileAccounting) {
|
|
|
200
200
|
phase: 'discovery',
|
|
201
201
|
message: `Discovering files... (scope: ${scopeResultType})`
|
|
202
202
|
});
|
|
203
|
-
const { files, skippedExtensions } = await resolveDiscoveryScope(mergedOptions, scope, fileAccounting);
|
|
203
|
+
const { files, skippedExtensions, changedScopeNoBaseline } = await resolveDiscoveryScope(mergedOptions, scope, fileAccounting);
|
|
204
204
|
throwIfAborted(mergedOptions.abortSignal);
|
|
205
205
|
// Detect changed functions for non-all scopes
|
|
206
206
|
let changedFunctions;
|
|
@@ -228,6 +228,7 @@ async function discoverAuditFiles(mergedOptions, root, fileAccounting) {
|
|
|
228
228
|
scope,
|
|
229
229
|
isScoped,
|
|
230
230
|
scopeResultType,
|
|
231
|
+
changedScopeNoBaseline,
|
|
231
232
|
};
|
|
232
233
|
}
|
|
233
234
|
/**
|
|
@@ -238,6 +239,11 @@ async function discoverAuditFiles(mergedOptions, root, fileAccounting) {
|
|
|
238
239
|
*/
|
|
239
240
|
async function resolveDiscoveryScope(mergedOptions, scope, fileAccounting) {
|
|
240
241
|
let files;
|
|
242
|
+
// True when a bare `changed` scope found no baseline in the index to diff
|
|
243
|
+
// against (an empty `functions` table). That is "the diff cannot be computed",
|
|
244
|
+
// not "nothing changed" — surfaced as a diagnostic so the run never reports a
|
|
245
|
+
// false clean. Explicit-files and all/other scopes leave this false.
|
|
246
|
+
let changedScopeNoBaseline = false;
|
|
241
247
|
// Extensions present on disk that discovery skipped (Spec 43 R5 follow-up).
|
|
242
248
|
// Populated only for the `all` scope — scoped runs are explicitly scoped by
|
|
243
249
|
// the user, so "what wasn't analyzed" there is the scope itself, not the
|
|
@@ -269,12 +275,29 @@ async function resolveDiscoveryScope(mergedOptions, scope, fileAccounting) {
|
|
|
269
275
|
error: err instanceof Error ? err.message : String(err)
|
|
270
276
|
});
|
|
271
277
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
278
|
+
let modifiedFiles;
|
|
279
|
+
if (mergedOptions.explicitFiles !== undefined) {
|
|
280
|
+
modifiedFiles = mergedOptions.explicitFiles;
|
|
281
|
+
}
|
|
282
|
+
else if (db) {
|
|
283
|
+
// `undefined` here means *no baseline* (an empty `functions` table) — a
|
|
284
|
+
// different answer from `[]` ("baseline present, nothing modified"). The
|
|
285
|
+
// former is reported as an uncomputable diff, never as a clean run. The
|
|
286
|
+
// `db`-unavailable case (init threw, e.g. a future schema version) keeps
|
|
287
|
+
// its existing fail-open zero-file path and the later engine-error
|
|
288
|
+
// diagnostic.
|
|
289
|
+
const detected = await db.detectModifiedFiles(path.resolve(mergedOptions.projectRoot || process.cwd()));
|
|
290
|
+
if (detected === undefined)
|
|
291
|
+
changedScopeNoBaseline = true;
|
|
292
|
+
modifiedFiles = detected ?? [];
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
modifiedFiles = [];
|
|
296
|
+
}
|
|
275
297
|
files = [...new Set(modifiedFiles.map((f) => path.resolve(f)))].sort();
|
|
276
298
|
logMcpInfo('discovery', 'changed scope resolved', {
|
|
277
|
-
fileCount: files.length
|
|
299
|
+
fileCount: files.length,
|
|
300
|
+
noBaseline: changedScopeNoBaseline
|
|
278
301
|
});
|
|
279
302
|
}
|
|
280
303
|
else if (Array.isArray(scope)) {
|
|
@@ -290,7 +313,7 @@ async function resolveDiscoveryScope(mergedOptions, scope, fileAccounting) {
|
|
|
290
313
|
files = discovered.files;
|
|
291
314
|
skippedExtensions = discovered.skippedExtensions;
|
|
292
315
|
}
|
|
293
|
-
return { files, skippedExtensions };
|
|
316
|
+
return { files, skippedExtensions, changedScopeNoBaseline };
|
|
294
317
|
}
|
|
295
318
|
/**
|
|
296
319
|
* Detect changed functions for a scoped run via the code index (Spec 60.1).
|
|
@@ -527,8 +550,6 @@ function buildPipelineAdapters(analyzers) {
|
|
|
527
550
|
const pipelineVisitors = [];
|
|
528
551
|
const pipelineReducers = [];
|
|
529
552
|
const pipelineDerivedReducers = [];
|
|
530
|
-
let reactBundle;
|
|
531
|
-
let solidBundle;
|
|
532
553
|
// Always-on infrastructure: function-index visitor populates the
|
|
533
554
|
// `functions` table so conventions + cross-domain reducers have data
|
|
534
555
|
// even on a cold run with no prior index sync.
|
|
@@ -567,8 +588,6 @@ function buildPipelineAdapters(analyzers) {
|
|
|
567
588
|
pipelineVisitors,
|
|
568
589
|
pipelineReducers,
|
|
569
590
|
pipelineDerivedReducers,
|
|
570
|
-
reactBundle,
|
|
571
|
-
solidBundle,
|
|
572
591
|
};
|
|
573
592
|
}
|
|
574
593
|
/**
|
|
@@ -707,19 +726,18 @@ function buildPipelineConfig(inputs) {
|
|
|
707
726
|
};
|
|
708
727
|
}
|
|
709
728
|
/**
|
|
710
|
-
* Run the
|
|
711
|
-
*
|
|
712
|
-
*
|
|
729
|
+
* Run the phase model — the single analysis path (Spec 68 §15) — and re-emit its
|
|
730
|
+
* findings into the correct analyzer buckets. The legacy analyzer path is
|
|
731
|
+
* deleted; there is no route split to choose between. Mutates `analyzerResults`
|
|
732
|
+
* and `pipelineAnalyzerConfig` (dry divergence).
|
|
713
733
|
*/
|
|
714
|
-
async function
|
|
734
|
+
async function runPhaseModelStage(inputs) {
|
|
715
735
|
const { mergedOptions, root, files, isScoped, pipelineAnalyzerConfig, analyzerResults, pipelineIndexHandle } = inputs;
|
|
716
736
|
let phaseFindings = [];
|
|
717
737
|
let phaseIncompleteFacts = new Map();
|
|
718
|
-
let
|
|
738
|
+
let phaseReachability;
|
|
719
739
|
let receiverDiagnostics = [];
|
|
720
|
-
|
|
721
|
-
routeAttribution = Object.fromEntries(attributeRoutes());
|
|
722
|
-
if (migrated.size > 0) {
|
|
740
|
+
if (MIGRATED_RULES.length > 0) {
|
|
723
741
|
// The `dry/diverging-clone` rule reads its knobs from `dry.divergence`.
|
|
724
742
|
const topLevelDivergence = mergedOptions.divergence;
|
|
725
743
|
if (topLevelDivergence && !pipelineAnalyzerConfig['dry']?.divergence) {
|
|
@@ -758,12 +776,17 @@ async function applyPhaseModelSplit(inputs) {
|
|
|
758
776
|
tsconfigAliases: infraConfig.tsconfigAliases,
|
|
759
777
|
packageEntryPoints: infraConfig.packageEntryPoints,
|
|
760
778
|
indexHandle: pipelineIndexHandle,
|
|
761
|
-
enabledRules:
|
|
779
|
+
enabledRules: new Set(MIGRATED_RULES.map((r) => r.id)),
|
|
762
780
|
externalTables,
|
|
763
781
|
workerCount: resolveWorkerCount(),
|
|
764
782
|
sqlDialect: infraConfig.sqlDialect,
|
|
765
783
|
sqlDialectReason: infraConfig.sqlDialectReason,
|
|
766
784
|
declaredTypePackages: infraConfig.declaredTypePackages,
|
|
785
|
+
// Item B (reissued) — resolve each vendor's declaration from the tree under
|
|
786
|
+
// analysis (its own node_modules / workspace path) once per run. The
|
|
787
|
+
// loop-query / data-access producers read these resolved surfaces; nothing
|
|
788
|
+
// about a vendor's API is committed and nothing is fetched at analysis time.
|
|
789
|
+
vendorSurfaces: resolveVendorSurfaces(root),
|
|
767
790
|
// Spec 70 2b — the diverging-clone write: seed `dry_pair_history` from the
|
|
768
791
|
// phase `code-block` fact (replacing `createDryVisitor`/`persistDryPairs`).
|
|
769
792
|
persistDryPairHistory: (facts) => {
|
|
@@ -774,6 +797,11 @@ async function applyPhaseModelSplit(inputs) {
|
|
|
774
797
|
});
|
|
775
798
|
phaseFindings = phaseResult.findings;
|
|
776
799
|
phaseIncompleteFacts = phaseResult.incompleteFacts;
|
|
800
|
+
const reachabilityFact = phaseResult.facts.get('reachability');
|
|
801
|
+
const fileImports = phaseResult.facts.get('file-imports');
|
|
802
|
+
phaseReachability = reachabilityFact && fileImports
|
|
803
|
+
? computeReachabilityScores(fileImports, reachabilityFact)
|
|
804
|
+
: undefined;
|
|
777
805
|
// Spec 69 §10 + Part 2b — re-home the pre-pass's diagnostics (deleted from
|
|
778
806
|
// `pipeline.ts`) on the phase result. Cannot-fire: re-derive per file from
|
|
779
807
|
// `phaseResult.unprovenQueryReceivers` + `phaseResult.unresolvedImports`, keyed
|
|
@@ -781,12 +809,34 @@ async function applyPhaseModelSplit(inputs) {
|
|
|
781
809
|
// receivers/imports never reached it). Manifest-stale: the ecosystem-list
|
|
782
810
|
// self-check, unchanged from the deleted `buildPipelineResult` loop.
|
|
783
811
|
receiverDiagnostics = await buildReceiverDiagnostics(phaseResult.unprovenQueryReceivers, phaseResult.unresolvedImports, phaseResult.unresolvedQuerySites, phaseResult.unparseableSql, root);
|
|
784
|
-
//
|
|
785
|
-
|
|
812
|
+
// Spec 68 §8 — `unreferenced-module`'s unproven modules: a module with no
|
|
813
|
+
// resolved importer while the graph carries a computed-specifier dynamic
|
|
814
|
+
// import. The rule withholds the dead-code finding for these; this surfaces
|
|
815
|
+
// the disposition (with its reason) as a coverage diagnostic instead.
|
|
816
|
+
for (const m of phaseResult.unprovenModules) {
|
|
817
|
+
receiverDiagnostics.push({
|
|
818
|
+
analyzerName: 'dependency-graph',
|
|
819
|
+
kind: 'unresolved-dynamic-import',
|
|
820
|
+
message: m.reason,
|
|
821
|
+
file: m.file,
|
|
822
|
+
line: m.line,
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
// §15 — a rule that could not run (fail-open, missing dependency) reported
|
|
826
|
+
// itself on the phase result's diagnostic channel. Merge those alongside the
|
|
827
|
+
// receiver-diagnostics the pre-pass re-derived, so the styles
|
|
828
|
+
// `undefined-class-disabled` diagnostic (re-homed from the deleted
|
|
829
|
+
// `initTailwindProbe`) reaches the same off-ladder surface.
|
|
830
|
+
receiverDiagnostics.push(...phaseResult.diagnostics);
|
|
831
|
+
// Strip the migrated rules' legacy emission from every analyzer result —
|
|
832
|
+
// the phase model is their single source of truth now (§15), and the legacy
|
|
833
|
+
// factories that keep running for their infra side-effect (styles reducer,
|
|
834
|
+
// schema-code visitor) still emit their migrated findings alongside. This is
|
|
835
|
+
// a direct projection of MIGRATED_RULES, not a route split: there is no
|
|
836
|
+
// "phase vs legacy" choice anymore, only discarding a stale duplicate.
|
|
837
|
+
const migratedRules = new Set(MIGRATED_RULES.map((r) => r.id));
|
|
786
838
|
for (const result of Object.values(analyzerResults)) {
|
|
787
|
-
|
|
788
|
-
result.violations = result.violations.filter((v) => !migrated.has(v.rule));
|
|
789
|
-
strippedCount += before - result.violations.length;
|
|
839
|
+
result.violations = result.violations.filter((v) => !migratedRules.has(v.rule));
|
|
790
840
|
}
|
|
791
841
|
// Re-emit phase findings as violations, bucketed by the rule's analyzer.
|
|
792
842
|
const byAnalyzer = new Map();
|
|
@@ -836,13 +886,8 @@ async function applyPhaseModelSplit(inputs) {
|
|
|
836
886
|
analyzerResults[analyzer] = makeEmptyAnalyzerResult(analyzer, phaseFiles.length);
|
|
837
887
|
}
|
|
838
888
|
}
|
|
839
|
-
logMcpInfo('analysis', 'phase model (both paths)', {
|
|
840
|
-
migrated: migrated.size,
|
|
841
|
-
stripped: strippedCount,
|
|
842
|
-
reemitted: phaseFindings.length,
|
|
843
|
-
});
|
|
844
889
|
}
|
|
845
|
-
return { phaseFindings, phaseIncompleteFacts,
|
|
890
|
+
return { phaseFindings, phaseIncompleteFacts, phaseReachability, receiverDiagnostics };
|
|
846
891
|
}
|
|
847
892
|
/**
|
|
848
893
|
* Spec 69 §10 + Part 2b — re-derive the pre-pass's coverage diagnostics from the
|
|
@@ -962,29 +1007,6 @@ export async function persistDryPairs(dryPairs, root) {
|
|
|
962
1007
|
// dry_pair_history persistence is advisory — non-fatal
|
|
963
1008
|
}
|
|
964
1009
|
}
|
|
965
|
-
/**
|
|
966
|
-
* Run the React analyzer's cross-component finalization (require-error-boundary
|
|
967
|
-
* / raw-element checks), appending extra violations into the `react` result.
|
|
968
|
-
*/
|
|
969
|
-
async function finalizeReact(reactBundle, analyzerResults, pipelineAnalyzerConfig) {
|
|
970
|
-
if (reactBundle) {
|
|
971
|
-
try {
|
|
972
|
-
const reactResult = analyzerResults['react'];
|
|
973
|
-
if (reactResult) {
|
|
974
|
-
// Pass the react namespace (not the whole namespaced config) so
|
|
975
|
-
// finalize reads `requireErrorBoundaries`/`rawElementCheck` at the
|
|
976
|
-
// same level the visitor's `context.config` does.
|
|
977
|
-
const extraViolations = await reactBundle.finalizeCrossComponent(pipelineAnalyzerConfig['react'] ?? {});
|
|
978
|
-
reactResult.violations.push(...extraViolations);
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
catch (err) {
|
|
982
|
-
logMcpInfo('analysis', 'react finalization failed (non-fatal)', {
|
|
983
|
-
error: err instanceof Error ? err.message : String(err)
|
|
984
|
-
});
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
}
|
|
988
1010
|
/**
|
|
989
1011
|
* Build the pipeline, run it, and fold its outputs — plus the both-paths phase
|
|
990
1012
|
* split and derived coverage — into a single result bundle for `run`.
|
|
@@ -995,7 +1017,7 @@ async function runPipelineStage(inputs) {
|
|
|
995
1017
|
let indexFactsWritten = 0;
|
|
996
1018
|
let writeIndexFactsMs = 0;
|
|
997
1019
|
const { auditIndex, pipelineIndexHandle, indexSkipDiagnostic } = await initializeAuditIndex(root);
|
|
998
|
-
const { pipelineVisitors, pipelineReducers, pipelineDerivedReducers,
|
|
1020
|
+
const { pipelineVisitors, pipelineReducers, pipelineDerivedReducers, } = buildPipelineAdapters(analyzers);
|
|
999
1021
|
// ── Safeguard warnings ────────────────────────────────────────────────
|
|
1000
1022
|
if (auditIndex && analyzers.includes('cross-domain')) {
|
|
1001
1023
|
const suCount = auditIndex.rawSql.count('schema_usage');
|
|
@@ -1011,6 +1033,7 @@ async function runPipelineStage(inputs) {
|
|
|
1011
1033
|
// Outputs hoisted so derived coverage (after the split) can read them.
|
|
1012
1034
|
let phaseFindings = [];
|
|
1013
1035
|
let phaseIncompleteFacts = new Map();
|
|
1036
|
+
let phaseReachability;
|
|
1014
1037
|
let pipelineCoverage;
|
|
1015
1038
|
let pipelineTableCatalog;
|
|
1016
1039
|
let pipelineStageTiming;
|
|
@@ -1023,7 +1046,6 @@ async function runPipelineStage(inputs) {
|
|
|
1023
1046
|
let pipelineTestCoverage;
|
|
1024
1047
|
let pipelineDeadClusters;
|
|
1025
1048
|
let pipelineSizeDistributions;
|
|
1026
|
-
let routeAttribution;
|
|
1027
1049
|
let receiverDiagnostics = [];
|
|
1028
1050
|
if (hasPipelineAnalyzers) {
|
|
1029
1051
|
const pipelineAnalyzerConfig = buildPipelineAnalyzerConfig(analyzers, mergedOptions, root, files, corpusFiles, provenanceTiming);
|
|
@@ -1052,13 +1074,12 @@ async function runPipelineStage(inputs) {
|
|
|
1052
1074
|
status: ar.status?.status,
|
|
1053
1075
|
});
|
|
1054
1076
|
}
|
|
1055
|
-
await
|
|
1056
|
-
const split = await applyPhaseModelSplit({
|
|
1077
|
+
const split = await runPhaseModelStage({
|
|
1057
1078
|
mergedOptions, root, files, isScoped, pipelineAnalyzerConfig, analyzerResults, pipelineIndexHandle,
|
|
1058
1079
|
});
|
|
1059
1080
|
phaseFindings = split.phaseFindings;
|
|
1060
1081
|
phaseIncompleteFacts = split.phaseIncompleteFacts;
|
|
1061
|
-
|
|
1082
|
+
phaseReachability = split.phaseReachability;
|
|
1062
1083
|
receiverDiagnostics = split.receiverDiagnostics;
|
|
1063
1084
|
// Spec 68 §8 — derived coverage, computed AFTER the split so phaseFindings
|
|
1064
1085
|
// holds the migrated rules' re-emitted findings.
|
|
@@ -1066,7 +1087,7 @@ async function runPipelineStage(inputs) {
|
|
|
1066
1087
|
rules: MIGRATED_RULES,
|
|
1067
1088
|
findings: phaseFindings,
|
|
1068
1089
|
presentFormats: presentFormatsOf(files),
|
|
1069
|
-
enabledRules:
|
|
1090
|
+
enabledRules: new Set(MIGRATED_RULES.map((r) => r.id)),
|
|
1070
1091
|
incompleteFacts: phaseIncompleteFacts,
|
|
1071
1092
|
groupOf: (ruleId) => RULE_ANALYZER.get(ruleId) ?? ruleId,
|
|
1072
1093
|
});
|
|
@@ -1088,11 +1109,6 @@ async function runPipelineStage(inputs) {
|
|
|
1088
1109
|
];
|
|
1089
1110
|
pipelineTestCoverage = pipelineResult.metadata?.testCoverage;
|
|
1090
1111
|
pipelineDeadClusters = pipelineResult.metadata?.deadClusters;
|
|
1091
|
-
// Spec 60 R2 — aggregate the SOLID analyzer's raw size readings.
|
|
1092
|
-
if (solidBundle) {
|
|
1093
|
-
const samples = await solidBundle.getSizeSamples();
|
|
1094
|
-
pipelineSizeDistributions = computeSizeDistributions(samples);
|
|
1095
|
-
}
|
|
1096
1112
|
}
|
|
1097
1113
|
catch (error) {
|
|
1098
1114
|
if (error instanceof AuditAbortedError) {
|
|
@@ -1138,6 +1154,7 @@ async function runPipelineStage(inputs) {
|
|
|
1138
1154
|
analyzerResults,
|
|
1139
1155
|
phaseFindings,
|
|
1140
1156
|
phaseIncompleteFacts,
|
|
1157
|
+
phaseReachability,
|
|
1141
1158
|
pipelineCoverage,
|
|
1142
1159
|
pipelineTableCatalog,
|
|
1143
1160
|
pipelineStageTiming,
|
|
@@ -1150,7 +1167,6 @@ async function runPipelineStage(inputs) {
|
|
|
1150
1167
|
pipelineTestCoverage,
|
|
1151
1168
|
pipelineDeadClusters,
|
|
1152
1169
|
pipelineSizeDistributions,
|
|
1153
|
-
routeAttribution,
|
|
1154
1170
|
indexFactsWritten,
|
|
1155
1171
|
writeIndexFactsMs,
|
|
1156
1172
|
};
|
|
@@ -1254,75 +1270,40 @@ function stampAnalyzerFields(ordered) {
|
|
|
1254
1270
|
}
|
|
1255
1271
|
}
|
|
1256
1272
|
/**
|
|
1257
|
-
* Spec 13 R2 — attach
|
|
1258
|
-
*
|
|
1259
|
-
* order as tiebreaker
|
|
1260
|
-
*
|
|
1273
|
+
* Spec 13 R2 — attach a reachability score to every violation and reorder within
|
|
1274
|
+
* severity tiers (live code first: entry point → imported → dead), original
|
|
1275
|
+
* order as tiebreaker. Derived from the phase `reachability` fact (reverse
|
|
1276
|
+
* import adjacency + package entry points); no DB read. Advisory — a missing
|
|
1277
|
+
* fact (a run where the phase model produced no reachability) leaves every
|
|
1278
|
+
* finding at the neutral 0.5 and preserves original order.
|
|
1261
1279
|
*/
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
const
|
|
1271
|
-
for (const
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
}
|
|
1276
|
-
}
|
|
1277
|
-
const reachabilityByFile = new Map();
|
|
1278
|
-
const reachRows = indexDb.rawDb
|
|
1279
|
-
.prepare("SELECT node_key, weight FROM graph_cache WHERE graph_type = 'reachability'")
|
|
1280
|
-
.all();
|
|
1281
|
-
for (const row of reachRows) {
|
|
1282
|
-
reachabilityByFile.set(row.node_key, row.weight);
|
|
1283
|
-
}
|
|
1284
|
-
for (const analyzerName of Object.keys(ordered)) {
|
|
1285
|
-
const result = ordered[analyzerName];
|
|
1286
|
-
const violations = result.violations;
|
|
1287
|
-
for (const v of violations) {
|
|
1288
|
-
const funcName = v.symbol;
|
|
1289
|
-
const file = v.file;
|
|
1290
|
-
// Try function-level hotspot first: "filePath::symbol"
|
|
1291
|
-
const funcTarget = funcName ? `${file}::${funcName}` : undefined;
|
|
1292
|
-
v.hotspot = (funcTarget ? hotspotByTarget.get(funcTarget) : undefined)
|
|
1293
|
-
?? hotspotByFile.get(file)
|
|
1294
|
-
?? 0;
|
|
1295
|
-
// Files with no cross-language entities (no reachability data) get a
|
|
1296
|
-
// neutral 0.5 so we never demote a finding we know nothing about.
|
|
1297
|
-
v.reachability = reachabilityByFile.get(file) ?? 0.5;
|
|
1298
|
-
}
|
|
1299
|
-
const severityOrder = {
|
|
1300
|
-
critical: 0,
|
|
1301
|
-
severe: 1,
|
|
1302
|
-
high: 2,
|
|
1303
|
-
info: 3,
|
|
1304
|
-
};
|
|
1305
|
-
const indexed = violations.map((v, i) => ({ v, i }));
|
|
1306
|
-
indexed.sort((a, b) => {
|
|
1307
|
-
const sevA = severityOrder[a.v.severity] ?? 99;
|
|
1308
|
-
const sevB = severityOrder[b.v.severity] ?? 99;
|
|
1309
|
-
if (sevA !== sevB)
|
|
1310
|
-
return sevA - sevB;
|
|
1311
|
-
const reachA = a.v.reachability ?? 0.5;
|
|
1312
|
-
const reachB = b.v.reachability ?? 0.5;
|
|
1313
|
-
if (reachA !== reachB)
|
|
1314
|
-
return reachB - reachA;
|
|
1315
|
-
const hsA = a.v.hotspot ?? 0;
|
|
1316
|
-
const hsB = b.v.hotspot ?? 0;
|
|
1317
|
-
if (hsA !== hsB)
|
|
1318
|
-
return hsB - hsA;
|
|
1319
|
-
return a.i - b.i;
|
|
1320
|
-
});
|
|
1321
|
-
result.violations = indexed.map((x) => x.v);
|
|
1280
|
+
function applyReachabilityRanking(ordered, reachabilityByFile) {
|
|
1281
|
+
const severityOrder = {
|
|
1282
|
+
critical: 0,
|
|
1283
|
+
severe: 1,
|
|
1284
|
+
high: 2,
|
|
1285
|
+
info: 3,
|
|
1286
|
+
};
|
|
1287
|
+
for (const result of Object.values(ordered)) {
|
|
1288
|
+
const violations = result.violations;
|
|
1289
|
+
for (const v of violations) {
|
|
1290
|
+
// A file absent from the corpus graph gets a neutral 0.5 so we never
|
|
1291
|
+
// demote a finding we know nothing about.
|
|
1292
|
+
v.reachability = reachabilityByFile?.get(v.file) ?? 0.5;
|
|
1322
1293
|
}
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1294
|
+
const indexed = violations.map((v, i) => ({ v, i }));
|
|
1295
|
+
indexed.sort((a, b) => {
|
|
1296
|
+
const sevA = severityOrder[a.v.severity] ?? 99;
|
|
1297
|
+
const sevB = severityOrder[b.v.severity] ?? 99;
|
|
1298
|
+
if (sevA !== sevB)
|
|
1299
|
+
return sevA - sevB;
|
|
1300
|
+
const reachA = a.v.reachability ?? 0.5;
|
|
1301
|
+
const reachB = b.v.reachability ?? 0.5;
|
|
1302
|
+
if (reachA !== reachB)
|
|
1303
|
+
return reachB - reachA;
|
|
1304
|
+
return a.i - b.i;
|
|
1305
|
+
});
|
|
1306
|
+
result.violations = indexed.map((x) => x.v);
|
|
1326
1307
|
}
|
|
1327
1308
|
}
|
|
1328
1309
|
/**
|
|
@@ -1369,7 +1350,7 @@ function classifyBaselineFindings(ordered, mergedOptions, files, isScoped) {
|
|
|
1369
1350
|
* Spec 60 size/test/distribution reports), and hook-contract-clean violations.
|
|
1370
1351
|
*/
|
|
1371
1352
|
function buildAuditResult(inputs) {
|
|
1372
|
-
const { ordered, files, analyzers, mergedOptions, isScoped, scopeResultType, indexFactsWritten, writeIndexFactsMs, startTime, startCpu, provenanceTiming, blastRadius, zeroFilesDiagnostics, pipelineDiagnostics, rejected, lintConfigDiagnostics, configPath, baselineMetadata, pipelineCoverage,
|
|
1353
|
+
const { ordered, files, analyzers, mergedOptions, isScoped, scopeResultType, indexFactsWritten, writeIndexFactsMs, startTime, startCpu, provenanceTiming, blastRadius, changedScopeNoBaseline, zeroFilesDiagnostics, pipelineDiagnostics, rejected, lintConfigDiagnostics, configPath, baselineMetadata, pipelineCoverage, pipelineTableCatalog, pipelineStageTiming, thresholdChanges, thresholdSources, pipelineSkippedFiles, pipelineUnparsedFiles, skippedExtensions, pipelineInputPresence, pipelineRuleTiming, pipelineFileAccounting, pipelineTestCoverage, pipelineDeadClusters, pipelineSizeDistributions, collectedFunctions, fileToFunctionsMap, } = inputs;
|
|
1373
1354
|
// Spec 69 §10 R4 — count query-shaped call sites whose DB receiver is
|
|
1374
1355
|
// unproven, keyed by the analyzer that owns the receiver-resolution
|
|
1375
1356
|
// instrument (`schema`). This is the site-level `cannot-fire` surface that
|
|
@@ -1380,6 +1361,19 @@ function buildAuditResult(inputs) {
|
|
|
1380
1361
|
continue;
|
|
1381
1362
|
unprovenSitesByAnalyzer.set(d.analyzerName, (unprovenSitesByAnalyzer.get(d.analyzerName) ?? 0) + 1);
|
|
1382
1363
|
}
|
|
1364
|
+
// A bare `changed` scope that found no baseline to diff against (an empty
|
|
1365
|
+
// `functions` table) is reported as an uncomputable diff, never as a clean
|
|
1366
|
+
// run. The diagnostic names both possible causes: the index was never
|
|
1367
|
+
// populated, or it was discarded (e.g. after corruption).
|
|
1368
|
+
const noBaselineDiagnostics = changedScopeNoBaseline
|
|
1369
|
+
? [{
|
|
1370
|
+
analyzerName: 'code-index',
|
|
1371
|
+
kind: 'no-baseline',
|
|
1372
|
+
message: 'Cannot determine what changed: the code index holds no baseline to diff against ' +
|
|
1373
|
+
'(no previously indexed files). No index has been built yet, or a prior index was ' +
|
|
1374
|
+
'discarded. Run a full audit to build the baseline, then re-run `changed`.',
|
|
1375
|
+
}]
|
|
1376
|
+
: [];
|
|
1383
1377
|
const summary = generateSummary(ordered, files.length, unprovenSitesByAnalyzer);
|
|
1384
1378
|
return {
|
|
1385
1379
|
timestamp: new Date(),
|
|
@@ -1397,8 +1391,9 @@ function buildAuditResult(inputs) {
|
|
|
1397
1391
|
scope: scopeResultType,
|
|
1398
1392
|
provenanceResolutionMs: provenanceTiming.totalMs,
|
|
1399
1393
|
...(blastRadius && { blastRadius }),
|
|
1400
|
-
...((zeroFilesDiagnostics.length > 0 || (pipelineDiagnostics?.length ?? 0) > 0 || rejected.length > 0 || lintConfigDiagnostics.length > 0) && {
|
|
1394
|
+
...((noBaselineDiagnostics.length > 0 || zeroFilesDiagnostics.length > 0 || (pipelineDiagnostics?.length ?? 0) > 0 || rejected.length > 0 || lintConfigDiagnostics.length > 0) && {
|
|
1401
1395
|
diagnostics: [
|
|
1396
|
+
...noBaselineDiagnostics,
|
|
1402
1397
|
...zeroFilesDiagnostics,
|
|
1403
1398
|
...(pipelineDiagnostics ?? []),
|
|
1404
1399
|
...lintConfigDiagnostics,
|
|
@@ -1413,7 +1408,6 @@ function buildAuditResult(inputs) {
|
|
|
1413
1408
|
}),
|
|
1414
1409
|
...(baselineMetadata && { baseline: baselineMetadata }),
|
|
1415
1410
|
...(pipelineCoverage && { coverage: pipelineCoverage }),
|
|
1416
|
-
...(routeAttribution && { routeAttribution }),
|
|
1417
1411
|
...(pipelineTableCatalog && { tableCatalog: pipelineTableCatalog }),
|
|
1418
1412
|
...(pipelineStageTiming && { stageTiming: pipelineStageTiming }),
|
|
1419
1413
|
...(thresholdChanges.length > 0 && { thresholdChanges }),
|
|
@@ -1492,7 +1486,7 @@ export function createAuditRunner(options = {}) {
|
|
|
1492
1486
|
// discovery (full `all` scope) and the pipeline (stage 1/2) so every touched
|
|
1493
1487
|
// file lands in exactly one terminal state and the balance can be asserted.
|
|
1494
1488
|
const fileAccounting = new FileAccounting();
|
|
1495
|
-
const { files, changedFunctions, skippedExtensions, blastRadius, corpusFiles, scope, isScoped, scopeResultType, } = await discoverAuditFiles(mergedOptions, root, fileAccounting);
|
|
1489
|
+
const { files, changedFunctions, skippedExtensions, blastRadius, corpusFiles, scope, isScoped, scopeResultType, changedScopeNoBaseline, } = await discoverAuditFiles(mergedOptions, root, fileAccounting);
|
|
1496
1490
|
const { collectedFunctions, fileToFunctionsMap } = await collectIndexedFunctions(mergedOptions, files);
|
|
1497
1491
|
// Run analyzers
|
|
1498
1492
|
// Spec 68 §15 — no selection model: the full pipeline always runs. `indexOnly`
|
|
@@ -1510,7 +1504,7 @@ export function createAuditRunner(options = {}) {
|
|
|
1510
1504
|
mergedOptions, root, files, corpusFiles, isScoped, fileAccounting,
|
|
1511
1505
|
styleConsumedFiles, styleContributingFiles, analyzers, provenanceTiming,
|
|
1512
1506
|
});
|
|
1513
|
-
const { analyzerResults, phaseFindings, phaseIncompleteFacts, pipelineCoverage, pipelineTableCatalog, pipelineStageTiming, pipelineSkippedFiles, pipelineUnparsedFiles, pipelineInputPresence, pipelineRuleTiming, pipelineFileAccounting, pipelineDiagnostics, pipelineTestCoverage, pipelineDeadClusters, pipelineSizeDistributions,
|
|
1507
|
+
const { analyzerResults, phaseFindings, phaseIncompleteFacts, phaseReachability, pipelineCoverage, pipelineTableCatalog, pipelineStageTiming, pipelineSkippedFiles, pipelineUnparsedFiles, pipelineInputPresence, pipelineRuleTiming, pipelineFileAccounting, pipelineDiagnostics, pipelineTestCoverage, pipelineDeadClusters, pipelineSizeDistributions, indexFactsWritten, writeIndexFactsMs, } = pipeline;
|
|
1514
1508
|
// ── Zero-files diagnostic ─────────────────────────────────────────────
|
|
1515
1509
|
// Extracted to runZeroFilesDiagnostics() for testability. Runs against the
|
|
1516
1510
|
// raw analyzerResults before truthiness filtering so the "no result" pass
|
|
@@ -1526,8 +1520,8 @@ export function createAuditRunner(options = {}) {
|
|
|
1526
1520
|
// Normalize the analyzer field on every violation to its result key (the
|
|
1527
1521
|
// single source of truth — see stampAnalyzerFields).
|
|
1528
1522
|
stampAnalyzerFields(orderedAnalyzerResults);
|
|
1529
|
-
// Spec 13 R2 —
|
|
1530
|
-
|
|
1523
|
+
// Spec 13 R2 — reachability scoring & finding reordering (advisory).
|
|
1524
|
+
applyReachabilityRanking(orderedAnalyzerResults, phaseReachability);
|
|
1531
1525
|
// Hook-contract guard: no violation may carry an empty file path, line 0, or
|
|
1532
1526
|
// missing line. A sentinel violation with file:'' broke the Claude Code hook's
|
|
1533
1527
|
// JSON consumer (Spec 15 regression); line:0 from CrossDomainAnalyzer broke it
|
|
@@ -1552,6 +1546,7 @@ export function createAuditRunner(options = {}) {
|
|
|
1552
1546
|
startCpu,
|
|
1553
1547
|
provenanceTiming,
|
|
1554
1548
|
blastRadius,
|
|
1549
|
+
changedScopeNoBaseline,
|
|
1555
1550
|
zeroFilesDiagnostics,
|
|
1556
1551
|
pipelineDiagnostics,
|
|
1557
1552
|
rejected,
|
|
@@ -1559,7 +1554,6 @@ export function createAuditRunner(options = {}) {
|
|
|
1559
1554
|
configPath,
|
|
1560
1555
|
baselineMetadata,
|
|
1561
1556
|
pipelineCoverage,
|
|
1562
|
-
routeAttribution,
|
|
1563
1557
|
pipelineTableCatalog,
|
|
1564
1558
|
pipelineStageTiming,
|
|
1565
1559
|
thresholdChanges,
|