maintainability-agent 3.0.2__tar.gz → 3.2.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 (376) hide show
  1. {maintainability_agent-3.0.2/src/maintainability_agent.egg-info → maintainability_agent-3.2.0}/PKG-INFO +2 -2
  2. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/README.md +1 -1
  3. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/pyproject.toml +1 -1
  4. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0/src/maintainability_agent.egg-info}/PKG-INFO +2 -2
  5. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_agent.egg-info/SOURCES.txt +15 -0
  6. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/__init__.py +1 -1
  7. maintainability_agent-3.2.0/src/maintainability_audit/_anchor.py +73 -0
  8. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_assets/standard.md +39 -18
  9. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_banner.py +23 -3
  10. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_catalog.py +22 -2
  11. maintainability_agent-3.2.0/src/maintainability_audit/_delegated_pillar.py +181 -0
  12. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_derive.py +2 -1
  13. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_discovery.py +85 -19
  14. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_environment.py +54 -2
  15. maintainability_agent-3.2.0/src/maintainability_audit/_evidence_reader.py +305 -0
  16. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_evidence_view.py +11 -4
  17. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_first_run.py +57 -16
  18. maintainability_agent-3.2.0/src/maintainability_audit/_grammar.py +93 -0
  19. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_grant_ledger.py +0 -8
  20. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_history_view.py +85 -24
  21. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_html_report_sections.py +78 -31
  22. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_html_view.py +5 -3
  23. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_in_loop.py +47 -1
  24. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_in_loop_view.py +32 -15
  25. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_metric_adapters.py +14 -127
  26. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_pillars.py +77 -1
  27. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_practice.py +71 -25
  28. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_pressures.py +61 -70
  29. maintainability_agent-3.0.2/src/maintainability_audit/prompts.py → maintainability_agent-3.2.0/src/maintainability_audit/_prompt_sections.py +40 -114
  30. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_ranges_core.py +44 -0
  31. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_ranges_fortran.py +29 -11
  32. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_ranges_php.py +2 -28
  33. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_ranges_rust.py +2 -31
  34. maintainability_agent-3.2.0/src/maintainability_audit/_ratio_adapters.py +153 -0
  35. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_scan_history.py +36 -4
  36. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_scan_view.py +46 -18
  37. maintainability_agent-3.2.0/src/maintainability_audit/_selection.py +177 -0
  38. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_semantic_policy.py +18 -8
  39. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_semantic_ts.py +14 -4
  40. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_setup_persist.py +47 -3
  41. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_test_commands.py +23 -2
  42. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_test_execution.py +23 -2
  43. maintainability_agent-3.2.0/src/maintainability_audit/_test_pairing.py +291 -0
  44. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_trends.py +1 -0
  45. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_verdict_adapters.py +50 -36
  46. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_work_order.py +39 -27
  47. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/config.py +53 -2
  48. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/evidence.py +1 -227
  49. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/idioms.py +22 -6
  50. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/metrics.py +38 -10
  51. maintainability_agent-3.2.0/src/maintainability_audit/prompts.py +128 -0
  52. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/report.py +25 -1
  53. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/scoring.py +2 -5
  54. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_analyzer_primary_estimate.py +1 -1
  55. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_analyzer_scoring_path.py +3 -3
  56. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_analyzer_spread_range.py +1 -1
  57. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_anchor_disclosure.py +1 -1
  58. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_band_concealment_p3.py +2 -1
  59. maintainability_agent-3.2.0/tests/test_check_refuses_plus_only_fragments.py +38 -0
  60. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_consumer_migration.py +2 -1
  61. maintainability_agent-3.2.0/tests/test_declarative_parsers.py +220 -0
  62. maintainability_agent-3.2.0/tests/test_delegated_security_pillar.py +242 -0
  63. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_evidence_invariants.py +1 -1
  64. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_evidence_normalization.py +1 -1
  65. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_evidence_properties.py +1 -1
  66. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_history_window.py +10 -25
  67. maintainability_agent-3.2.0/tests/test_pairing_reads_imports.py +213 -0
  68. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_population_floors.py +1 -1
  69. maintainability_agent-3.2.0/tests/test_release_needs_an_empty_ledger.py +150 -0
  70. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_release_plan_status.py +8 -1
  71. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_scan_scope.py +2 -6
  72. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_scoring_calibration.py +2 -5
  73. maintainability_agent-3.2.0/tests/test_terminal_asks_the_same_questions.py +98 -0
  74. maintainability_agent-3.2.0/tests/test_tree_cannot_opt_the_host_in.py +111 -0
  75. maintainability_agent-3.2.0/tests/test_tree_reads_use_the_primitive.py +167 -0
  76. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_trends.py +14 -1
  77. maintainability_agent-3.2.0/tests/test_unanchored_set_matches_corpus.py +129 -0
  78. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_verified_grade.py +1 -1
  79. maintainability_agent-3.2.0/tests/test_work_order_binds_to_this_interpreter.py +72 -0
  80. maintainability_agent-3.0.2/src/maintainability_audit/_anchor.py +0 -49
  81. maintainability_agent-3.0.2/src/maintainability_audit/_selection.py +0 -136
  82. maintainability_agent-3.0.2/src/maintainability_audit/_test_pairing.py +0 -149
  83. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/LICENSE +0 -0
  84. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/setup.cfg +0 -0
  85. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_agent.egg-info/dependency_links.txt +0 -0
  86. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_agent.egg-info/entry_points.txt +0 -0
  87. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_agent.egg-info/requires.txt +0 -0
  88. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_agent.egg-info/top_level.txt +0 -0
  89. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/__main__.py +0 -0
  90. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_adapters.py +0 -0
  91. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_analysis.py +0 -0
  92. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_analyzer_sections.py +0 -0
  93. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_arguments.py +0 -0
  94. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_aspects.py +0 -0
  95. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_assets/analyzer-catalog.json +0 -0
  96. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_attestation.py +0 -0
  97. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_backfill.py +0 -0
  98. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_bands.py +0 -0
  99. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_built_ins.py +0 -0
  100. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_calibration.py +0 -0
  101. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_charts.py +0 -0
  102. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_cognitive.py +0 -0
  103. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_config_defaults.py +0 -0
  104. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_conformance.py +0 -0
  105. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_corroborate.py +0 -0
  106. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_coverage_notes.py +0 -0
  107. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_criteria_scope.py +0 -0
  108. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_documents.py +0 -0
  109. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_economics.py +0 -0
  110. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_economics_view.py +0 -0
  111. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_finding_match.py +0 -0
  112. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_formula.py +0 -0
  113. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_gates.py +0 -0
  114. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_generic.py +0 -0
  115. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_hostile_prompt.py +0 -0
  116. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_hotspots.py +0 -0
  117. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_identity.py +0 -0
  118. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_jvm_adapters.py +0 -0
  119. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_markdown_sections.py +0 -0
  120. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_masking.py +0 -0
  121. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_mcp_audit.py +0 -0
  122. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_mcp_gate.py +0 -0
  123. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_mcp_grants.py +0 -0
  124. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_mcp_refusals.py +0 -0
  125. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_mcp_resources.py +0 -0
  126. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_mcp_setup.py +0 -0
  127. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_metrics_types.py +0 -0
  128. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_operator_reads.py +0 -0
  129. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_precommit.py +0 -0
  130. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_precommit_install.py +0 -0
  131. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_precommit_view.py +0 -0
  132. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_ranges_c.py +0 -0
  133. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_ranges_cobol.py +0 -0
  134. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_ranges_cpp.py +0 -0
  135. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_ranges_csharp.py +0 -0
  136. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_ranges_go.py +0 -0
  137. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_ranges_java.py +0 -0
  138. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_ranges_js.py +0 -0
  139. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_ranges_ruby.py +0 -0
  140. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_ranges_swift.py +0 -0
  141. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_ratchet.py +0 -0
  142. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_recurrence.py +0 -0
  143. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_run_comparison.py +0 -0
  144. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_runner.py +0 -0
  145. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_safe_write.py +0 -0
  146. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_second_source.py +0 -0
  147. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_semantic.py +0 -0
  148. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_semantic_view.py +0 -0
  149. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_setup_errors.py +0 -0
  150. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_skill_data/SKILL.md +0 -0
  151. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_skill_data/agents/anthropic.yaml +0 -0
  152. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_skill_data/agents/copilot.yaml +0 -0
  153. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_skill_data/agents/openai.yaml +0 -0
  154. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_skill_data/copilot/maintainability-agent.prompt.md +0 -0
  155. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_skill_data/references/finding-taxonomy.md +0 -0
  156. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_skill_install.py +0 -0
  157. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_stored_grants.py +0 -0
  158. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_tdd_view.py +0 -0
  159. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_tokens.py +0 -0
  160. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_tool_adapters.py +0 -0
  161. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_user_config.py +0 -0
  162. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_verification.py +0 -0
  163. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_work_order_view.py +0 -0
  164. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_work_order_weights.py +0 -0
  165. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/_xml.py +0 -0
  166. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/baseline.py +0 -0
  167. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/cli.py +0 -0
  168. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/deadcode.py +0 -0
  169. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/declarations.py +0 -0
  170. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/duplication.py +0 -0
  171. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/git_tools.py +0 -0
  172. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/history.py +0 -0
  173. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/instructions.py +0 -0
  174. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/mcp_server.py +0 -0
  175. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/renderers.py +0 -0
  176. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/sarif.py +0 -0
  177. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/similarity.py +0 -0
  178. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/src/maintainability_audit/source.py +0 -0
  179. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_acquisition_trust.py +0 -0
  180. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_adapter_docs.py +0 -0
  181. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_adapter_recordings.py +0 -0
  182. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_adapters.py +0 -0
  183. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_added_lines.py +0 -0
  184. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_analysis_coverage.py +0 -0
  185. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_analysis_execution.py +0 -0
  186. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_analyzer_bridge.py +0 -0
  187. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_analyzer_config_isolation.py +0 -0
  188. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_analyzer_estimate_claims.py +0 -0
  189. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_analyzer_pinning.py +0 -0
  190. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_analyzer_provenance.py +0 -0
  191. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_analyzer_provenance_exclusions.py +0 -0
  192. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_analyzer_xml_bounds.py +0 -0
  193. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_anchor_and_mode.py +0 -0
  194. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_annotations_match_behaviour.py +0 -0
  195. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_anticipated_refusals.py +0 -0
  196. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_architecture.py +0 -0
  197. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_artifact_write_route_class.py +0 -0
  198. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_aspect_coverage.py +0 -0
  199. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_asset_snapshot_class.py +0 -0
  200. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_ast_reading_resolves_spellings.py +0 -0
  201. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_attestation.py +0 -0
  202. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_audit_components.py +0 -0
  203. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_authorization_freshness.py +0 -0
  204. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_authorship_gates.py +0 -0
  205. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_backfill.py +0 -0
  206. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_band_concealment_class.py +0 -0
  207. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_band_pressures.py +0 -0
  208. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_bands.py +0 -0
  209. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_body_or_signature_class.py +0 -0
  210. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_c_declarations.py +0 -0
  211. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_calibration_corpus.py +0 -0
  212. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_calibration_drift.py +0 -0
  213. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_calibration_tooling.py +0 -0
  214. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_catalog_selection.py +0 -0
  215. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_catalog_tier_promise.py +0 -0
  216. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_chat_primary_docs.py +0 -0
  217. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_checkstyle_adapter.py +0 -0
  218. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_checkstyle_catalog.py +0 -0
  219. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_ci_history_recipe.py +0 -0
  220. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_ci_installs_the_analyzer_pool.py +0 -0
  221. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_claimed_languages.py +0 -0
  222. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_cli.py +0 -0
  223. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_clone_group_class.py +0 -0
  224. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_cobol_declarations.py +0 -0
  225. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_cognitive_complexity.py +0 -0
  226. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_cohort_statistics.py +0 -0
  227. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_concealment_properties.py +0 -0
  228. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_config_shape.py +0 -0
  229. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_consent_and_grant.py +0 -0
  230. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_contract_claims.py +0 -0
  231. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_corroborate.py +0 -0
  232. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_coverage_by_language.py +0 -0
  233. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_cpp_declarations.py +0 -0
  234. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_criteria_completion.py +0 -0
  235. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_csharp_declarations.py +0 -0
  236. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_d15_composition.py +0 -0
  237. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_d15_goal_directed.py +0 -0
  238. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_dead_code.py +0 -0
  239. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_declaration_criteria_scope.py +0 -0
  240. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_declaration_edge_cases.py +0 -0
  241. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_declaration_grading.py +0 -0
  242. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_declaration_ranges.py +0 -0
  243. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_declaration_source.py +0 -0
  244. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_declared_authorship.py +0 -0
  245. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_declared_imports.py +0 -0
  246. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_derivation_mix.py +0 -0
  247. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_determinism.py +0 -0
  248. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_dimension_ratchet.py +0 -0
  249. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_direct_runner_availability_class.py +0 -0
  250. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_discovery.py +0 -0
  251. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_doc_claims.py +0 -0
  252. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_docs_links.py +0 -0
  253. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_economic_context.py +0 -0
  254. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_empty_output_not_clean.py +0 -0
  255. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_empty_output_not_clean_adapters.py +0 -0
  256. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_environment_work_order.py +0 -0
  257. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_exclusion_dialects.py +0 -0
  258. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_falsifier_standard.py +0 -0
  259. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_file_list_cap_class.py +0 -0
  260. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_finding_identity.py +0 -0
  261. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_first_run_elicitation.py +0 -0
  262. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_first_run_prompt.py +0 -0
  263. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_first_run_reelicitation.py +0 -0
  264. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_fix_breadth_window.py +0 -0
  265. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_fixed_form_fortran.py +0 -0
  266. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_format_ask.py +0 -0
  267. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_fortran_declarations.py +0 -0
  268. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_fortran_metrics.py +0 -0
  269. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_fortran_practice.py +0 -0
  270. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_git_argv.py +0 -0
  271. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_git_read_only.py +0 -0
  272. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_git_readonly_class.py +0 -0
  273. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_go_declarations.py +0 -0
  274. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_grammar_constructs.py +0 -0
  275. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_grammar_token_coverage.py +0 -0
  276. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_grant_only_user_tier.py +0 -0
  277. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_history.py +0 -0
  278. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_history_boundary.py +0 -0
  279. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_history_manifest.py +0 -0
  280. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_history_schema2.py +0 -0
  281. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_hostile_prompt.py +0 -0
  282. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_html_executive.py +0 -0
  283. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_html_section_parity_class.py +0 -0
  284. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_identity_docs.py +0 -0
  285. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_identity_resolution.py +0 -0
  286. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_idiom_divergence.py +0 -0
  287. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_in_loop_budgets.py +0 -0
  288. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_in_loop_check.py +0 -0
  289. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_installed_wheel_runtime.py +0 -0
  290. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_instruction_targets.py +0 -0
  291. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_java_range_guard.py +0 -0
  292. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_java_ranges.py +0 -0
  293. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_java_scored.py +0 -0
  294. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_java_wiring.py +0 -0
  295. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_js_complexity_operators.py +0 -0
  296. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_js_declarations.py +0 -0
  297. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_language_coverage.py +0 -0
  298. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_language_support_honesty.py +0 -0
  299. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_mcp_baseline_payload.py +0 -0
  300. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_mcp_history.py +0 -0
  301. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_mcp_server.py +0 -0
  302. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_migration_1_0.py +0 -0
  303. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_near_duplicates.py +0 -0
  304. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_network_disclosure.py +0 -0
  305. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_one_setup_across_surfaces_class.py +0 -0
  306. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_operator_named_paths.py +0 -0
  307. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_operator_named_reads.py +0 -0
  308. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_pattern_budget.py +0 -0
  309. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_phase6_claims.py +0 -0
  310. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_php_declarations.py +0 -0
  311. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_pillars_and_practice.py +0 -0
  312. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_platform_claim.py +0 -0
  313. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_pmd_adapter.py +0 -0
  314. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_pmd_audit_closures.py +0 -0
  315. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_policy_claims.py +0 -0
  316. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_pool_runs_by_config.py +0 -0
  317. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_practice_nested_gates_class.py +0 -0
  318. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_precommit_staged.py +0 -0
  319. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_presentation_refusals.py +0 -0
  320. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_promises.py +0 -0
  321. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_prompt_escalation_once.py +0 -0
  322. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_prompt_honesty.py +0 -0
  323. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_prompt_location_escalation_class.py +0 -0
  324. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_prove_falsifiers.py +0 -0
  325. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_python_complexity.py +0 -0
  326. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_python_declarations.py +0 -0
  327. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_read_boundary.py +0 -0
  328. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_readme_claims.py +0 -0
  329. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_recurrence.py +0 -0
  330. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_release_plan.py +0 -0
  331. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_report_remedy_copy.py +0 -0
  332. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_roles_recorded.py +0 -0
  333. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_root_grants.py +0 -0
  334. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_rubric_ignores_project_lint_config.py +0 -0
  335. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_ruby_declarations.py +0 -0
  336. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_run_comparison.py +0 -0
  337. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_runner.py +0 -0
  338. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_rust_declarations.py +0 -0
  339. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_sarif.py +0 -0
  340. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_scan_history.py +0 -0
  341. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_scanner_unsafe_direction.py +0 -0
  342. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_scanning.py +0 -0
  343. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_schema_fidelity.py +0 -0
  344. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_scope_conformance.py +0 -0
  345. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_semantic_policy.py +0 -0
  346. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_setup_gate_completeness.py +0 -0
  347. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_setup_gate_independent_contract.py +0 -0
  348. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_setup_precondition.py +0 -0
  349. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_skill_install.py +0 -0
  350. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_sonar_resolve.py +0 -0
  351. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_source_index.py +0 -0
  352. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_spotbugs_adapter.py +0 -0
  353. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_spotbugs_catalog.py +0 -0
  354. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_stage8_contract.py +0 -0
  355. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_suite_isolation.py +0 -0
  356. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_swift_declarations.py +0 -0
  357. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_tdd_structure.py +0 -0
  358. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_test_commands.py +0 -0
  359. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_test_execution_class.py +0 -0
  360. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_three_presentations.py +0 -0
  361. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_tool_resolution.py +0 -0
  362. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_tree_chosen_spawn.py +0 -0
  363. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_type_fields_not_functions_class.py +0 -0
  364. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_uat_falsifiers.py +0 -0
  365. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_unread_code.py +0 -0
  366. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_user_config_tier.py +0 -0
  367. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_verification_audit_closures.py +0 -0
  368. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_version_claim.py +0 -0
  369. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_wheel_contents.py +0 -0
  370. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_work_order.py +0 -0
  371. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_work_order_items.py +0 -0
  372. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_work_order_selection.py +0 -0
  373. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_workflow_supply_chain.py +0 -0
  374. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_write_boundary.py +0 -0
  375. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_write_route_class.py +0 -0
  376. {maintainability_agent-3.0.2 → maintainability_agent-3.2.0}/tests/test_written_record.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maintainability-agent
