code-standards 7.20.3__tar.gz → 7.22.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. {code_standards-7.20.3 → code_standards-7.22.0}/PKG-INFO +4 -4
  2. {code_standards-7.20.3 → code_standards-7.22.0}/pyproject.toml +4 -4
  3. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/api.py +187 -120
  4. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/cli/main.py +963 -675
  5. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/cli-reference.v1.json +1 -1
  6. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/eslint.peers.json +2 -2
  7. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/eslint.strict.mjs +3 -0
  8. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/rule-calibration-campaign.v1.json +32 -0
  9. code_standards-7.22.0/src/sarj_standards/configs/rule-inventory.v1.json +1 -0
  10. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/rule-ledger.json +6 -1
  11. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/adoption/doctor.py +416 -360
  12. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/adoption/hooks.py +58 -51
  13. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/adoption/lifecycle.py +13 -5
  14. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/adoption/manifest.py +28 -18
  15. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/adoption/retired_suppressions.py +74 -49
  16. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/adoption/scaffold.py +443 -344
  17. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/adoption/service.py +73 -46
  18. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/adoption/transaction.py +17 -12
  19. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/adoption/upgrade.py +209 -140
  20. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/catalogs/slack_automations.py +22 -13
  21. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/diagnostics/baseline.py +4 -0
  22. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/diagnostics/serialize.py +14 -8
  23. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/linting/analysis.py +19 -18
  24. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/linting/external.py +332 -271
  25. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/linting/runner.py +29 -19
  26. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/linting/textlint.py +291 -202
  27. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/release/artifacts.py +30 -20
  28. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/release/causality.py +40 -8
  29. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/release/rollout.py +469 -331
  30. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/release/tags.py +23 -11
  31. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/repository/comment_corpus.py +85 -52
  32. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/repository/repository.py +23 -9
  33. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/repository/rule_catalog_artifact.py +40 -30
  34. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/repository/rule_changes.py +42 -19
  35. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/repository/rule_maintenance.py +24 -12
  36. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/rules/contracts.py +25 -16
  37. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/rules/corpus_runner.py +34 -24
  38. code_standards-7.22.0/src/sarj_standards/schemas/rule-catalog.v1.json +1 -0
  39. code_standards-7.20.3/src/sarj_standards/configs/rule-inventory.v1.json +0 -1
  40. code_standards-7.20.3/src/sarj_standards/schemas/rule-catalog.v1.json +0 -1
  41. {code_standards-7.20.3 → code_standards-7.22.0}/.gitignore +0 -0
  42. {code_standards-7.20.3 → code_standards-7.22.0}/LICENSE +0 -0
  43. {code_standards-7.20.3 → code_standards-7.22.0}/README.md +0 -0
  44. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/__init__.py +0 -0
  45. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/__main__.py +0 -0
  46. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/_meta.py +0 -0
  47. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/cli/__init__.py +0 -0
  48. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/Mintfile.mobile.strict +0 -0
  49. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/basedpyright.python315-watch.json +0 -0
  50. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/basedpyright.strict.json +0 -0
  51. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/detekt.strict.yml +0 -0
  52. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/doctor.config.json +0 -0
  53. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/eslint-compact-formatter.mjs +0 -0
  54. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/eslint.application.mjs +0 -0
  55. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/ktlint.strict.editorconfig +0 -0
  56. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/markdownlint.strict.yaml +0 -0
  57. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/mobile-tools.versions.json +0 -0
  58. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/mobsf.strict.yml +0 -0
  59. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/pyright.strict.json +0 -0
  60. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/ruff.application.toml +0 -0
  61. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/ruff.python315-watch.toml +0 -0
  62. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/ruff.strict.toml +0 -0
  63. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/rule-warning-levels.v1.json +0 -0
  64. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/shellcheck.strict.rc +0 -0
  65. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/swiftformat.strict +0 -0
  66. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/swiftlint.strict.yml +0 -0
  67. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/taplo.strict.toml +0 -0
  68. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/configs/yamllint.strict.yaml +0 -0
  69. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/__init__.py +0 -0
  70. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/adoption/__init__.py +0 -0
  71. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/adoption/configs.py +0 -0
  72. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/adoption/exclusions.py +0 -0
  73. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/adoption/launcher.py +0 -0
  74. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/adoption/packagemanager.py +0 -0
  75. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/adoption/uvtool.py +0 -0
  76. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/catalogs/__init__.py +0 -0
  77. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/corpus/__init__.py +0 -0
  78. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/corpus/manifest.py +0 -0
  79. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/corpus/snapshot.py +0 -0
  80. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/diagnostics/__init__.py +0 -0
  81. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/diagnostics/analysis.schema.json +0 -0
  82. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/diagnostics/models.py +0 -0
  83. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/diagnostics/source.py +0 -0
  84. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/filesystem.py +0 -0
  85. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/linting/__init__.py +0 -0
  86. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/linting/library_policy.py +0 -0
  87. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/linting/mobile_tools.py +0 -0
  88. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/linting/policy.py +0 -0
  89. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/linting/repo_standards.py +0 -0
  90. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/release/__init__.py +0 -0
  91. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/release/_values.py +0 -0
  92. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/release/changes.py +0 -0
  93. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/release/process.py +0 -0
  94. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/release/publish.py +0 -0
  95. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/release/registry.py +0 -0
  96. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/release/release_age.py +0 -0
  97. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/release/retirement.py +0 -0
  98. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/release/typescript.py +0 -0
  99. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/repository/__init__.py +0 -0
  100. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/repository/cli_reference_artifact.py +0 -0
  101. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/repository/config_generation.py +0 -0
  102. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/repository/docs.py +0 -0
  103. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/repository/hooks.py +0 -0
  104. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/repository/ledger.py +0 -0
  105. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/repository/ruff_freshness.py +0 -0
  106. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/repository/rule_authoring.py +0 -0
  107. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/repository/rule_inventory_artifact.py +0 -0
  108. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/repository/rule_lifecycle.py +0 -0
  109. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/repository/third_party_catalog_artifact.py +0 -0
  110. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/rules/__init__.py +0 -0
  111. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/rules/catalog.py +0 -0
  112. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/rules/evaluation.py +0 -0
  113. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/rules/warning_levels.py +0 -0
  114. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/setup/__init__.py +0 -0
  115. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/libs/setup/repository.py +0 -0
  116. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/py.typed +0 -0
  117. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/schemas/__init__.py +0 -0
  118. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/schemas/_paths.py +0 -0
  119. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/schemas/rule-catalog.v1.schema.json +0 -0
  120. {code_standards-7.20.3 → code_standards-7.22.0}/src/sarj_standards/schemas/slack-automations.v1.schema.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: code-standards
