code-standards 8.150.0__tar.gz → 8.151.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 (210) hide show
  1. {code_standards-8.150.0 → code_standards-8.151.0}/PKG-INFO +7 -5
  2. {code_standards-8.150.0 → code_standards-8.151.0}/README.md +2 -0
  3. {code_standards-8.150.0 → code_standards-8.151.0}/pyproject.toml +5 -5
  4. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/api.py +19 -6
  5. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/cli-reference.v1.json +2 -2
  6. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/adoption/devops.py +4 -4
  7. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/diagnostics/analysis.schema.json +2 -1
  8. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/diagnostics/models.py +45 -14
  9. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/diagnostics/serialize.py +5 -2
  10. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/diagnostics/source.py +8 -2
  11. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/action_metadata.py +6 -6
  12. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/analysis.py +82 -23
  13. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/devops_source.py +9 -9
  14. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/devops_tools.py +17 -14
  15. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/duplicate_code.py +3 -3
  16. code_standards-8.151.0/src/sarj_standards/libs/linting/execution_plan.py +59 -0
  17. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/external.py +300 -199
  18. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/prepared_devops.py +17 -7
  19. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/prepared_native.py +3 -3
  20. code_standards-8.151.0/src/sarj_standards/libs/linting/process.py +114 -0
  21. code_standards-8.151.0/src/sarj_standards/libs/linting/scheduling.py +38 -0
  22. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/unused_modules.py +3 -3
  23. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/docs.py +17 -2
  24. code_standards-8.150.0/src/sarj_standards/libs/linting/scheduling.py +0 -41
  25. {code_standards-8.150.0 → code_standards-8.151.0}/.gitignore +0 -0
  26. {code_standards-8.150.0 → code_standards-8.151.0}/LICENSE +0 -0
  27. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/__init__.py +0 -0
  28. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/__main__.py +0 -0
  29. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/_meta.py +0 -0
  30. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/cli/__init__.py +0 -0
  31. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/cli/main.py +0 -0
  32. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/Mintfile.mobile.strict +0 -0
  33. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/action-parser/LICENSE.actions +0 -0
  34. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/action-parser/LICENSE.cronstrue +0 -0
  35. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/action-parser/LICENSE.yaml +0 -0
  36. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/action-parser/generate.mts +0 -0
  37. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/action-parser/index.mts +0 -0
  38. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/action-parser/package-lock.json +0 -0
  39. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/action-parser/package.json +0 -0
  40. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/action-parser/upstream.json +0 -0
  41. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/action-parser.cjs +0 -0
  42. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/actionlint.strict.yaml +0 -0
  43. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/basedpyright.python315-watch.json +0 -0
  44. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/basedpyright.strict.json +0 -0
  45. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/checkov.strict.yml +0 -0
  46. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/compile-eslint.mts +0 -0
  47. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/detekt.strict.yml +0 -0
  48. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/devops-verification.v1.json +0 -0
  49. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/doctor.config.json +0 -0
  50. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/eslint-compact-formatter.mts +0 -0
  51. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/eslint-required-policy.mts +0 -0
  52. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/eslint-selected-rules.mts +0 -0
  53. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/eslint.application.mjs +0 -0
  54. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/eslint.peers.json +0 -0
  55. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/eslint.strict.mjs +0 -0
  56. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/eslint.strict.ts +0 -0
  57. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/hadolint.strict.yaml +0 -0
  58. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/ktlint.strict.editorconfig +0 -0
  59. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/markdownlint.strict.yaml +0 -0
  60. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/mobile-tools.versions.json +0 -0
  61. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/mobsf.strict.yml +0 -0
  62. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/pyright.strict.json +0 -0
  63. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/ruff.application.toml +0 -0
  64. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/ruff.cast.toml +0 -0
  65. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/ruff.python315-watch.toml +0 -0
  66. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/ruff.required.toml +0 -0
  67. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/ruff.strict.toml +0 -0
  68. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/rule-calibration-campaign.v1.json +0 -0
  69. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/rule-inventory.v1.json +0 -0
  70. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/rule-ledger.json +0 -0
  71. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/rule-level-source.mts +0 -0
  72. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/schemas/SKAFFOLD-LICENSE +0 -0
  73. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/schemas/google-build-v1-schema.json +0 -0
  74. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/schemas/google-build-v1.LICENSE +0 -0
  75. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/schemas/skaffold-v4beta7.json +0 -0
  76. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/schemas/upstream-schemas.json +0 -0
  77. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/semgrep-overrides.txt +0 -0
  78. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/shellcheck.strict.rc +0 -0
  79. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/swiftformat.strict +0 -0
  80. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/swiftlint.strict.yml +0 -0
  81. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/taplo.strict.toml +0 -0
  82. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/tflint.strict.hcl +0 -0
  83. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/tflint.version.hcl +0 -0
  84. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/tool-json.mts +0 -0
  85. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/yamllint.strict.yaml +0 -0
  86. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/configs/zizmor.strict.yml +0 -0
  87. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/__init__.py +0 -0
  88. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/adoption/__init__.py +0 -0
  89. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/adoption/age_transition.py +0 -0
  90. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/adoption/configs.py +0 -0
  91. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/adoption/doctor.py +0 -0
  92. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/adoption/exclusions.py +0 -0
  93. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/adoption/hooks.py +0 -0
  94. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/adoption/launcher.py +0 -0
  95. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/adoption/lifecycle.py +0 -0
  96. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/adoption/manifest.py +0 -0
  97. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/adoption/native_bootstrap.py +0 -0
  98. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/adoption/packagemanager.py +0 -0
  99. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/adoption/repository_baseline.py +0 -0
  100. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/adoption/retired_suppressions.py +0 -0
  101. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/adoption/scaffold.py +0 -0
  102. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/adoption/service.py +0 -0
  103. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/adoption/transaction.py +0 -0
  104. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/adoption/upgrade.py +0 -0
  105. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/adoption/uvtool.py +0 -0
  106. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/catalogs/__init__.py +0 -0
  107. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/catalogs/slack_automations.py +0 -0
  108. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/corpus/__init__.py +0 -0
  109. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/corpus/manifest.py +0 -0
  110. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/corpus/snapshot.py +0 -0
  111. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/diagnostics/__init__.py +0 -0
  112. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/diagnostics/baseline.py +0 -0
  113. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/diagnostics/initial_import.py +0 -0
  114. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/filesystem.py +0 -0
  115. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/json_boundary.py +0 -0
  116. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/__init__.py +0 -0
  117. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/_cloudbuild_autofix.py +0 -0
  118. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/autofix.py +0 -0
  119. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/chart_schemas.py +0 -0
  120. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/cloudbuild.py +0 -0
  121. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/cloudbuild_schema.py +0 -0
  122. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/complexity_baseline.py +0 -0
  123. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/devops_programs.py +0 -0
  124. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/devops_schema.py +0 -0
  125. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/embedded_shell.py +0 -0
  126. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/kubernetes_context.py +0 -0
  127. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/library_policy.py +0 -0
  128. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/mobile_tools.py +0 -0
  129. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/policy.py +0 -0
  130. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/prepared_kubernetes.py +0 -0
  131. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/python_fixes.py +0 -0
  132. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/repo_standards.py +0 -0
  133. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/runner.py +0 -0
  134. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/security_tools.py +0 -0
  135. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/shell_ast.py +0 -0
  136. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/shell_format.py +0 -0
  137. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/shell_policy.py +0 -0
  138. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/test_value.py +0 -0
  139. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/text_rule_base.py +0 -0
  140. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/text_rules/__init__.py +0 -0
  141. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/text_rules/_registry.py +0 -0
  142. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/text_rules/_slack_payload.py +0 -0
  143. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/text_rules/cloudbuild_contract.py +0 -0
  144. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/text_rules/slack_event_text_in_mrkdwn.py +0 -0
  145. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/textlint.py +0 -0
  146. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/linting/toml_literals.py +0 -0
  147. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/__init__.py +0 -0
  148. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/_values.py +0 -0
  149. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/artifacts.py +0 -0
  150. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/causality.py +0 -0
  151. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/changes.py +0 -0
  152. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/ci_artifacts.py +0 -0
  153. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/make_checks.py +0 -0
  154. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/metadata_verification.py +0 -0
  155. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/native_test_setup.py +0 -0
  156. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/process.py +0 -0
  157. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/publish.py +0 -0
  158. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/registry.py +0 -0
  159. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/release_age.py +0 -0
  160. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/release_detect.py +0 -0
  161. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/retirement.py +0 -0
  162. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/reviewed_analysis.py +0 -0
  163. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/rollout.py +0 -0
  164. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/status.py +0 -0
  165. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/tags.py +0 -0
  166. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/test_selection.py +0 -0
  167. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/typescript.py +0 -0
  168. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/verification_process.py +0 -0
  169. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/release/wheel_tests.py +0 -0
  170. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/__init__.py +0 -0
  171. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/cli_examples.py +0 -0
  172. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/cli_reference_artifact.py +0 -0
  173. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/cloudbuild_schema_projection.py +0 -0
  174. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/comment_corpus.py +0 -0
  175. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/config_generation.py +0 -0
  176. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/hooks.py +0 -0
  177. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/immutable_git.py +0 -0
  178. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/ledger.py +0 -0
  179. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/repository.py +0 -0
  180. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/ruff_freshness.py +0 -0
  181. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/rule_authoring.py +0 -0
  182. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/rule_catalog_artifact.py +0 -0
  183. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/rule_changes.py +0 -0
  184. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/rule_checkout.py +0 -0
  185. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/rule_dependencies.py +0 -0
  186. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/rule_examples.py +0 -0
  187. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/rule_imports.mts +0 -0
  188. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/rule_inventory_artifact.py +0 -0
  189. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/rule_level_source.py +0 -0
  190. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/rule_lifecycle.py +0 -0
  191. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/rule_maintenance.py +0 -0
  192. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/rule_registration.py +0 -0
  193. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/shell_checks.py +0 -0
  194. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/repository/third_party_catalog_artifact.py +0 -0
  195. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/rules/__init__.py +0 -0
  196. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/rules/catalog.py +0 -0
  197. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/rules/contracts.py +0 -0
  198. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/rules/corpus_runner.py +0 -0
  199. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/rules/evaluation.py +0 -0
  200. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/schema_boundary.py +0 -0
  201. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/setup/__init__.py +0 -0
  202. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/setup/repository.py +0 -0
  203. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/typed_containers.py +0 -0
  204. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/libs/yaml_boundary.py +0 -0
  205. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/py.typed +0 -0
  206. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/schemas/__init__.py +0 -0
  207. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/schemas/_paths.py +0 -0
  208. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/schemas/rule-catalog.v1.json +0 -0
  209. {code_standards-8.150.0 → code_standards-8.151.0}/src/sarj_standards/schemas/rule-catalog.v1.schema.json +0 -0
  210. {code_standards-8.150.0 → code_standards-8.151.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: 8.150.0
3
+ Version: 8.151.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/
@@ -30,10 +30,10 @@ Requires-Dist: pyyaml==6.0.3
30
30
  Requires-Dist: referencing==0.37.0
31
31
  Requires-Dist: repo-standards==6.14.0
32
32
  Requires-Dist: ruff==0.17.2
33
- Requires-Dist: sarj-iac-lint==0.30.3
34
- Requires-Dist: sarj-python-lint==1.85.0
35
- Requires-Dist: sarj-rule-contracts==0.4.2
36
- Requires-Dist: sarj-sql-lint==0.31.0
33
+ Requires-Dist: sarj-iac-lint==0.31.0
34
+ Requires-Dist: sarj-python-lint==1.88.0
35
+ Requires-Dist: sarj-rule-contracts==0.5.0
36
+ Requires-Dist: sarj-sql-lint==0.33.0
37
37
  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')
38
38
  Requires-Dist: typer==0.27.3
39
39
  Description-Content-Type: text/markdown
@@ -105,6 +105,8 @@ Unused private Python test helpers require `reportUnusedFunction` set to `warnin
105
105
 
106
106
  `check --jobs 2` overlaps native analysis with the external-tool pipeline. The default is `--jobs 1`; external tools remain sequential and report ordering is deterministic.
107
107
 
108
+ Public Python suggested edits retain `expected_source_hash`, the lowercase SHA-256 digest of the exact captured source bytes, alongside `expected_text_hash` for the replaced slice. Use `to_json(report, include_fix_source_hashes=True)` or the same keyword on report, diagnostic, fix or edit `as_dict` to emit `expectedSourceHash`. The default JSON output is unchanged. Opted-in JSON requires the updated bundled `analysis.schema.json`; older strict v1 schemas reject the added field. This provenance does not apply edits or validate a current file, and SARIF output is unchanged.
109
+
108
110
  DevOps execution blocks may sequence existing commands without helper scripts. Keep interpreter source, control flow, logical chains, pipelines and substitutions in independently linted files; Python `-m` and script paths, and jq/awk `-f` files remain supported. SARJ310 is an error, including short inline programs. Cloud Build SARJ315 checks YAML and JSON for unique IDs, ordered step dependencies, script/argv exclusivity and untrusted substitutions in shell source; pass substitutions as quoted arguments to external scripts. JSON configs are discovered in operational directories or with `cloudbuild` in their filename; JSON syntax must be strict, with unique keys and finite numbers. Files named with `cloudbuild`, and Build-shaped files under a `cloudbuild` directory, use the bundled, digest-verified Google Build v1 field schema for field names, types, enums and protobuf durations/integers. The offline schema rejects output-only fields; trigger expansion, resource access and hosted admission require the deployment environment.
109
111
 
110
112
  The source gate uses pinned actionlint, shared ShellCheck, offline Zizmor, Hadolint and Skaffold v4beta7 schema validation. Every selected `action.yml`/`action.yaml` uses GitHub's pinned action parser and converter offline, including composite steps, expression syntax and available contexts, runtime requirements, step IDs and action-reference forms. This uses the core Node runtime and needs no referencing workflow, consumer npm setup or security capability. Cloud Deploy validation is a documented partial structural contract. Terraform validation and TFLint require explicit repository trust and prepared local providers; analysis never initializes or downloads them. Compose requires an exact consumer `[devops].compose_version`; its source check does not resolve environment inputs. Declare `compose_projects = [["base.yaml", "overlays/production.yaml"]]` in `[devops]` to validate custom filenames and overlays in invocation order. Selecting any member checks the whole project; declared fragments are not checked independently. Undeclared conventional filenames remain standalone projects. Missing inputs and native field errors fail coverage. Source checks use Compose's partially resolved model; full consistency requires the actual deployment environment.
@@ -65,6 +65,8 @@ Unused private Python test helpers require `reportUnusedFunction` set to `warnin
65
65
 
66
66
  `check --jobs 2` overlaps native analysis with the external-tool pipeline. The default is `--jobs 1`; external tools remain sequential and report ordering is deterministic.
67
67
 
68
+ Public Python suggested edits retain `expected_source_hash`, the lowercase SHA-256 digest of the exact captured source bytes, alongside `expected_text_hash` for the replaced slice. Use `to_json(report, include_fix_source_hashes=True)` or the same keyword on report, diagnostic, fix or edit `as_dict` to emit `expectedSourceHash`. The default JSON output is unchanged. Opted-in JSON requires the updated bundled `analysis.schema.json`; older strict v1 schemas reject the added field. This provenance does not apply edits or validate a current file, and SARIF output is unchanged.
69
+
68
70
  DevOps execution blocks may sequence existing commands without helper scripts. Keep interpreter source, control flow, logical chains, pipelines and substitutions in independently linted files; Python `-m` and script paths, and jq/awk `-f` files remain supported. SARJ310 is an error, including short inline programs. Cloud Build SARJ315 checks YAML and JSON for unique IDs, ordered step dependencies, script/argv exclusivity and untrusted substitutions in shell source; pass substitutions as quoted arguments to external scripts. JSON configs are discovered in operational directories or with `cloudbuild` in their filename; JSON syntax must be strict, with unique keys and finite numbers. Files named with `cloudbuild`, and Build-shaped files under a `cloudbuild` directory, use the bundled, digest-verified Google Build v1 field schema for field names, types, enums and protobuf durations/integers. The offline schema rejects output-only fields; trigger expansion, resource access and hosted admission require the deployment environment.
69
71
 
70
72
  The source gate uses pinned actionlint, shared ShellCheck, offline Zizmor, Hadolint and Skaffold v4beta7 schema validation. Every selected `action.yml`/`action.yaml` uses GitHub's pinned action parser and converter offline, including composite steps, expression syntax and available contexts, runtime requirements, step IDs and action-reference forms. This uses the core Node runtime and needs no referencing workflow, consumer npm setup or security capability. Cloud Deploy validation is a documented partial structural contract. Terraform validation and TFLint require explicit repository trust and prepared local providers; analysis never initializes or downloads them. Compose requires an exact consumer `[devops].compose_version`; its source check does not resolve environment inputs. Declare `compose_projects = [["base.yaml", "overlays/production.yaml"]]` in `[devops]` to validate custom filenames and overlays in invocation order. Selecting any member checks the whole project; declared fragments are not checked independently. Undeclared conventional filenames remain standalone projects. Missing inputs and native field errors fail coverage. Source checks use Compose's partially resolved model; full consistency requires the actual deployment environment.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "code-standards"
3
- version = "8.150.0"
3
+ version = "8.151.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"
@@ -17,7 +17,7 @@ classifiers = [
17
17
  "Topic :: Software Development :: Quality Assurance",
18
18
  ]
19
19
  dependencies = [
20
- "sarj-rule-contracts==0.4.2",
20
+ "sarj-rule-contracts==0.5.0",
21
21
  "pathspec==1.1.1",
22
22
  "basedpyright==1.40.2",
23
23
  "deptry==0.25.1",
@@ -31,9 +31,9 @@ dependencies = [
31
31
  "repo-standards==6.14.0",
32
32
  "ruff==0.17.2",
33
33
  "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')",
34
- "sarj-iac-lint==0.30.3",
35
- "sarj-python-lint==1.85.0",
36
- "sarj-sql-lint==0.31.0",
34
+ "sarj-iac-lint==0.31.0",
35
+ "sarj-python-lint==1.88.0",
36
+ "sarj-sql-lint==0.33.0",
37
37
  "typer==0.27.3",
38
38
  ]
39
39
 
@@ -56,6 +56,7 @@ from .libs.diagnostics import (
56
56
  from .libs.filesystem import is_link_like
57
57
  from .libs.linting.analysis import analyze as analyze_paths, report_from_tools
58
58
  from .libs.linting.complexity_baseline import ComplexityBaselineResolver
59
+ from .libs.linting.execution_plan import analysis_jobs
59
60
  from .libs.linting.external import UpstreamESLintRules, analyze_external
60
61
  from .libs.linting.library_policy import (
61
62
  ManifestPolicyError,
@@ -137,6 +138,17 @@ class Result:
137
138
  changes: tuple[Change, ...] = ()
138
139
  exit_code: int = 0
139
140
 
141
+ def __post_init__(self) -> None:
142
+ if type(self.status) is not Status:
143
+ msg = "result status must be a Status"
144
+ raise TypeError(msg)
145
+ if isinstance(self.exit_code, bool) or not isinstance(self.exit_code, int): # pyright: ignore[reportUnnecessaryIsInstance] -- validate untyped public constructor and dataclass replacement calls as well as typed callers.
146
+ msg = "result exit code must be an integer"
147
+ raise TypeError(msg)
148
+ if (self.status in {Status.OK, Status.CHANGED}) != (self.exit_code == 0):
149
+ msg = "result status and exit code must agree on success"
150
+ raise ValueError(msg)
151
+
140
152
  @property
141
153
  def ok(self) -> bool:
142
154
  return self.exit_code == 0
@@ -269,10 +281,9 @@ class Standards:
269
281
  ) -> AnalysisReport:
270
282
  try:
271
283
  import_context = initial_import.load(self.root, staged=staged)
284
+ analysis_jobs(jobs)
272
285
  except ValueError as exc:
273
286
  return _failed_analysis(self.root, "invalid-input", str(exc))
274
- if jobs not in {1, 2}:
275
- return _failed_analysis(self.root, "invalid-input", "analysis jobs must be 1 or 2")
276
287
  external = external or bool(upstream_rules.ids)
277
288
  try:
278
289
  normalized_trust = TrustMode(trust)
@@ -300,7 +311,7 @@ class Standards:
300
311
  try:
301
312
  selection_policy = _selection_policy(self.root, adopted, normalized_mode)
302
313
  rule_selection = _rule_selection(rules)
303
- selected = _analysis_inputs(self.root, paths, mode=normalized_mode)
314
+ selected = _analysis_inputs(self.root, paths, adopted=adopted, mode=normalized_mode)
304
315
  except (OSError, TypeError, ValueError) as exc:
305
316
  return _failed_analysis(self.root, "invalid-input", str(exc))
306
317
  try:
@@ -512,7 +523,8 @@ class Standards:
512
523
  )
513
524
  return Result(Status.FAILED, findings=(finding,), exit_code=1)
514
525
  finding = Finding("update.latest.failed", "error", output or "latest standards update failed")
515
- return Result(Status.FAILED, findings=(finding,), exit_code=completed.returncode)
526
+ status = Status.INTERRUPTED if completed.returncode == _INTERRUPTED_EXIT else Status.FAILED
527
+ return Result(status, findings=(finding,), exit_code=completed.returncode)
516
528
 
517
529
  def inspect(self) -> Inspection:
518
530
  return inspect(self.root)
@@ -823,13 +835,14 @@ def _with_coverage(report: AnalysisReport, coverage: Sequence[CoverageNotice]) -
823
835
  return AnalysisReport(report.root, completion, conclusion, report.tools, notices)
824
836
 
825
837
 
826
- def _analysis_inputs(root: Path, paths: Sequence[str] | None, *, mode: AnalysisMode = AnalysisMode.POLICY) -> list[str]:
838
+ def _analysis_inputs(
839
+ root: Path, paths: Sequence[str] | None, *, adopted: Manifest | None, mode: AnalysisMode
840
+ ) -> list[str]:
827
841
  if paths is not None:
828
842
  selected = _contained_paths(root, paths)
829
843
  return selected if mode is AnalysisMode.RAW else _with_tracked_terraform_tests(root, selected)
830
844
  if mode is AnalysisMode.RAW:
831
845
  return [str(root)]
832
- adopted = load_manifest(root)
833
846
  verify_paths = adopted.verify_paths if adopted is not None else (".",)
834
847
  return _with_tracked_terraform_tests(root, [str(root / path) for path in verify_paths])
835
848
 
@@ -3316,6 +3316,6 @@
3316
3316
  },
3317
3317
  "program": "code-standards",
3318
3318
  "schemaVersion": 1,
3319
- "summary": "Adopt, check, fix, diagnose, and update sarj-ai standards (v8.150.0).",
3320
- "version": "8.150.0"
3319
+ "summary": "Adopt, check, fix, diagnose, and update sarj-ai standards (v8.151.0).",
3320
+ "version": "8.151.0"
3321
3321
  }