3
- Version: 3.0.2
3
+ Version: 3.2.0
4
4
  Summary: Maintainability gate and AI remediation prompt generator for CI.
5
5
  Author: Marshall Guillory
6
6
  License: MIT
@@ -45,7 +45,7 @@ Dynamic: license-file
45
45
  **A deterministic, offline maintainability audit whose output is a _bounded
46
46
  work order_ for an AI coding agent** — a copy-paste prompt, per finding, that
47
47
  says *fix exactly these and refactor nothing else*. Chat-primary; CLI for CI.
48
- Version **3.0.2**.
48
+ Version **3.2.0**.
49
49
 
50
50
  **Planned, not shipped:** the audit tool will become `maintainability-audit`;
51
51
  a new `maintainability-agent` companion will run it and automate human-selected
@@ -5,7 +5,7 @@
5
5
  **A deterministic, offline maintainability audit whose output is a _bounded
6
6
  work order_ for an AI coding agent** — a copy-paste prompt, per finding, that
7
7
  says *fix exactly these and refactor nothing else*. Chat-primary; CLI for CI.
8
- Version **3.0.2**.
8
+ Version **3.2.0**.
9
9
 
10
10
  **Planned, not shipped:** the audit tool will become `maintainability-audit`;
11
11
  a new `maintainability-agent` companion will run it and automate human-selected
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "maintainability-agent"
7
- version = "3.0.2"
7
+ version = "3.2.0"
8
8
  description = "Maintainability gate and AI remediation prompt generator for CI."