3
- Version: 7.20.3
3
+ Version: 7.22.0
4
4
  Summary: One deterministic quality gate for Sarj Python, TypeScript, Swift, Kotlin, shell, SQL, IaC, and repository policy.
5
5
  Project-URL: Homepage, https://code-standards.sarj.ai/
6
6
  Project-URL: Documentation, https://code-standards.sarj.ai/cli/
@@ -27,9 +27,9 @@ Requires-Dist: pydantic==2.13.5
27
27
  Requires-Dist: pyyaml==6.0.3
28
28
  Requires-Dist: repo-standards==5.14.1
29
29
  Requires-Dist: ruff==0.16.7
30
- Requires-Dist: sarj-iac-lint==0.15.7
31
- Requires-Dist: sarj-python-lint==0.82.1
32
- Requires-Dist: sarj-sql-lint==0.17.0
30
+ Requires-Dist: sarj-iac-lint==0.15.8
31
+ Requires-Dist: sarj-python-lint==0.82.3
32
+ Requires-Dist: sarj-sql-lint==0.17.1
33
33
  Requires-Dist: semgrep==1.175.0
34
34
  Requires-Dist: shellcheck-py==0.11.0.1; (sys_platform == 'darwin' and platform_machine == 'arm64') or (sys_platform == 'darwin' and platform_machine == 'x86_64') or (sys_platform == 'linux' and platform_machine == 'x86_64') or (sys_platform == 'win32' and platform_machine == 'AMD64')
35
35
  Requires-Dist: typer==0.27.2
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "code-standards"
3
- version = "7.20.3"
3
+ version = "7.22.0"
4
4
  description = "One deterministic quality gate for Sarj Python, TypeScript, Swift, Kotlin, shell, SQL, IaC, and repository policy."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.14"