@@ -12,7 +12,7 @@ from .doctor import Finding
12
12
  if TYPE_CHECKING:
13
13
  from collections.abc import Sequence
14
14
 
15
- from sarj_standards.libs.linting.external import ProcessRunner
15
+ from sarj_standards.libs.linting.external import ProcessExecutor
16
16
 
17
17
  from .lifecycle import Command
18
18
 
@@ -133,9 +133,9 @@ def install_commands(
133
133
  return tuple(commands)
134
134
 
135
135
 
136
- def health_findings(root: Path, files: Sequence[Path], *, runner: ProcessRunner | None = None) -> tuple[Finding, ...]:
136
+ def health_findings(root: Path, files: Sequence[Path], *, runner: ProcessExecutor | None = None) -> tuple[Finding, ...]:
137
137
  from sarj_standards.libs.linting.devops_tools import TOOLS, checked_tool # ruff: ignore[import-outside-top-level] -- read-only doctor checks start after adoption modules initialize.
138
- from sarj_standards.libs.linting.external import run_process # ruff: ignore[import-outside-top-level] -- reuse bounded native runner.
138
+ from sarj_standards.libs.linting.external import DEFAULT_EXECUTOR # ruff: ignore[import-outside-top-level] -- reuse bounded native runner.
139
139
 
140
140
  from .doctor import Finding, Level # ruff: ignore[import-outside-top-level] -- doctor record boundary avoids recursive module imports.
141
141
 
@@ -155,7 +155,7 @@ def health_findings(root: Path, files: Sequence[Path], *, runner: ProcessRunner
155
155
  checked_tool(
156
156
  name,
157
157
  root=root,
158
- runner=run_process if runner is None else runner,
158
+ runner=DEFAULT_EXECUTOR if runner is None else runner,
159
159
  expected_version=adopted.compose_version if name == "docker" else None,
160
160
  )
161
161
  except (OSError, ValueError, subprocess.SubprocessError) as error:
@@ -64,7 +64,8 @@
64
64
  "properties": {
65
65
  "location": { "$ref": "#/$defs/location" },
66
66
  "replacement": { "type": "string" },
67
- "expectedTextHash": { "type": "string", "pattern": "^[0-9a-f]{64}$" }
67
+ "expectedTextHash": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
68
+ "expectedSourceHash": { "type": "string", "pattern": "^[0-9a-f]{64}$" }
68
69
  }
69
70
  },
70
71
  "fix": {
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from dataclasses import dataclass
3
+ from dataclasses import dataclass, field
4
4
  from enum import StrEnum
5
5
  from hashlib import sha256
6
6
  from pathlib import Path, PureWindowsPath
@@ -98,6 +98,7 @@ class _TextEditData(TypedDict):
98
98
  location: _LocationData
99
99
  replacement: str
100
100
  expectedTextHash: NotRequired[str]
101
+ expectedSourceHash: NotRequired[str]
101
102
 
102
103
 
103
104
  class _FixData(TypedDict):
@@ -289,6 +290,7 @@ class TextEdit:
289
290
  location: Location
290
291
  replacement: str
291
292
  expected_text_hash: str | None = None
293
+ expected_source_hash: str | None = field(default=None, kw_only=True)
292
294
 
293
295
  def __post_init__(self) -> None:
294
296
  _require_instance(self.location, Location, "text edit location")
@@ -297,17 +299,17 @@ class TextEdit:
297
299
  raise ValueError(msg)
298
300
  _require_text(self.replacement, "text edit replacement", allow_empty=True)
299
301
  if self.expected_text_hash is not None:
300
- _require_text(self.expected_text_hash, "expected text hash")
301
- if len(self.expected_text_hash) != _SHA256_HEX_LENGTH or any(
302
- char not in "0123456789abcdef" for char in self.expected_text_hash
303
- ):
304
- msg = "expected text hash must be a lowercase SHA-256 digest"
305
- raise ValueError(msg)
302
+ _require_sha256(self.expected_text_hash, "expected text hash")
303
+ if self.expected_source_hash is not None:
304
+ _require_sha256(self.expected_source_hash, "expected source hash")
306
305
 
307
- def as_dict(self) -> _TextEditData:
306
+ def as_dict(self, *, include_fix_source_hashes: bool = False) -> _TextEditData:
307
+ _require_instance(include_fix_source_hashes, bool, "include fix source hashes")
308
308
  result: _TextEditData = {"location": self.location.as_dict(), "replacement": self.replacement}
309
309
  if self.expected_text_hash is not None:
310
310
  result["expectedTextHash"] = self.expected_text_hash
311
+ if include_fix_source_hashes and self.expected_source_hash is not None:
312
+ result["expectedSourceHash"] = self.expected_source_hash
311
313
  return result
312
314
 
313
315
 
@@ -340,9 +342,15 @@ class Fix:
340
342
  ):
341
343
  msg = "fix edits must not overlap"
342
344
  raise ValueError(msg)
345
+ _require_matching_edit_sources(self.edits)
343
346
 
344
- def as_dict(self) -> _FixData:
345
- return {"title": self.title, "safety": self.safety.value, "edits": [edit.as_dict() for edit in self.edits]}
347
+ def as_dict(self, *, include_fix_source_hashes: bool = False) -> _FixData:
348
+ _require_instance(include_fix_source_hashes, bool, "include fix source hashes")
349
+ return {
350
+ "title": self.title,
351
+ "safety": self.safety.value,
352
+ "edits": [edit.as_dict(include_fix_source_hashes=include_fix_source_hashes) for edit in self.edits],
353
+ }
346
354
 
347
355
 
348
356
  @dataclass(frozen=True, slots=True)
@@ -405,7 +413,8 @@ class Diagnostic:
405
413
  if self.fingerprint is not None:
406
414
  _require_text(self.fingerprint, "diagnostic fingerprint")
407
415
 
408
- def as_dict(self) -> _DiagnosticData:
416
+ def as_dict(self, *, include_fix_source_hashes: bool = False) -> _DiagnosticData:
417
+ _require_instance(include_fix_source_hashes, bool, "include fix source hashes")
409
418
  result: _DiagnosticData = {
410
419
  "code": self.code,
411
420
  "message": self.message,
@@ -425,7 +434,7 @@ class Diagnostic:
425
434
  if self.notes:
426
435
  result["notes"] = list(self.notes)
427
436
  if self.fixes:
428
- result["fixes"] = [item.as_dict() for item in self.fixes]
437
+ result["fixes"] = [item.as_dict(include_fix_source_hashes=include_fix_source_hashes) for item in self.fixes]
429
438
  if self.tags:
430
439
  result["tags"] = list(self.tags)
431
440
  if self.fingerprint is not None:
@@ -581,7 +590,8 @@ class AnalysisReport:
581
590
  def ok(self) -> bool:
582
591
  return self.exit_code == 0
583
592
 
584
- def as_dict(self) -> _AnalysisReport:
593
+ def as_dict(self, *, include_fix_source_hashes: bool = False) -> _AnalysisReport:
594
+ _require_instance(include_fix_source_hashes, bool, "include fix source hashes")
585
595
  rules: dict[str, RuleDescriptor] = {}
586
596
  for item in self.diagnostics:
587
597
  key = f"{item.source}:{item.rule_id or item.code}"
@@ -599,7 +609,9 @@ class AnalysisReport:
599
609
  "conclusion": self.conclusion.value,
600
610
  "exitCode": self.exit_code,
601
611
  "rules": [rules[key].as_dict() for key in sorted(rules)],
602
- "diagnostics": [item.as_dict() for item in self.diagnostics],
612
+ "diagnostics": [
613
+ item.as_dict(include_fix_source_hashes=include_fix_source_hashes) for item in self.diagnostics
614
+ ],
603
615
  "issues": [item.as_dict() for item in self.issues],
604
616
  "tools": [item.as_dict() for item in self.tools],
605
617
  "coverage": [item.as_dict() for item in self.coverage],
@@ -628,6 +640,18 @@ def _edit_key(edit: TextEdit) -> _EditKey:
628
640
  return _EditKey(edit.location.path, region.start.byte_offset, region.end.byte_offset)
629
641
 
630
642
 
643
+ def _require_matching_edit_sources(edits: tuple[TextEdit, ...]) -> None:
644
+ source_hashes: dict[str, str] = {}
645
+ for edit in edits:
646
+ digest = edit.expected_source_hash
647
+ if digest is None:
648
+ continue
649
+ path = edit.location.path
650
+ if source_hashes.setdefault(path, digest) != digest:
651
+ msg = f"fix edits for {path} must refer to the same source revision"
652
+ raise ValueError(msg)
653
+
654
+
631
655
  def _validate_help_url(value: str | None) -> None:
632
656
  if value is None:
633
657
  return
@@ -660,6 +684,13 @@ def _require_text(value: object, label: str, *, allow_empty: bool = False) -> No
660
684
  raise ValueError(msg)
661
685
 
662
686
 
687
+ def _require_sha256(value: str, label: str) -> None:
688
+ _require_text(value, label)
689
+ if len(value) != _SHA256_HEX_LENGTH or any(char not in "0123456789abcdef" for char in value):
690
+ msg = f"{label} must be a lowercase SHA-256 digest"
691
+ raise ValueError(msg)
692
+
693
+
663
694
  def _require_int(value: int, label: str) -> None:
664
695
  if type(value) is not int:
665
696
  msg = f"{label} must be an integer"
@@ -25,8 +25,11 @@ class _DiagnosticPriority(NamedTuple):
25
25
  _GITHUB_ANNOTATION_LIMIT = 10
26
26
 
27
27
 
28
- def to_json(report: AnalysisReport, *, indent: int | None = 2) -> str:
29
- return json.dumps(report.as_dict(), indent=indent, sort_keys=True) + "\n"
28
+ def to_json(report: AnalysisReport, *, indent: int | None = 2, include_fix_source_hashes: bool = False) -> str:
29
+ return (
30
+ json.dumps(report.as_dict(include_fix_source_hashes=include_fix_source_hashes), indent=indent, sort_keys=True)
31
+ + "\n"
32
+ )
30
33
 
31
34
 
32
35
  def to_sarif(report: AnalysisReport) -> str:
@@ -7,6 +7,8 @@ from pathlib import Path
7
7
  import re
8
8
  from typing import TYPE_CHECKING, NamedTuple
9
9
 
10
+ from sarj_rule_contracts import SourceSnapshot
11
+
10
12
  from .models import Position, Region
11
13
 
12
14
 
@@ -28,7 +30,7 @@ class SourceDocument:
28
30
  _lines: tuple[str, ...] = field(init=False, repr=False)
29
31
  _line_byte_offsets: tuple[int, ...] = field(init=False, repr=False)
30
32
  _byte_length: int = field(init=False, repr=False)
31
- _utf16_indexes: dict[int, _Utf16Index] = field(init=False, repr=False)
33
+ _utf16_indexes: dict[int, _Utf16Index] = field(init=False, repr=False, compare=False)
32
34
 
33
35
  def __post_init__(self) -> None:
34
36
  lines = tuple(re.findall(r".*?(?:\r\n|\r|\n)|.+\Z", self.text, flags=re.DOTALL)) or ("",)
@@ -46,7 +48,11 @@ class SourceDocument:
46
48
 
47
49
  @classmethod
48
50
  def read(cls, path: Path) -> Self:
49
- return cls(path, path.read_bytes().decode("utf-8", errors="surrogateescape"))
51
+ return cls.from_snapshot(SourceSnapshot.read(path))
52
+
53
+ @classmethod
54
+ def from_snapshot(cls, snapshot: SourceSnapshot) -> Self:
55
+ return cls(snapshot.path, snapshot.decode("utf-8", errors="surrogateescape").text)
50
56
 
51
57
  def point(self, *, line: int, column: int) -> Position | None:
52
58
  if line < 1 or column < 1 or line > len(self._lines):
@@ -22,11 +22,11 @@ from sarj_standards.libs.diagnostics import (
22
22
  ToolReport,
23
23
  )
24
24
  from sarj_standards.libs.filesystem import read_prefix
25
- from sarj_standards.libs.linting.external import run_process
25
+ from sarj_standards.libs.linting.external import DEFAULT_EXECUTOR, process_request
26
26
 
27
27
 
28
28
  if TYPE_CHECKING:
29
- from sarj_standards.libs.linting.external import ProcessRunner
29
+ from sarj_standards.libs.linting.external import ProcessExecutor
30
30
 
31
31
  _SHA256 = "7408ae0434ecd92441b1bd32a267e181231493e3f8319ad94655fa781c8ba26a"
32
32
  _MAX_SOURCE_BYTES = 2 * 1024 * 1024
@@ -55,7 +55,7 @@ class _Results(RootModel[list[_Result]]):
55
55
 
56
56
 
57
57
  def analyze_actions(
58
- *, root: Path, paths: tuple[str, ...], runner: ProcessRunner = run_process
58
+ *, root: Path, paths: tuple[str, ...], runner: ProcessExecutor = DEFAULT_EXECUTOR
59
59
  ) -> tuple[ToolReport, ...]:
60
60
  selected = tuple(dict.fromkeys(name for name in paths if Path(name).name in {"action.yml", "action.yaml"}))
61
61
  if not selected:
@@ -73,7 +73,7 @@ def analyze_actions(
73
73
  )
74
74
 
75
75
 
76
- def _analyze_selected(root: Path, selected: tuple[str, ...], *, runner: ProcessRunner) -> tuple[ToolReport, ...]:
76
+ def _analyze_selected(root: Path, selected: tuple[str, ...], *, runner: ProcessExecutor) -> tuple[ToolReport, ...]:
77
77
  reports: list[ToolReport] = []
78
78
  documents: dict[str, SourceDocument] = {}
79
79
  size = 0
@@ -105,7 +105,7 @@ def _document(root: Path, name: str) -> SourceDocument:
105
105
 
106
106
 
107
107
  def _analyze_batch(
108
- root: Path, documents: dict[str, SourceDocument], *, runner: ProcessRunner
108
+ root: Path, documents: dict[str, SourceDocument], *, runner: ProcessExecutor
109
109
  ) -> tuple[ToolReport, ...]:
110
110
  bundle = CONFIGS_DIR / "action-parser.cjs"
111
111
  if hashlib.sha256(bundle.read_bytes()).hexdigest() != _SHA256:
@@ -120,7 +120,7 @@ def _analyze_batch(
120
120
  encoding="utf-8",
121
121
  )
122
122
  argv = (str(node), str(bundle), str(source))
123
- output = run_process(argv, cwd=root, timeout_seconds=30) if runner is run_process else runner(argv, cwd=root)
123
+ output = runner.run(process_request(argv, cwd=root, timeout_seconds=30))
124
124
  if output.returncode:
125
125
  message = f"GitHub action parser failed: {output.stderr.strip()[-_MAX_FAILURE_CHARS:]}"
126
126
  raise ValueError(message)