9
9
  readme = "README.md"
10
10
  # 3.11, not 3.10. `_discovery`, `_pillars` and `_runner` all import
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maintainability-agent
3
- Version: 3.0.2
3
+ Version: 3.2.0
4
4
  Summary: Maintainability gate and AI remediation prompt generator for CI.
5
5
  Author: Marshall Guillory
6
6
  License: MIT
@@ -45,7 +45,7 @@ Dynamic: license-file
45
45
  **A deterministic, offline maintainability audit whose output is a _bounded
46
46
  work order_ for an AI coding agent** — a copy-paste prompt, per finding, that
47
47
  says *fix exactly these and refactor nothing else*. Chat-primary; CLI for CI.
48
- Version **3.0.2**.
48
+ Version **3.2.0**.
49
49
 
50
50
  **Planned, not shipped:** the audit tool will become `maintainability-audit`;
51
51
  a new `maintainability-agent` companion will run it and automate human-selected
@@ -29,18 +29,21 @@ src/maintainability_audit/_conformance.py
29
29
  src/maintainability_audit/_corroborate.py
30
30
  src/maintainability_audit/_coverage_notes.py
31
31
  src/maintainability_audit/_criteria_scope.py
32
+ src/maintainability_audit/_delegated_pillar.py
32
33
  src/maintainability_audit/_derive.py