@@ -28,9 +28,9 @@ dependencies = [
28
28
  "ruff==0.16.7",
29
29
  "semgrep==1.175.0",
30
30
  "shellcheck-py==0.11.0.1; (sys_platform == 'darwin' and platform_machine == 'arm64') or (sys_platform == 'darwin' and platform_machine == 'x86_64') or (sys_platform == 'linux' and platform_machine == 'x86_64') or (sys_platform == 'win32' and platform_machine == 'AMD64')",
31
- "sarj-iac-lint==0.15.7",
32
- "sarj-python-lint==0.82.1",
33
- "sarj-sql-lint==0.17.0",
31
+ "sarj-iac-lint==0.15.8",
32
+ "sarj-python-lint==0.82.3",
33
+ "sarj-sql-lint==0.17.1",
34
34
  "typer==0.27.2",
35
35
  ]
36
36
 
@@ -70,6 +70,7 @@ if TYPE_CHECKING:
70
70
  from collections.abc import Sequence
71
71
 
72
72
  from .libs.adoption.manifest import Manifest, Profile
73
+ from .libs.linting.runner import GroupedPaths
73
74
 
74
75
 
75
76
  _INVALID_EXIT = 2
@@ -256,33 +257,13 @@ class Standards:
256
257
  return _failed_analysis(self.root, "invalid-input", str(exc))
257
258
  try:
258
259
  adopted = _analysis_manifest(self.root, normalized_mode)
259
- selection_policy = (
260
- Policy.corpus_from_manifest(self.root, adopted)
261
- if normalized_mode is AnalysisMode.CORPUS
262
- else (
263
- Policy.observe_from_manifest(self.root, adopted)
264
- if normalized_mode is AnalysisMode.OBSERVE
265
- else Policy.from_manifest(self.root, adopted)
266
- )
267
- )
260
+ selection_policy = _selection_policy(self.root, adopted, normalized_mode)
268
261
  rule_selection = _rule_selection(rules)
269
262
  selected = _analysis_inputs(self.root, paths, mode=normalized_mode)
270
263
  except (OSError, TypeError, ValueError) as exc:
271
264
  return _failed_analysis(self.root, "invalid-input", str(exc))
272
265
  try:
273
- baseline_counts: dict[str, int]
274
- baseline_counts = (
275
- diagnostic_baseline.load(
276
- self.root / adopted.diagnostic_baseline,
277
- require_v2=True,
278
- expected_bundle_version=__version__,
279
- expected_catalog_digest=diagnostic_baseline.bundled_catalog_digest(),
280
- )
281
- if normalized_mode is AnalysisMode.POLICY
282
- and adopted is not None
283
- and adopted.diagnostic_baseline is not None
284
- else {}
285
- )
266
+ baseline_counts = _analysis_baseline(self.root, adopted, normalized_mode)
286
267
  changed_scope = diagnostic_baseline.changed_line_scope(self.root, staged=staged)
287
268
  except (OSError, TypeError, ValueError) as exc:
288
269
  return _failed_analysis(self.root, "baseline-failure", str(exc))
@@ -303,70 +284,12 @@ class Standards:
303
284
  rule_selection=rule_selection,
304
285
  )
305
286
  if rule_selection is None:
306
- try:
307
- policy = _filter_tool_report(_policy_report(self.root, active_selected), selection_policy)
308
- except (OSError, TypeError, ValueError, ManifestPolicyError) as exc:
309
- issue = ExecutionIssue("sarj-library-policy", "policy-failure", f"{type(exc).__name__}: {exc}")
310
- policy = ToolReport("sarj-library-policy", Completion.FAILED, issues=(issue,))
311
- native = report_from_tools(self.root, (*native.tools, policy))
287
+ native = _with_library_policy(self.root, native, active_selected, selection_policy)
312
288
  if rule_selection is None and normalized_mode is AnalysisMode.POLICY:
313
- from .libs.linting.repo_standards import ( # ruff: ignore[import-outside-top-level]
314
- analyze as analyze_repository,
315
- )
316
-
317
- try:
318
- repository = analyze_repository(self.root, staged=staged)
319
- except Exception as exc: # ruff: ignore[blind-except] -- dependency contract failures must fail closed.
320
- issue = ExecutionIssue(
321
- "repo-standards",
322
- "integration-failure",
323
- f"{type(exc).__name__}: {exc}",
324
- )
325
- repository = ToolReport("repo-standards", Completion.FAILED, issues=(issue,))
326
- if repository is not None:
327
- native = report_from_tools(self.root, (*native.tools, repository))
328
- coverage: list[CoverageNotice] = []
329
- excluded = sum(Path(item).is_file() and item not in active_selected for item in selected)
330
- if excluded:
331
- coverage.append(
332
- CoverageNotice(
333
- "sarj-standards",
334
- "excluded by repository policy",
335
- excluded,
336
- CoverageDisposition.EXCLUDED,
337
- )
338
- )
339
- routed = _routed_for_selection(selected_groups, rule_selection)
340
- if rule_selection is None:
341
- routed.update(
342
- item
343
- for item in active_selected
344
- if Path(item).is_file() and library_policy_accepts_path(Path(item), self.root)
345
- )
346
- unsupported = sum(Path(item).is_file() and item not in routed for item in active_selected)
347
- if unsupported:
348
- coverage.append(
349
- CoverageNotice(
350
- "sarj-standards",
351
- "no bundled analyzer accepts the selected file type",
352
- unsupported,
353
- )
354
- )
289
+ native = _with_repository_analysis(self.root, native, staged=staged)
290
+ coverage = _selection_coverage(self.root, selected, active_selected, selected_groups, rule_selection)
355
291
  if not external:
356
- if selected_groups.typescript and (rule_selection is None or RuleEngine.ESLINT in rule_selection.engines):
357
- eslint_enabled = adopted is None or "eslint" in adopted.configs
358
- coverage.append(
359
- CoverageNotice(
360
- "eslint",
361
- (
362
- "native analysis does not run TypeScript; use check or external trusted analysis"
363
- if eslint_enabled
364
- else "disabled by repository capabilities"
365
- ),
366
- len(selected_groups.typescript),
367
- CoverageDisposition.FAILED if eslint_enabled else CoverageDisposition.NOT_REQUESTED,
368
- )
369
- )
292
+ _native_typescript_coverage(selected_groups, adopted, rule_selection, coverage)
370
293
  if normalized_mode in {AnalysisMode.POLICY, AnalysisMode.OBSERVE}:
371
294
  native = _with_warning_severity(native, _warning_rule_keys())
372
295
  return _with_coverage(
@@ -381,43 +304,20 @@ class Standards:
381
304
  CoverageDisposition.NOT_REQUESTED,
382
305
  )
383
306
  )