33
34
  src/maintainability_audit/_discovery.py
34
35
  src/maintainability_audit/_documents.py
35
36
  src/maintainability_audit/_economics.py
36
37
  src/maintainability_audit/_economics_view.py
37
38
  src/maintainability_audit/_environment.py
39
+ src/maintainability_audit/_evidence_reader.py
38
40
  src/maintainability_audit/_evidence_view.py
39
41
  src/maintainability_audit/_finding_match.py
40
42
  src/maintainability_audit/_first_run.py
41
43
  src/maintainability_audit/_formula.py
42
44
  src/maintainability_audit/_gates.py
43
45
  src/maintainability_audit/_generic.py
46
+ src/maintainability_audit/_grammar.py
44
47
  src/maintainability_audit/_grant_ledger.py
45
48
  src/maintainability_audit/_history_view.py
46
49
  src/maintainability_audit/_hostile_prompt.py
@@ -68,6 +71,7 @@ src/maintainability_audit/_precommit.py
68
71
  src/maintainability_audit/_precommit_install.py
69
72
  src/maintainability_audit/_precommit_view.py
70
73
  src/maintainability_audit/_pressures.py
74
+ src/maintainability_audit/_prompt_sections.py
71
75
  src/maintainability_audit/_ranges_c.py
72
76
  src/maintainability_audit/_ranges_cobol.py
73
77
  src/maintainability_audit/_ranges_core.py
@@ -82,6 +86,7 @@ src/maintainability_audit/_ranges_ruby.py
82
86
  src/maintainability_audit/_ranges_rust.py
83
87
  src/maintainability_audit/_ranges_swift.py
84
88
  src/maintainability_audit/_ratchet.py
89
+ src/maintainability_audit/_ratio_adapters.py
85
90
  src/maintainability_audit/_recurrence.py
86
91
  src/maintainability_audit/_run_comparison.py
87
92
  src/maintainability_audit/_runner.py
@@ -183,6 +188,7 @@ tests/test_calibration_tooling.py
183
188
  tests/test_catalog_selection.py
184
189
  tests/test_catalog_tier_promise.py
185
190
  tests/test_chat_primary_docs.py
191
+ tests/test_check_refuses_plus_only_fragments.py
186
192
  tests/test_checkstyle_adapter.py
187
193
  tests/test_checkstyle_catalog.py
188
194
  tests/test_ci_history_recipe.py
@@ -211,8 +217,10 @@ tests/test_declaration_edge_cases.py
211
217
  tests/test_declaration_grading.py
212
218
  tests/test_declaration_ranges.py
213
219
  tests/test_declaration_source.py
220
+ tests/test_declarative_parsers.py
214
221
  tests/test_declared_authorship.py
215
222
  tests/test_declared_imports.py
223
+ tests/test_delegated_security_pillar.py
216
224
  tests/test_derivation_mix.py
217
225
  tests/test_determinism.py
218
226
  tests/test_dimension_ratchet.py
@@ -279,6 +287,7 @@ tests/test_network_disclosure.py
279
287
  tests/test_one_setup_across_surfaces_class.py
280
288
  tests/test_operator_named_paths.py
281
289
  tests/test_operator_named_reads.py
290
+ tests/test_pairing_reads_imports.py
282
291
  tests/test_pattern_budget.py
283
292
  tests/test_phase6_claims.py
284
293
  tests/test_php_declarations.py
@@ -302,6 +311,7 @@ tests/test_python_declarations.py
302
311
  tests/test_read_boundary.py
303
312
  tests/test_readme_claims.py
304
313
  tests/test_recurrence.py
314
+ tests/test_release_needs_an_empty_ledger.py
305
315
  tests/test_release_plan.py
306
316
  tests/test_release_plan_status.py
307
317
  tests/test_report_remedy_copy.py
@@ -333,14 +343,18 @@ tests/test_stage8_contract.py
333
343
  tests/test_suite_isolation.py
334
344
  tests/test_swift_declarations.py
335
345
  tests/test_tdd_structure.py
346
+ tests/test_terminal_asks_the_same_questions.py
336
347
  tests/test_test_commands.py
337
348
  tests/test_test_execution_class.py
338
349
  tests/test_three_presentations.py
339
350
  tests/test_tool_resolution.py
351
+ tests/test_tree_cannot_opt_the_host_in.py
340
352
  tests/test_tree_chosen_spawn.py
353
+ tests/test_tree_reads_use_the_primitive.py
341
354
  tests/test_trends.py
342
355
  tests/test_type_fields_not_functions_class.py
343
356
  tests/test_uat_falsifiers.py
357
+ tests/test_unanchored_set_matches_corpus.py
344
358
  tests/test_unread_code.py
345
359
  tests/test_user_config_tier.py
346
360
  tests/test_verification_audit_closures.py
@@ -348,6 +362,7 @@ tests/test_verified_grade.py
348
362
  tests/test_version_claim.py
349
363
  tests/test_wheel_contents.py
350
364
  tests/test_work_order.py
365
+ tests/test_work_order_binds_to_this_interpreter.py
351
366
  tests/test_work_order_items.py
352
367
  tests/test_work_order_selection.py
353
368
  tests/test_workflow_supply_chain.py
@@ -1,3 +1,3 @@
1
1
  """Maintainability Audit CI."""
2
2
 
3
- __version__ = "3.0.2"
3
+ __version__ = "3.2.0"
@@ -0,0 +1,73 @@
1
+ """What the reference corpus does not hold, and how to say it.
2
+
3
+ Rubric data, like `_calibration`: a fact about the anchor rather than a
4
+ computation over a repository. It lives beside the constant it qualifies
5
+ because the two go stale together — a recalibration that adds Swift and
6
+ COBOL to the corpus empties this in the same commit that moves
7
+ `CALIBRATION_C`.
8
+
9
+ **Unanchored is policy, not an oversight.** Under the corpus policy
10
+ decided with 2.4.1, a language ships parsed and the corpus is re-measured
11
+ once after the remaining scanners land: adding a scanner moves
12
+ `scanner_fingerprint` and invalidates every stored measurement, so paying
13
+ per language means paying in full, repeatedly, for an anchor that is
14
+ obsolete again at the next one.
15
+
16
+ What is *not* policy is silence about it where the grade is read. The
17
+ report carried `score.reference` and the note said the corpus spanned
18
+ "every language this scanner parses" — which was true when it was written
19
+ and false from 2.4.0 onward, for three releases. A Grok audit on
20
+ 2026-09-04 named it as the second instance of a shape this project had
21
+ already shipped once: a limit disclosed only in JSON is disclosed nowhere
22
+ that matters.
23
+ """
24
+ from __future__ import annotations
25
+
26
+ #: Parsed by a scanner, absent from the reference corpus.
27
+ #:
28
+ #: Stated rather than derived, and the reason still holds:
29
+ #: `tools/calibration/corpus.json` is a repository file that does not ship
30
+ #: in the wheel, so a runtime read would be unavailable exactly where a
31
+ #: user reads a grade. What a stated list buys in availability it owes in
32
+ #: upkeep, and it did not pay.
33
+ #:
34
+ #: **D143.** This read `("Swift", "COBOL", "Go", "Rust", "PHP", "Ruby")`
35
+ #: after 3.0.0 anchored five of those six. The corpus grew from 112
36
+ #: repositories to 180 and from eight measured languages to thirteen, and
37
+ #: this line did not move — so every skin told a Swift, Go, Rust, PHP or
38
+ #: Ruby reader their grade was provisional against a corpus that had
39
+ #: measured their language for two releases. A caveat that names an
40
+ #: anchored language is not a caution, it is a false statement about the
41
+ #: evidence, and it undersells the thing the recalibration was for.
42
+ #:
43
+ #: COBOL stays, and stays alone. It is parsed and deliberately never
44
+ #: anchored: GitHub holds one COBOL repository above 500 stars and what is
45
+ #: there is curriculum, Java parsers and JavaScript wrappers rather than
46
+ #: COBOL codebases, so the corpus policy excludes it permanently rather
47
+ #: than temporarily. Emptying this tuple would delete a disclosure that is
48
+ #: still true.
49
+ #:
50
+ #: `tests/test_unanchored_set_matches_corpus.py` recomputes
51
+ #: `parsed - corpus` from `DECLARATION_SUFFIXES` and `corpus.json` and
52
+ #: fails if this stops matching — including when a language is *added* to
53
+ #: the corpus, which is the direction that went unnoticed.
54
+ UNANCHORED_LANGUAGES: tuple[str, ...] = ("COBOL",)
55
+
56
+
57
+ def unanchored_names() -> str:
58
+ """The languages, joined for a sentence. Empty when there are none."""
59
+ return " and ".join(UNANCHORED_LANGUAGES)
60
+
61
+
62
+ def unanchored_sentence() -> str:
63
+ """One sentence naming what the anchor omits, for the corpus note."""
64
+ if not UNANCHORED_LANGUAGES:
65
+ return ""
66
+ from ._grammar import agreement
67
+
68
+ say = agreement(len(UNANCHORED_LANGUAGES))
69
+ return (
70
+ f"{unanchored_names()} {say.verb} parsed but absent from it, so "
71
+ f"{say.possessive} findings are as good as {say.possessive} "
72
+ f"parser while {say.possessive} grade is provisional."
73
+ )
@@ -118,10 +118,20 @@ The corpus median lands at **4.0 (B)**: a well-run real codebase earns a B, and
118
118
 