384
- run_eslint = rule_selection is None or RuleEngine.ESLINT in rule_selection.engines
385
- external_reports = (
386
- (
387
- analyze_external(
388
- active_selected,
389
- root=self.root,
390
- trust=normalized_trust,
391
- policy=selection_policy,
392
- capabilities=(
393
- frozenset({"eslint"}) if rule_selection is not None else frozenset(adopted.enabled_capabilities)
394
- ),
395
- grouped=selected_groups,
396
- include_react_doctor=include_react_doctor and rule_selection is None,
397
- force_react_doctor=react_doctor_triggered,
398
- react_doctor_staged=staged,
399
- react_doctor_full_scan=react_doctor_full_scan,
400
- pass_on_unpruned_eslint_suppressions=pass_on_unpruned_eslint_suppressions,
401
- )
402
- if adopted is not None
403
- else analyze_external(
404
- active_selected,
405
- root=self.root,
406
- trust=normalized_trust,
407
- capabilities=frozenset({"eslint"}) if rule_selection is not None else None,
408
- grouped=selected_groups,
409
- include_react_doctor=include_react_doctor and rule_selection is None,
410
- force_react_doctor=react_doctor_triggered,
411
- react_doctor_staged=staged,
412
- react_doctor_full_scan=react_doctor_full_scan,
413
- pass_on_unpruned_eslint_suppressions=pass_on_unpruned_eslint_suppressions,
414
- )
415
- )
416
- if run_eslint
417
- else ()
307
+ external_reports = _selected_external_analysis(
308
+ active_selected,
309
+ root=self.root,
310
+ selected_groups=selected_groups,
311
+ adopted=adopted,
312
+ selection_policy=selection_policy,
313
+ rule_selection=rule_selection,
314
+ normalized_trust=normalized_trust,
315
+ include_react_doctor=include_react_doctor,
316
+ react_doctor_triggered=react_doctor_triggered,
317
+ staged=staged,
318
+ react_doctor_full_scan=react_doctor_full_scan,
319
+ pass_on_unpruned_eslint_suppressions=pass_on_unpruned_eslint_suppressions,
418
320
  )
419
- if rule_selection is not None:
420
- external_reports = tuple(_filter_report_selectors(report, rule_selection) for report in external_reports)
421
321
  combined = report_from_tools(self.root, (*native.tools, *external_reports))
422
322
  if normalized_mode in {AnalysisMode.POLICY, AnalysisMode.OBSERVE}:
423
323
  combined = _with_warning_severity(combined, _warning_rule_keys())
@@ -675,6 +575,173 @@ def _with_tracked_terraform_tests(root: Path, selected: list[str]) -> list[str]:
675
575
  return list(dict.fromkeys((*selected, *diagnostic_baseline.tracked_terraform_test_paths(root))))
676
576
 
677
577
 
578
+ def _selection_policy(root: Path, adopted: Manifest | None, normalized_mode: AnalysisMode) -> Policy:
579
+ return (
580
+ Policy.corpus_from_manifest(root, adopted)
581
+ if normalized_mode is AnalysisMode.CORPUS
582
+ else (
583
+ Policy.observe_from_manifest(root, adopted)
584
+ if normalized_mode is AnalysisMode.OBSERVE
585
+ else Policy.from_manifest(root, adopted)
586
+ )
587
+ )
588
+
589
+
590
+ def _selected_external_analysis(
591
+ active_selected: list[str],
592
+ *,
593
+ root: Path,
594
+ selected_groups: GroupedPaths,
595
+ adopted: Manifest | None,
596
+ selection_policy: Policy,
597
+ rule_selection: RuleSelection | None,
598
+ normalized_trust: TrustMode,
599
+ include_react_doctor: bool,
600
+ react_doctor_triggered: bool,
601
+ staged: bool,
602
+ react_doctor_full_scan: bool,
603
+ pass_on_unpruned_eslint_suppressions: bool,
604
+ ) -> tuple[ToolReport, ...]:
605
+ run_eslint = rule_selection is None or RuleEngine.ESLINT in rule_selection.engines
606
+ external_reports = (
607
+ (
608
+ analyze_external(
609
+ active_selected,
610
+ root=root,
611
+ trust=normalized_trust,
612
+ policy=selection_policy,
613
+ capabilities=(
614
+ frozenset({"eslint"}) if rule_selection is not None else frozenset(adopted.enabled_capabilities)
615
+ ),
616
+ grouped=selected_groups,
617
+ include_react_doctor=include_react_doctor and rule_selection is None,
618
+ force_react_doctor=react_doctor_triggered,
619
+ react_doctor_staged=staged,
620
+ react_doctor_full_scan=react_doctor_full_scan,
621
+ pass_on_unpruned_eslint_suppressions=pass_on_unpruned_eslint_suppressions,
622
+ )
623
+ if adopted is not None
624
+ else analyze_external(
625
+ active_selected,
626
+ root=root,
627
+ trust=normalized_trust,
628
+ capabilities=frozenset({"eslint"}) if rule_selection is not None else None,
629
+ grouped=selected_groups,
630
+ include_react_doctor=include_react_doctor and rule_selection is None,
631
+ force_react_doctor=react_doctor_triggered,
632
+ react_doctor_staged=staged,
633
+ react_doctor_full_scan=react_doctor_full_scan,
634
+ pass_on_unpruned_eslint_suppressions=pass_on_unpruned_eslint_suppressions,
635
+ )
636
+ )
637
+ if run_eslint
638
+ else ()
639
+ )
640
+ if rule_selection is not None:
641
+ external_reports = tuple(_filter_report_selectors(report, rule_selection) for report in external_reports)
642
+ return external_reports
643
+
644
+
645
+ def _with_library_policy(
646
+ root: Path, native: AnalysisReport, active_selected: list[str], selection_policy: Policy
647
+ ) -> AnalysisReport:
648
+ try:
649
+ policy = _filter_tool_report(_policy_report(root, active_selected), selection_policy)
650
+ except (OSError, TypeError, ValueError, ManifestPolicyError) as exc:
651
+ issue = ExecutionIssue("sarj-library-policy", "policy-failure", f"{type(exc).__name__}: {exc}")
652
+ policy = ToolReport("sarj-library-policy", Completion.FAILED, issues=(issue,))
653
+ return report_from_tools(root, (*native.tools, policy))
654
+
655
+
656
+ def _with_repository_analysis(root: Path, native: AnalysisReport, *, staged: bool) -> AnalysisReport:
657
+ from .libs.linting.repo_standards import ( # ruff: ignore[import-outside-top-level]
658
+ analyze as analyze_repository,
659
+ )
660
+
661
+ try:
662
+ repository = analyze_repository(root, staged=staged)
663
+ except Exception as exc: # ruff: ignore[blind-except] -- dependency contract failures must fail closed.
664
+ issue = ExecutionIssue(
665
+ "repo-standards",
666
+ "integration-failure",
667
+ f"{type(exc).__name__}: {exc}",
668
+ )
669
+ repository = ToolReport("repo-standards", Completion.FAILED, issues=(issue,))
670
+ if repository is not None:
671
+ native = report_from_tools(root, (*native.tools, repository))
672
+ return native
673
+
674
+
675
+ def _selection_coverage(
676
+ root: Path,
677
+ selected: list[str],
678
+ active_selected: list[str],
679
+ selected_groups: GroupedPaths,
680
+ rule_selection: RuleSelection | None,
681
+ ) -> list[CoverageNotice]:
682
+ coverage: list[CoverageNotice] = []
683
+ excluded = sum(Path(item).is_file() and item not in active_selected for item in selected)
684
+ if excluded:
685
+ coverage.append(
686
+ CoverageNotice(
687
+ "sarj-standards",
688
+ "excluded by repository policy",
689
+ excluded,
690
+ CoverageDisposition.EXCLUDED,
691
+ )
692
+ )
693
+ routed = _routed_for_selection(selected_groups, rule_selection)
694
+ if rule_selection is None:
695
+ routed.update(
696
+ item for item in active_selected if Path(item).is_file() and library_policy_accepts_path(Path(item), root)
697
+ )
698
+ unsupported = sum(Path(item).is_file() and item not in routed for item in active_selected)
699
+ if unsupported:
700
+ coverage.append(
701
+ CoverageNotice(
702
+ "sarj-standards",
703
+ "no bundled analyzer accepts the selected file type",
704
+ unsupported,
705
+ )
706
+ )
707
+ return coverage
708
+
709
+
710
+ def _native_typescript_coverage(
711
+ selected_groups: GroupedPaths,
712
+ adopted: Manifest | None,
713
+ rule_selection: RuleSelection | None,
714
+ coverage: list[CoverageNotice],
715
+ ) -> None:
716
+ if selected_groups.typescript and (rule_selection is None or RuleEngine.ESLINT in rule_selection.engines):
717
+ eslint_enabled = adopted is None or "eslint" in adopted.configs
718
+ coverage.append(
719
+ CoverageNotice(
720
+ "eslint",
721
+ (
722
+ "native analysis does not run TypeScript; use check or external trusted analysis"
723
+ if eslint_enabled
724
+ else "disabled by repository capabilities"
725
+ ),
726
+ len(selected_groups.typescript),
727
+ CoverageDisposition.FAILED if eslint_enabled else CoverageDisposition.NOT_REQUESTED,
728
+ )
729
+ )
730
+
731
+
732
+ def _analysis_baseline(root: Path, adopted: Manifest | None, normalized_mode: AnalysisMode) -> dict[str, int]:
733
+ return (
734
+ diagnostic_baseline.load(
735
+ root / adopted.diagnostic_baseline,
736
+ require_v2=True,
737
+ expected_bundle_version=__version__,
738
+ expected_catalog_digest=diagnostic_baseline.bundled_catalog_digest(),
739
+ )
740
+ if normalized_mode is AnalysisMode.POLICY and adopted is not None and adopted.diagnostic_baseline is not None
741
+ else {}
742
+ )
743
+
744
+
678
745
  def _rule_selection(values: Sequence[str | RuleSelector] | None) -> RuleSelection | None:
679
746
  if values is None:
680
747
  return None