119
119
  ### The reference corpus
120
120
 
121
- Calibration is reproducible, not a snapshot someone took once. The corpus is defined in [`tools/calibration/corpus.json`](../tools/calibration/corpus.json) — 40 mature open-source repositories **pinned to exact commits**, spanning 32 to 18,789 source files and 463,581 declarations across Python, TypeScript and JavaScript:
121
+ Calibration is reproducible, not a snapshot someone took once. The corpus is defined in [`tools/calibration/corpus.json`](../tools/calibration/corpus.json) — **180 mature open-source repositories pinned to exact commits**, spanning 24 to 18,789 source files and 2,749,575 declarations across thirteen languages: Python, TypeScript, JavaScript, Java, C, C++, C#, Fortran, Swift, Go, Rust, PHP and Ruby.
122
+
123
+ The forty repositories 1.x calibrated on are all still in it, at the commits they were measured at then:
122
124
 
123
125
  > angular · ansible · ant-design · anime · axios · Chart.js · code-server · django · echarts · excalidraw · express · fastapi · flask · freeCodeCamp · github-readme-stats · hackingtool · hoppscotch · jquery · keras · localstack · lodash · manim · material-ui · mermaid · models · n8n · nest · playwright · reveal.js · scrapy · strapi · svelte · tabby · tailwindcss · transformers · uBlock · vite · webpack · youtube-dl · yt-dlp
124
126
 
127
+ **This paragraph opened "40 mature open-source repositories … across
128
+ Python, TypeScript and JavaScript" for two releases after 3.0.0 measured
129
+ 180 across thirteen (D143).** The corrected numbers were four paragraphs
130
+ below it the whole time, so a reader met both and had nothing to tell
131
+ them which was current. Same defect as the unanchored caveat below and
132
+ found in the same pass: a count written into prose, with nothing that
133
+ fails when the thing it counts moves.
134
+
125
135
  **What the anchor does not cover.** For five releases this section named a
126
136
  real defect: the corpus was 40 repositories of Python, TypeScript and
127
137
  JavaScript while the scanner parsed eight languages, so Java, C, C++, C#
@@ -131,23 +141,34 @@ median and fortran-lang/stdlib at **1.10x** — the first a true statement
131
141
  about LAPACK relative to mature OSS *web* code, and not a statement about
132
142
  typical Fortran, because no typical Fortran was in the comparison set.
133
143
 
134
- **2.0.0 extended the corpus rather than the caveat.** It is now 112
135
- repositories spanning eight languages: Python, TypeScript, JavaScript,
136
- Java, C, C++, C# and Fortran. The original 40 rows are unchanged and still
137
- pinned to the commits they were measured at, so what moved the constants
138
- is the languages added and nothing else.
139
-
140
- **Six parsed languages are not in it.** Swift (2.4.0), COBOL (2.7.0)
141
- and Go, Rust, PHP and Ruby (2.11.0) shipped after that measurement and
142
- are **unanchored**: a scanner reads them, and the medians their rates are
143
- compared against were measured on other languages. This is the corpus policy decided with 2.4.1 — a
144
- language ships parsed, and the corpus is re-measured once after the
145
- remaining scanners land, because a new scanner moves
146
- `scanner_fingerprint` and invalidates every stored row. The cost is
147
- disclosed rather than absorbed: findings in those languages are as good
148
- as their parser, their grade is provisional, and every surface that
149
- prints a grade says so. This sentence read "every language this scanner
150
- parses" for three releases after it stopped being true.
144
+ **3.0.0 extended the corpus.** It is now **180 repositories spanning
145
+ thirteen languages**: Python, TypeScript, JavaScript, Java, C, C++, C#,
146
+ Fortran, Swift, Go, Rust, PHP and Ruby. The 112 rows measured for 2.0.0
147
+ are unchanged and still pinned to the commits they were measured at, so
148
+ what moved the constants is the languages added and nothing else.
149
+
150
+ **One parsed language is not in it: COBOL.** It is **unanchored** — a
151
+ scanner reads it, and the medians its rates are compared against were
152
+ measured on other languages. The cost is disclosed rather than absorbed:
153
+ findings in COBOL are as good as their parser, a COBOL grade is
154
+ provisional, and every surface that prints a grade says so.
155
+
156
+ COBOL is unanchored **permanently**, not pending a re-measure. Against
157
+ the corpus criteria GitHub holds one COBOL repository above 500 stars,
158
+ and what is public is curriculum, Java parsers and JavaScript wrappers
159
+ rather than COBOL codebases; the language's real corpus is on private
160
+ mainframes. Anchoring it on what exists would build a reference out of
161
+ teaching material — an anchor that *looks* like one, which is worse than
162
+ the disclosed absence.
163
+
164
+ Two corrections are recorded here rather than quietly applied, because
165
+ both were live for multiple releases. This sentence read "every language
166
+ this scanner parses" for three releases after that stopped being true.
167
+ It then named **six** unanchored languages for two releases after 3.0.0
168
+ anchored five of them (D143) — Swift, Go, Rust, PHP and Ruby readers were
169
+ told their grade was provisional against a corpus that had measured their
170
+ language. A caveat naming an anchored language is not a caution; it is a
171
+ false statement about the evidence.
151
172
 
152
173
  Three limits remain, and they are narrower than the one they replace:
153
174
 
@@ -17,9 +17,12 @@ one-way.
17
17
  """
18
18
  from __future__ import annotations
19
19
 
20
+ import os
20
21
  import re
21
22
  from pathlib import Path
22
23
 
24
+ from ._operator_reads import open_regular_file
25
+
23
26
  # Whatever a language uses to open a comment, plus decoration a generator
24
27
  # may pad the line with: `#`, `//`, `*`, `/*`, `!`, `--`, `<!--`, `;`, and
25
28
  # the emoji rules some tools draw. `@` is spared, or `# @generated` would
@@ -40,12 +43,29 @@ BANNER_LINES = 20
40
43
  def banner_says_generated(
41
44
  path: Path, banners: tuple[str, ...]
42
45
  ) -> str | None:
43
- """The banner text, if the head of this file carries one."""
46
+ """The banner text, if the head of this file carries one.
47
+
48
+ The head only, so this stays cheap on a large tree — but read
49
+ *through* a checked handle rather than by name, so a FIFO refuses
50
+ instead of stopping the audit dead (D145). `path.open()` on one waits
51
+ for a writer that never comes, and the previous `except OSError`
52
+ could not have caught that: there is no error to catch, only a
53
+ process that never returns.
54
+ """
44
55
  try:
45
- with path.open(encoding="utf-8", errors="replace") as handle:
46
- head = "".join(next(handle, "") for _ in range(BANNER_LINES)).lower()
56
+ handle = open_regular_file(
57
+ path,
58
+ "Banner detection reads the head of a discovered file; a "
59
+ "device, socket or FIFO would block the audit rather than "
60
+ "declare itself generated.",
61
+ )
47
62
  except OSError:
48
63
  return None
64
+ try:
65
+ with os.fdopen(handle, "r", encoding="utf-8", errors="replace", closefd=False) as opened:
66
+ head = "".join(next(opened, "") for _ in range(BANNER_LINES)).lower()
67
+ finally:
68
+ os.close(handle)
49
69
  previous: str | None = None
50
70
  for raw in head.splitlines():
51
71
  stripped = COMMENT_LEAD.sub("", raw).strip()
@@ -190,7 +190,23 @@ def decide(tool: dict[str, Any], settings: dict[str, Any]) -> Selection:
190
190
  3. the depth, licence and concern tiers.
191
191
  """
192
192
  slug = tool["slug"]
193
+ return (
194
+ _denial(tool, settings)
195
+ or (Selection(slug, True, "explicitly allowed by name")
196
+ if slug in settings["allow_tools"] else None)
197
+ or _tier_decision(tool, settings)
198
+ )
199
+
200
+
201
+ def _denial(tool: dict[str, Any], settings: dict[str, Any]) -> Selection | None:
202
+ """Step 1: every deny, which beats even an explicit allow.
193
203
 
204
+ `None` means nothing here refused the tool, not that it was admitted —
205
+ the caller has two more steps. Separate from the tiers because these
206
+ are the operator's own prohibitions and the tiers are policy: a deny
207
+ that could be overridden by an allow would not be a deny.
208
+ """
209
+ slug = tool["slug"]
194
210
  if slug in settings["deny_tools"]:
195
211
  return Selection(slug, False, "denied by name")
196
212
  if tool["license_class"] in settings["deny_license_classes"]:
@@ -198,10 +214,14 @@ def decide(tool: dict[str, Any], settings: dict[str, Any]) -> Selection:
198
214
  denied_concerns = set(tool["upstream_tags"]) & set(settings["deny_concerns"])
199
215
  if denied_concerns:
200
216
  return Selection(slug, False, f"concern {','.join(sorted(denied_concerns))} denied")
217
+ return None
201
218
 
202
- if slug in settings["allow_tools"]:
203
- return Selection(slug, True, "explicitly allowed by name")
204
219
 
220
+ def _tier_decision(tool: dict[str, Any], settings: dict[str, Any]) -> Selection:
221
+ """Step 3: the depth, licence and concern tiers, for a tool no deny
222
+ caught and no allow admitted. The only step that can end in `True`
223
+ on the tool's own merits."""
224
+ slug = tool["slug"]
205
225
  if tool["deprecated"]:
206
226
  return Selection(slug, False, "deprecated upstream")
207
227
  if not tool["languages"]:
@@ -0,0 +1,181 @@
1
+ """Reading a delegated pillar's own report of itself.
2
+
3
+ [ADR 007](adr-007-pillars-and-practice.md) makes the five pillars of the
4
+ source framework the top-level taxonomy and declares **Security
5
+ delegated to `secure-code-agent`**, reported as `NotApplicable` so that
6
+ silence is never read as safety. That entry was always a placeholder for
7
+ an artifact that did not exist yet: the other tool measures the pillar,
8
+ so the other tool has to be the one that reports it.
9
+
10
+ `secure-code-agent` 0.5.0 ships the emitter — `--security-pillar <path>`
11
+ writes `security-pillar.json`, schema `secure-code-agent/security-pillar`
12
+ — and it was built against *this* vocabulary rather than its own. The
13
+ scope words, the two-axis split and the posture matrix with its
14
+ thresholds all come from `_pillars.py`, because two tools saying "level
15
+ 3" or "healthy" about one repository have to mean the same thing by it
16
+ or the joined view is worse than either alone.
17
+
18
+ This module reads the document and checks it is the one expected. The
19
+ *shaping* of the pillar entry lives in `_pillars` beside the entries it
20
+ sits among — scoring may not import a scanner, and reading a path the
21
+ audited tree chooses is what makes this one. Neither half computes a
22
+ number: a delegated pillar's numbers belong to the tool that measured
23
+ them, and a consumer that recomputed anything would be a second opinion
24
+ nobody asked for.
25
+
26
+ **Absence is the normal case and stays the documented one.** No file
27
+ means the pillar reports exactly what it reported before — delegated,
28
+ `NotApplicable`, naming the other tool. That path is not a failure and
29
+ does not warn: most repositories run one tool.
30
+
31
+ **A file that cannot be trusted is refused rather than partly believed.**
32
+ Wrong schema, unreadable JSON, a shape that is not an object — each
33
+ returns `None`, and the pillar falls back to the honest placeholder. The
34
+ alternative is a security posture assembled from a document this tool
35
+ does not actually understand, which is worse than no posture at all.
36
+
37
+ A trailing note on where this sits: it reads a path from the audited
38
+ tree, so it is a scanner rather than part of the rubric.
39
+ `test_scoring_never_imports_scanners_or_assembly` holds that boundary,
40
+ and `report` is what joins the two.
41
+ """
42
+ from __future__ import annotations
43
+
44
+ import json
45
+ from pathlib import Path
46
+ from typing import Any
47
+
48
+ from ._operator_reads import PathNotAllowed, read_source_file
49
+
50
+ #: Written beside the other artifacts a repository keeps about itself.
51
+ #: The path is a convention rather than a search: a document this tool
52
+ #: trusts for a whole pillar should be somewhere a reader can find, not
53
+ #: wherever a glob happened to match.
54
+ DEFAULT_PILLAR_PATH = ".maintainability/security-pillar.json"
55
+
56
+ #: The contract this module understands. A document announcing anything
57
+ #: else is refused rather than read hopefully — the schema string is the
58
+ #: producer's promise about the shape, and guessing past it is how a
59
+ #: consumer starts reporting fields that mean something different.
60
+ SCHEMA = "secure-code-agent/security-pillar"
61
+ SCHEMA_VERSION = 1
62
+
63
+ #: What the pillar block takes from the document. Named rather than
64
+ #: copied wholesale so a field added upstream cannot silently appear in
65
+ #: this tool's report carrying meaning nobody here has checked.
66
+ CARRIED = (
67
+ "practice",
68
+ "condition",
69
+ "condition_letter",
70
+ "posture",
71
+ "verified_grade",
72
+ "evidence_status",
73
+ "evidence_reasons",
74
+ "coverage",
75
+ "findings_by_severity",
76
+ "reported_not_scored",
77
+ "loc_scanned",
78
+ )
79
+
80
+
81
+ def read_delegated(root: Path, relative: str = DEFAULT_PILLAR_PATH) -> dict[str, Any] | None:
82
+ """The delegated pillar's own report, or `None` when there is none.
83
+
84
+ `None` for every reason a reader might not get a usable document:
85
+ the file is absent, it is not a regular file, it does not parse, it
86
+ is not an object, or it announces a schema this tool does not know.
87
+ All five collapse to one answer because the caller does the same
88
+ thing with each — keep the placeholder that names the other tool.
89
+
90
+ Read through the regular-file primitive like every other path the
91
+ audited tree chooses (D145): a FIFO here would hang the audit rather
92
+ than fail it.
93
+ """
94
+ path = Path(root) / relative
95
+ if not path.exists():
96
+ return None
97
+ try:
98
+ payload = json.loads("\n".join(read_source_file(path)))
99
+ except (OSError, ValueError) as error:
100
+ if isinstance(error, PathNotAllowed):
101
+ raise
102
+ return None
103
+ if not isinstance(payload, dict):
104
+ return None
105
+ if payload.get("schema") != SCHEMA:
106
+ return None
107
+ if payload.get("schema_version") != SCHEMA_VERSION:
108
+ return None
109
+ return payload if _is_well_formed(payload) else None
110
+
111
+
112
+ #: Fields whose *shape* the consumer depends on. A header check alone
113
+ #: accepts any object announcing the right schema, and the pillar then
114
+ #: reads these as if the producer had promised them.
115
+ _SHAPES: tuple[tuple[str, type | tuple[type, ...]], ...] = (
116
+ ("producer", dict),
117
+ ("practice", dict),
118
+ ("condition", (int, float, type(None))),
119
+ ("posture", (str, type(None))),
120
+ ("evidence_status", (str, type(None))),
121
+ ("coverage", (dict, type(None))),
122
+ ("findings_by_severity", (dict, type(None))),
123
+ ("reported_not_scored", (dict, type(None))),
124
+ )
125
+
126
+ #: The postures this tool knows how to render. A document naming one it
127
+ #: does not is refused rather than printed through: the word is the
128
+ #: reader's whole summary of the pillar.
129
+ _POSTURES = frozenset({"healthy", "managed debt", "unmanaged debt", "unverified"})
130
+
131
+
132
+ def _is_well_formed(payload: dict[str, Any]) -> bool:
133
+ """Whether the document's fields are the shapes the pillar reads.
134
+
135
+ The header said what the document *claims to be*; this asks whether
136
+ it is. Checking only `schema` accepted
137
+ `{"producer": "not-an-object", ...}`, which the pillar then indexed
138
+ as a mapping and crashed on — a well-formed announcement with a
139
+ malformed body, which is the shape a header check cannot see.
140
+
141
+ Also refused: a `posture` this tool cannot render, and a `posture`
142
+ the document's own two axes do not support. The second is the one
143
+ worth stating. `healthy` requires practice at or above
144
+ `HIGH_PRACTICE` and a condition at or above `GOOD_CONDITION`; a
145
+ document claiming `healthy` at practice 1 is either a different
146
+ matrix or a mistake, and either way this tool must not print it.
147
+ Both tools are supposed to mean the same thing by the word — the
148
+ consumer is where that is checkable.
149
+ """
150
+ for name, kinds in _SHAPES:
151
+ if name in payload and not isinstance(payload[name], kinds):
152
+ return False
153
+ practice = payload.get("practice")
154
+ if isinstance(practice, dict) and not isinstance(practice.get("level"), int):
155
+ return False
156
+ posture = payload.get("posture")
157
+ if posture is not None and posture not in _POSTURES:
158
+ return False
159
+ return _posture_agrees(payload)
160
+
161
+
162
+ def _posture_agrees(payload: dict[str, Any]) -> bool:
163
+ """Whether the stated posture follows from the two axes given.
164
+
165
+ Recomputed rather than trusted. The matrix and its thresholds are
166
+ `_pillars.py`'s, copied into the producer by value, and a copied
167
+ constant is a constant that can drift — so the one place the two
168
+ can be compared is here, against a document carrying both inputs
169
+ and the answer.
170
+ """
171
+ from ._pillars import posture as compute
172
+
173
+ stated = payload.get("posture")
174
+ practice = payload.get("practice")
175
+ if stated is None or not isinstance(practice, dict):
176
+ return True
177
+ level = practice.get("level")
178
+ condition = payload.get("condition")
179
+ if not isinstance(level, int):
180
+ return False
181
+ return compute(level, condition) == stated
@@ -17,9 +17,10 @@ from statistics import median
17
17
  from typing import Any
18
18
 
19
19
  from ._aspects import evidence_aspect_scores, is_untested
20
+ from ._evidence_reader import normalize_report_evidence
20
21
  from ._formula import CALIBRATED_ASPECTS, curve, overall_from_aspects
21
22
  from ._pressures import production_pressures
22
- from .evidence import REPORT_SCHEMA_VERSION, SCHEMA_VERSION_KEY, normalize_report_evidence
23
+ from .evidence import REPORT_SCHEMA_VERSION, SCHEMA_VERSION_KEY
23
24
 
24
25
  # The stored column holding the analyzers' production reading. Named
25
26
  # once because both the refusal and the mix key on it.