maintainability-agent 3.1.0__tar.gz → 3.3.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 (377) hide show
  1. {maintainability_agent-3.1.0/src/maintainability_agent.egg-info → maintainability_agent-3.3.0}/PKG-INFO +2 -2
  2. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/README.md +1 -1
  3. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/pyproject.toml +1 -1
  4. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0/src/maintainability_agent.egg-info}/PKG-INFO +2 -2
  5. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_agent.egg-info/SOURCES.txt +12 -0
  6. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/__init__.py +1 -1
  7. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_anchor.py +6 -3
  8. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_arguments.py +9 -0
  9. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_catalog.py +22 -2
  10. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_coverage_notes.py +36 -0
  11. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_criteria_scope.py +37 -31
  12. maintainability_agent-3.3.0/src/maintainability_audit/_delegated_pillar.py +204 -0
  13. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_derive.py +2 -1
  14. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_discovery.py +70 -26
  15. maintainability_agent-3.3.0/src/maintainability_audit/_evidence_reader.py +305 -0
  16. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_evidence_view.py +11 -4
  17. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_first_run.py +57 -16
  18. maintainability_agent-3.3.0/src/maintainability_audit/_grammar.py +93 -0
  19. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_grant_ledger.py +0 -8
  20. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_history_view.py +85 -24
  21. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_html_report_sections.py +101 -31
  22. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_html_view.py +5 -3
  23. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_in_loop_view.py +32 -15
  24. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_mcp_audit.py +36 -4
  25. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_metric_adapters.py +14 -127
  26. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_pillars.py +84 -1
  27. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_practice.py +46 -24
  28. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_pressures.py +61 -70
  29. maintainability_agent-3.1.0/src/maintainability_audit/prompts.py → maintainability_agent-3.3.0/src/maintainability_audit/_prompt_sections.py +40 -114
  30. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_ranges_core.py +44 -0
  31. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_ranges_fortran.py +29 -11
  32. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_ranges_php.py +2 -28
  33. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_ranges_rust.py +2 -31
  34. maintainability_agent-3.3.0/src/maintainability_audit/_ratio_adapters.py +153 -0
  35. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_scan_history.py +130 -9
  36. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_scan_view.py +46 -18
  37. maintainability_agent-3.3.0/src/maintainability_audit/_selection.py +177 -0
  38. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_semantic_policy.py +18 -8
  39. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_test_execution.py +81 -15
  40. maintainability_agent-3.3.0/src/maintainability_audit/_test_pairing.py +291 -0
  41. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_trends.py +1 -0
  42. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_verdict_adapters.py +50 -36
  43. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_work_order.py +39 -27
  44. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/cli.py +2 -1
  45. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/config.py +1 -1
  46. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/duplication.py +21 -10
  47. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/evidence.py +1 -227
  48. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/history.py +58 -28
  49. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/idioms.py +22 -6
  50. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/metrics.py +17 -9
  51. maintainability_agent-3.3.0/src/maintainability_audit/prompts.py +128 -0
  52. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/report.py +30 -3
  53. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/scoring.py +2 -5
  54. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_analyzer_primary_estimate.py +1 -1
  55. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_analyzer_scoring_path.py +3 -3
  56. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_analyzer_spread_range.py +1 -1
  57. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_anchor_disclosure.py +1 -1
  58. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_band_concealment_p3.py +2 -1
  59. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_bands.py +80 -0
  60. maintainability_agent-3.3.0/tests/test_child_sandbox.py +203 -0
  61. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_consumer_migration.py +2 -1
  62. maintainability_agent-3.3.0/tests/test_declarative_parsers.py +220 -0
  63. maintainability_agent-3.3.0/tests/test_delegated_security_pillar.py +312 -0
  64. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_docs_links.py +30 -0
  65. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_evidence_invariants.py +1 -1
  66. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_evidence_normalization.py +1 -1
  67. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_evidence_properties.py +1 -1
  68. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_history_window.py +10 -25
  69. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_html_section_parity_class.py +21 -1
  70. maintainability_agent-3.3.0/tests/test_mcp_format_contract.py +22 -0
  71. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_mcp_server.py +33 -0
  72. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_operator_named_reads.py +5 -1
  73. maintainability_agent-3.3.0/tests/test_pairing_reads_imports.py +213 -0
  74. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_population_floors.py +1 -1
  75. maintainability_agent-3.3.0/tests/test_release_needs_an_empty_ledger.py +150 -0
  76. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_release_plan_status.py +8 -1
  77. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_scan_scope.py +2 -6
  78. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_scoring_calibration.py +2 -5
  79. maintainability_agent-3.3.0/tests/test_terminal_asks_the_same_questions.py +98 -0
  80. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_test_execution_class.py +28 -8
  81. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_tree_cannot_opt_the_host_in.py +61 -3
  82. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_trends.py +14 -1
  83. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_unanchored_set_matches_corpus.py +7 -2
  84. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_verified_grade.py +1 -1
  85. maintainability_agent-3.1.0/src/maintainability_audit/_selection.py +0 -136
  86. maintainability_agent-3.1.0/src/maintainability_audit/_test_pairing.py +0 -149
  87. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/LICENSE +0 -0
  88. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/setup.cfg +0 -0
  89. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_agent.egg-info/dependency_links.txt +0 -0
  90. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_agent.egg-info/entry_points.txt +0 -0
  91. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_agent.egg-info/requires.txt +0 -0
  92. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_agent.egg-info/top_level.txt +0 -0
  93. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/__main__.py +0 -0
  94. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_adapters.py +0 -0
  95. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_analysis.py +0 -0
  96. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_analyzer_sections.py +0 -0
  97. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_aspects.py +0 -0
  98. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_assets/analyzer-catalog.json +0 -0
  99. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_assets/standard.md +0 -0
  100. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_attestation.py +0 -0
  101. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_backfill.py +0 -0
  102. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_bands.py +0 -0
  103. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_banner.py +0 -0
  104. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_built_ins.py +0 -0
  105. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_calibration.py +0 -0
  106. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_charts.py +0 -0
  107. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_cognitive.py +0 -0
  108. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_config_defaults.py +0 -0
  109. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_conformance.py +0 -0
  110. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_corroborate.py +0 -0
  111. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_documents.py +0 -0
  112. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_economics.py +0 -0
  113. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_economics_view.py +0 -0
  114. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_environment.py +0 -0
  115. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_finding_match.py +0 -0
  116. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_formula.py +0 -0
  117. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_gates.py +0 -0
  118. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_generic.py +0 -0
  119. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_hostile_prompt.py +0 -0
  120. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_hotspots.py +0 -0
  121. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_identity.py +0 -0
  122. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_in_loop.py +0 -0
  123. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_jvm_adapters.py +0 -0
  124. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_markdown_sections.py +0 -0
  125. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_masking.py +0 -0
  126. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_mcp_gate.py +0 -0
  127. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_mcp_grants.py +0 -0
  128. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_mcp_refusals.py +0 -0
  129. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_mcp_resources.py +0 -0
  130. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_mcp_setup.py +0 -0
  131. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_metrics_types.py +0 -0
  132. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_operator_reads.py +0 -0
  133. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_precommit.py +0 -0
  134. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_precommit_install.py +0 -0
  135. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_precommit_view.py +0 -0
  136. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_ranges_c.py +0 -0
  137. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_ranges_cobol.py +0 -0
  138. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_ranges_cpp.py +0 -0
  139. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_ranges_csharp.py +0 -0
  140. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_ranges_go.py +0 -0
  141. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_ranges_java.py +0 -0
  142. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_ranges_js.py +0 -0
  143. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_ranges_ruby.py +0 -0
  144. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_ranges_swift.py +0 -0
  145. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_ratchet.py +0 -0
  146. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_recurrence.py +0 -0
  147. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_run_comparison.py +0 -0
  148. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_runner.py +0 -0
  149. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_safe_write.py +0 -0
  150. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_second_source.py +0 -0
  151. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_semantic.py +0 -0
  152. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_semantic_ts.py +0 -0
  153. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_semantic_view.py +0 -0
  154. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_setup_errors.py +0 -0
  155. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_setup_persist.py +0 -0
  156. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_skill_data/SKILL.md +0 -0
  157. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_skill_data/agents/anthropic.yaml +0 -0
  158. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_skill_data/agents/copilot.yaml +0 -0
  159. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_skill_data/agents/openai.yaml +0 -0
  160. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_skill_data/copilot/maintainability-agent.prompt.md +0 -0
  161. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_skill_data/references/finding-taxonomy.md +0 -0
  162. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_skill_install.py +0 -0
  163. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_stored_grants.py +0 -0
  164. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_tdd_view.py +0 -0
  165. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_test_commands.py +0 -0
  166. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_tokens.py +0 -0
  167. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_tool_adapters.py +0 -0
  168. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_user_config.py +0 -0
  169. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_verification.py +0 -0
  170. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_work_order_view.py +0 -0
  171. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_work_order_weights.py +0 -0
  172. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/_xml.py +0 -0
  173. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/baseline.py +0 -0
  174. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/deadcode.py +0 -0
  175. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/declarations.py +0 -0
  176. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/git_tools.py +0 -0
  177. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/instructions.py +0 -0
  178. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/mcp_server.py +0 -0
  179. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/renderers.py +0 -0
  180. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/sarif.py +0 -0
  181. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/similarity.py +0 -0
  182. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/src/maintainability_audit/source.py +0 -0
  183. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_acquisition_trust.py +0 -0
  184. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_adapter_docs.py +0 -0
  185. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_adapter_recordings.py +0 -0
  186. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_adapters.py +0 -0
  187. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_added_lines.py +0 -0
  188. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_analysis_coverage.py +0 -0
  189. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_analysis_execution.py +0 -0
  190. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_analyzer_bridge.py +0 -0
  191. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_analyzer_config_isolation.py +0 -0
  192. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_analyzer_estimate_claims.py +0 -0
  193. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_analyzer_pinning.py +0 -0
  194. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_analyzer_provenance.py +0 -0
  195. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_analyzer_provenance_exclusions.py +0 -0
  196. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_analyzer_xml_bounds.py +0 -0
  197. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_anchor_and_mode.py +0 -0
  198. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_annotations_match_behaviour.py +0 -0
  199. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_anticipated_refusals.py +0 -0
  200. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_architecture.py +0 -0
  201. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_artifact_write_route_class.py +0 -0
  202. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_aspect_coverage.py +0 -0
  203. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_asset_snapshot_class.py +0 -0
  204. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_ast_reading_resolves_spellings.py +0 -0
  205. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_attestation.py +0 -0
  206. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_audit_components.py +0 -0
  207. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_authorization_freshness.py +0 -0
  208. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_authorship_gates.py +0 -0
  209. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_backfill.py +0 -0
  210. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_band_concealment_class.py +0 -0
  211. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_band_pressures.py +0 -0
  212. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_body_or_signature_class.py +0 -0
  213. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_c_declarations.py +0 -0
  214. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_calibration_corpus.py +0 -0
  215. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_calibration_drift.py +0 -0
  216. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_calibration_tooling.py +0 -0
  217. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_catalog_selection.py +0 -0
  218. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_catalog_tier_promise.py +0 -0
  219. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_chat_primary_docs.py +0 -0
  220. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_check_refuses_plus_only_fragments.py +0 -0
  221. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_checkstyle_adapter.py +0 -0
  222. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_checkstyle_catalog.py +0 -0
  223. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_ci_history_recipe.py +0 -0
  224. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_ci_installs_the_analyzer_pool.py +0 -0
  225. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_claimed_languages.py +0 -0
  226. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_cli.py +0 -0
  227. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_clone_group_class.py +0 -0
  228. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_cobol_declarations.py +0 -0
  229. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_cognitive_complexity.py +0 -0
  230. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_cohort_statistics.py +0 -0
  231. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_concealment_properties.py +0 -0
  232. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_config_shape.py +0 -0
  233. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_consent_and_grant.py +0 -0
  234. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_contract_claims.py +0 -0
  235. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_corroborate.py +0 -0
  236. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_coverage_by_language.py +0 -0
  237. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_cpp_declarations.py +0 -0
  238. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_criteria_completion.py +0 -0
  239. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_csharp_declarations.py +0 -0
  240. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_d15_composition.py +0 -0
  241. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_d15_goal_directed.py +0 -0
  242. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_dead_code.py +0 -0
  243. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_declaration_criteria_scope.py +0 -0
  244. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_declaration_edge_cases.py +0 -0
  245. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_declaration_grading.py +0 -0
  246. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_declaration_ranges.py +0 -0
  247. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_declaration_source.py +0 -0
  248. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_declared_authorship.py +0 -0
  249. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_declared_imports.py +0 -0
  250. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_derivation_mix.py +0 -0
  251. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_determinism.py +0 -0
  252. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_dimension_ratchet.py +0 -0
  253. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_direct_runner_availability_class.py +0 -0
  254. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_discovery.py +0 -0
  255. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_doc_claims.py +0 -0
  256. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_economic_context.py +0 -0
  257. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_empty_output_not_clean.py +0 -0
  258. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_empty_output_not_clean_adapters.py +0 -0
  259. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_environment_work_order.py +0 -0
  260. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_exclusion_dialects.py +0 -0
  261. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_falsifier_standard.py +0 -0
  262. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_file_list_cap_class.py +0 -0
  263. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_finding_identity.py +0 -0
  264. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_first_run_elicitation.py +0 -0
  265. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_first_run_prompt.py +0 -0
  266. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_first_run_reelicitation.py +0 -0
  267. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_fix_breadth_window.py +0 -0
  268. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_fixed_form_fortran.py +0 -0
  269. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_format_ask.py +0 -0
  270. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_fortran_declarations.py +0 -0
  271. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_fortran_metrics.py +0 -0
  272. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_fortran_practice.py +0 -0
  273. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_git_argv.py +0 -0
  274. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_git_read_only.py +0 -0
  275. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_git_readonly_class.py +0 -0
  276. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_go_declarations.py +0 -0
  277. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_grammar_constructs.py +0 -0
  278. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_grammar_token_coverage.py +0 -0
  279. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_grant_only_user_tier.py +0 -0
  280. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_history.py +0 -0
  281. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_history_boundary.py +0 -0
  282. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_history_manifest.py +0 -0
  283. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_history_schema2.py +0 -0
  284. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_hostile_prompt.py +0 -0
  285. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_html_executive.py +0 -0
  286. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_identity_docs.py +0 -0
  287. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_identity_resolution.py +0 -0
  288. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_idiom_divergence.py +0 -0
  289. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_in_loop_budgets.py +0 -0
  290. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_in_loop_check.py +0 -0
  291. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_installed_wheel_runtime.py +0 -0
  292. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_instruction_targets.py +0 -0
  293. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_java_range_guard.py +0 -0
  294. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_java_ranges.py +0 -0
  295. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_java_scored.py +0 -0
  296. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_java_wiring.py +0 -0
  297. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_js_complexity_operators.py +0 -0
  298. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_js_declarations.py +0 -0
  299. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_language_coverage.py +0 -0
  300. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_language_support_honesty.py +0 -0
  301. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_mcp_baseline_payload.py +0 -0
  302. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_mcp_history.py +0 -0
  303. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_migration_1_0.py +0 -0
  304. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_near_duplicates.py +0 -0
  305. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_network_disclosure.py +0 -0
  306. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_one_setup_across_surfaces_class.py +0 -0
  307. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_operator_named_paths.py +0 -0
  308. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_pattern_budget.py +0 -0
  309. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_phase6_claims.py +0 -0
  310. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_php_declarations.py +0 -0
  311. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_pillars_and_practice.py +0 -0
  312. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_platform_claim.py +0 -0
  313. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_pmd_adapter.py +0 -0
  314. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_pmd_audit_closures.py +0 -0
  315. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_policy_claims.py +0 -0
  316. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_pool_runs_by_config.py +0 -0
  317. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_practice_nested_gates_class.py +0 -0
  318. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_precommit_staged.py +0 -0
  319. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_presentation_refusals.py +0 -0
  320. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_promises.py +0 -0
  321. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_prompt_escalation_once.py +0 -0
  322. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_prompt_honesty.py +0 -0
  323. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_prompt_location_escalation_class.py +0 -0
  324. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_prove_falsifiers.py +0 -0
  325. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_python_complexity.py +0 -0
  326. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_python_declarations.py +0 -0
  327. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_read_boundary.py +0 -0
  328. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_readme_claims.py +0 -0
  329. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_recurrence.py +0 -0
  330. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_release_plan.py +0 -0
  331. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_report_remedy_copy.py +0 -0
  332. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_roles_recorded.py +0 -0
  333. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_root_grants.py +0 -0
  334. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_rubric_ignores_project_lint_config.py +0 -0
  335. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_ruby_declarations.py +0 -0
  336. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_run_comparison.py +0 -0
  337. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_runner.py +0 -0
  338. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_rust_declarations.py +0 -0
  339. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_sarif.py +0 -0
  340. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_scan_history.py +0 -0
  341. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_scanner_unsafe_direction.py +0 -0
  342. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_scanning.py +0 -0
  343. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_schema_fidelity.py +0 -0
  344. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_scope_conformance.py +0 -0
  345. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_semantic_policy.py +0 -0
  346. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_setup_gate_completeness.py +0 -0
  347. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_setup_gate_independent_contract.py +0 -0
  348. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_setup_precondition.py +0 -0
  349. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_skill_install.py +0 -0
  350. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_sonar_resolve.py +0 -0
  351. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_source_index.py +0 -0
  352. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_spotbugs_adapter.py +0 -0
  353. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_spotbugs_catalog.py +0 -0
  354. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_stage8_contract.py +0 -0
  355. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_suite_isolation.py +0 -0
  356. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_swift_declarations.py +0 -0
  357. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_tdd_structure.py +0 -0
  358. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_test_commands.py +0 -0
  359. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_three_presentations.py +0 -0
  360. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_tool_resolution.py +0 -0
  361. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_tree_chosen_spawn.py +0 -0
  362. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_tree_reads_use_the_primitive.py +0 -0
  363. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_type_fields_not_functions_class.py +0 -0
  364. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_uat_falsifiers.py +0 -0
  365. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_unread_code.py +0 -0
  366. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_user_config_tier.py +0 -0
  367. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_verification_audit_closures.py +0 -0
  368. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_version_claim.py +0 -0
  369. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_wheel_contents.py +0 -0
  370. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_work_order.py +0 -0
  371. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_work_order_binds_to_this_interpreter.py +0 -0
  372. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_work_order_items.py +0 -0
  373. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_work_order_selection.py +0 -0
  374. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_workflow_supply_chain.py +0 -0
  375. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_write_boundary.py +0 -0
  376. {maintainability_agent-3.1.0 → maintainability_agent-3.3.0}/tests/test_write_route_class.py +0 -0
  377. {maintainability_agent-3.1.0 → maintainability_agent-3.3.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.1.0
3
+ Version: 3.3.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.1.0**.
48
+ Version **3.3.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.1.0**.
8
+ Version **3.3.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.1.0"
7
+ version = "3.3.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.1.0
3
+ Version: 3.3.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.1.0**.
48
+ Version **3.3.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
@@ -186,6 +191,7 @@ tests/test_chat_primary_docs.py
186
191
  tests/test_check_refuses_plus_only_fragments.py
187
192
  tests/test_checkstyle_adapter.py
188
193
  tests/test_checkstyle_catalog.py
194
+ tests/test_child_sandbox.py
189
195
  tests/test_ci_history_recipe.py
190
196
  tests/test_ci_installs_the_analyzer_pool.py
191
197
  tests/test_claimed_languages.py
@@ -212,8 +218,10 @@ tests/test_declaration_edge_cases.py
212
218
  tests/test_declaration_grading.py
213
219
  tests/test_declaration_ranges.py
214
220
  tests/test_declaration_source.py
221
+ tests/test_declarative_parsers.py
215
222
  tests/test_declared_authorship.py
216
223
  tests/test_declared_imports.py
224
+ tests/test_delegated_security_pillar.py
217
225
  tests/test_derivation_mix.py
218
226
  tests/test_determinism.py
219
227
  tests/test_dimension_ratchet.py
@@ -272,6 +280,7 @@ tests/test_js_declarations.py
272
280
  tests/test_language_coverage.py
273
281
  tests/test_language_support_honesty.py
274
282
  tests/test_mcp_baseline_payload.py
283
+ tests/test_mcp_format_contract.py
275
284
  tests/test_mcp_history.py
276
285
  tests/test_mcp_server.py
277
286
  tests/test_migration_1_0.py
@@ -280,6 +289,7 @@ tests/test_network_disclosure.py
280
289
  tests/test_one_setup_across_surfaces_class.py
281
290
  tests/test_operator_named_paths.py
282
291
  tests/test_operator_named_reads.py
292
+ tests/test_pairing_reads_imports.py
283
293
  tests/test_pattern_budget.py
284
294
  tests/test_phase6_claims.py
285
295
  tests/test_php_declarations.py
@@ -303,6 +313,7 @@ tests/test_python_declarations.py
303
313
  tests/test_read_boundary.py
304
314
  tests/test_readme_claims.py
305
315
  tests/test_recurrence.py
316
+ tests/test_release_needs_an_empty_ledger.py
306
317
  tests/test_release_plan.py
307
318
  tests/test_release_plan_status.py
308
319
  tests/test_report_remedy_copy.py
@@ -334,6 +345,7 @@ tests/test_stage8_contract.py
334
345
  tests/test_suite_isolation.py
335
346
  tests/test_swift_declarations.py
336
347
  tests/test_tdd_structure.py
348
+ tests/test_terminal_asks_the_same_questions.py
337
349
  tests/test_test_commands.py
338
350
  tests/test_test_execution_class.py
339
351
  tests/test_three_presentations.py
@@ -1,3 +1,3 @@
1
1
  """Maintainability Audit CI."""
2
2
 
3
- __version__ = "3.1.0"
3
+ __version__ = "3.3.0"
@@ -63,8 +63,11 @@ def unanchored_sentence() -> str:
63
63
  """One sentence naming what the anchor omits, for the corpus note."""
64
64
  if not UNANCHORED_LANGUAGES:
65
65
  return ""
66
+ from ._grammar import agreement
67
+
68
+ say = agreement(len(UNANCHORED_LANGUAGES))
66
69
  return (
67
- f"{unanchored_names()} are parsed but absent from it, so their "
68
- "findings are as good as their parser while their grade is "
69
- "provisional."
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."
70
73
  )
@@ -120,6 +120,15 @@ def add_arguments(parser: argparse.ArgumentParser) -> None:
120
120
  parser.add_argument("--output", help="Output file. Defaults to stdout.")
121
121
  _add_artifact_arguments(parser)
122
122
  parser.add_argument("--sarif-output", help="Optional SARIF output path for GitHub code scanning.")
123
+ parser.add_argument(
124
+ "--security-pillar",
125
+ help=(
126
+ "Path to the security-pillar.json secure-code-agent writes with its "
127
+ "own --security-pillar. ADR 007 delegates the security pillar, and "
128
+ "this is the document it is read from; defaults to "
129
+ ".maintainability/security-pillar.json."
130
+ ),
131
+ )
123
132
  parser.add_argument("--sarif-input", action="append", help="Optional external SARIF file to summarize in reports. Repeatable.")
124
133
  parser.add_argument("--changed-only", help="Audit only files changed in a git revspec, for example main...HEAD.")
125
134
  parser.add_argument("--baseline", help="Existing baseline JSON. With --fail-on-new, only new findings fail.")
@@ -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"]:
@@ -59,4 +59,40 @@ def coverage_notes(coverage: dict[str, Any]) -> list[str]:
59
59
  "or widen `analyzers.depth`, to have them reported.",
60
60
  "",
61
61
  ])
62
+
63
+ lines.extend(_child_boundary_note(coverage))
62
64
  return lines
65
+
66
+
67
+ def _child_boundary_note(coverage: dict[str, Any]) -> list[str]:
68
+ """What this run does not control about the tools it spawned.
69
+
70
+ `docs/product-intent.md` has said since P1 was amended that this
71
+ package "does not sandbox children", and lists *"that third-party
72
+ tools cannot use the network"* among the things it does not promise.
73
+ The **report** did not say it. A reader holding a page headed "the
74
+ analysis itself performs no network access" and a table naming the
75
+ external tools that ran had no way to learn the second sentence does
76
+ not cover the first.
77
+
78
+ That makes it P8's gap rather than P1's: the promise was correctly
79
+ scoped all along, and the run relying on that scoping never disclosed
80
+ it to the person reading the output.
81
+
82
+ Only when an external tool actually ran. With no children spawned
83
+ there is nothing to disclose, and saying it anyway would be the noise
84
+ this section is careful to avoid.
85
+ """
86
+ spawned = coverage.get("tools_contributed") or 0
87
+ if not spawned:
88
+ return []
89
+ return [
90
+ "**Analyzer children are not network-isolated.** "
91
+ f"{spawned} external {'tool' if spawned == 1 else 'tools'} ran as local "
92
+ "child processes. This agent does not transmit the audited source and "
93
+ "opens no socket of its own, but it does not sandbox what it spawns: a "
94
+ "third-party analyzer that reaches the network is outside what this run "
95
+ "controls or observes. Determinism and no upload are the promise; a "
96
+ "kernel air-gap is not.",
97
+ "",
98
+ ]
@@ -154,40 +154,46 @@ def complete_from_built_ins(
154
154
  """
155
155
  completed: dict[str, dict[str, float]] = {}
156
156
  for unit, values in per_unit.items():
157
- missing = declaration_concepts_missing(set(values))
158
- if not missing:
159
- completed[unit] = values
160
- continue
161
- location = unit_locations.get(unit)
162
- fallback = built_in.get(location) if location else None
163
- filled = dict(values)
164
- for concept in missing:
165
- if fallback and concept in fallback:
166
- filled[concept] = fallback[concept]
167
- if declaration_concepts_missing(set(filled)):
168
- # No counterpart in the scanner's reading, so this
169
- # declaration is not scoreable on the full criterion set by
170
- # either tier — and it is dropped rather than left to
171
- # disqualify its language.
172
- #
173
- # Measured, because the alternative looked reasonable and was
174
- # not: `lizard` reads files the built-in scanner produces no
175
- # declarations for — Alamofire's `docs/js/jquery.min.js` and
176
- # `jazzy.js`, five generated documentation assets. Keeping
177
- # them gave JavaScript a bucket that could never be completed
178
- # and took the whole repository to the built-in tier, Swift
179
- # and all. The same shape hid Java behind a repository's
180
- # bundled `.js`.
181
- #
182
- # Dropping them also aligns the two populations: a unit the
183
- # scanner never read is not in the built-in rate either, so
184
- # including it in the analyzer rate compares a wider
185
- # population against a narrower one.
186
- continue
187
- completed[unit] = filled
157
+ filled = _completed_unit(values, unit_locations.get(unit), built_in)
158
+ if filled is not None:
159
+ completed[unit] = filled
188
160
  return completed
189
161
 
190
162
 
163
+ def _completed_unit(
164
+ values: dict[str, float],
165
+ location: tuple[str, int] | None,
166
+ built_in: dict[tuple[str, int], dict[str, float]],
167
+ ) -> dict[str, float] | None:
168
+ """One declaration's readings, completed from the built-in tier.
169
+
170
+ `None` means the union of both tiers still does not cover the
171
+ criterion set, so this declaration is **dropped** rather than left to
172
+ disqualify its language.
173
+
174
+ Measured, because the alternative looked reasonable and was not:
175
+ `lizard` reads files the built-in scanner produces no declarations
176
+ for — Alamofire's `docs/js/jquery.min.js` and `jazzy.js`, five
177
+ generated documentation assets. Keeping them gave JavaScript a bucket
178
+ that could never be completed and took the whole repository to the
179
+ built-in tier, Swift and all. The same shape hid Java behind a
180
+ repository's bundled `.js`.
181
+
182
+ Dropping them also aligns the two populations: a unit the scanner
183
+ never read is not in the built-in rate either, so including it in the
184
+ analyzer rate compares a wider population against a narrower one.
185
+ """
186
+ missing = declaration_concepts_missing(set(values))
187
+ if not missing:
188
+ return values
189
+ fallback = (built_in.get(location) if location else None) or {}
190
+ filled = dict(values)
191
+ for concept in missing:
192
+ if concept in fallback:
193
+ filled[concept] = fallback[concept]
194
+ return None if declaration_concepts_missing(set(filled)) else filled
195
+
196
+
191
197
  def built_in_readings(
192
198
  metrics: object, root: str = ""
193
199
  ) -> dict[tuple[str, int], dict[str, float]]:
@@ -0,0 +1,204 @@
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
+ #: Versions of that contract this tool can read. **Two, deliberately.**
62
+ #: v2 is v1 plus `scoring_model` and nothing else, so a reader that
63
+ #: handles both is not guessing: the producer asserts the key-set
64
+ #: difference is exactly that one field.
65
+ #:
66
+ #: Accepting v2 before the producer emits it is what removes the
67
+ #: deadlock. If MA required v1 while `secure-code-agent` shipped v2,
68
+ #: every document would be refused for the window between the two
69
+ #: releases -- silently, since a refused document reads as no delegated
70
+ #: pillar. Either side can now release first.
71
+ SCHEMA_VERSIONS = frozenset({1, 2})
72
+
73
+ #: Kept for callers that imported the old name. The gate reads the set.
74
+ SCHEMA_VERSION = 1
75
+
76
+ #: What the pillar block takes from the document. Named rather than
77
+ #: copied wholesale so a field added upstream cannot silently appear in
78
+ #: this tool's report carrying meaning nobody here has checked.
79
+ CARRIED = (
80
+ "practice",
81
+ "condition",
82
+ "condition_letter",
83
+ "posture",
84
+ "verified_grade",
85
+ "evidence_status",
86
+ "evidence_reasons",
87
+ "coverage",
88
+ "findings_by_severity",
89
+ "reported_not_scored",
90
+ "loc_scanned",
91
+ #: v2. The producer's own identifier for its scoring model, bumped
92
+ #: when a repository's condition could differ for reasons that are
93
+ #: not the repository -- normalizer, slope, weights, letter bands,
94
+ #: the scanner floor, or the built-in rule profile. Absent on v1.
95
+ #:
96
+ #: This is what MA keys a delegated trend on, in preference to the
97
+ #: producer's version, because the version moves on docs releases
98
+ #: that change no score and fragmenting a trend for those teaches a
99
+ #: reader to ignore the breaks (D155).
100
+ "scoring_model",
101
+ )
102
+
103
+
104
+ def read_delegated(root: Path, relative: str = DEFAULT_PILLAR_PATH) -> dict[str, Any] | None:
105
+ """The delegated pillar's own report, or `None` when there is none.
106
+
107
+ `None` for every reason a reader might not get a usable document:
108
+ the file is absent, it is not a regular file, it does not parse, it
109
+ is not an object, or it announces a schema this tool does not know.
110
+ All five collapse to one answer because the caller does the same
111
+ thing with each — keep the placeholder that names the other tool.
112
+
113
+ Read through the regular-file primitive like every other path the
114
+ audited tree chooses (D145): a FIFO here would hang the audit rather
115
+ than fail it.
116
+ """
117
+ path = Path(root) / relative
118
+ if not path.exists():
119
+ return None
120
+ try:
121
+ payload = json.loads("\n".join(read_source_file(path)))
122
+ except (OSError, ValueError) as error:
123
+ if isinstance(error, PathNotAllowed):
124
+ raise
125
+ return None
126
+ if not isinstance(payload, dict):
127
+ return None
128
+ if payload.get("schema") != SCHEMA:
129
+ return None
130
+ if payload.get("schema_version") not in SCHEMA_VERSIONS:
131
+ return None
132
+ return payload if _is_well_formed(payload) else None
133
+
134
+
135
+ #: Fields whose *shape* the consumer depends on. A header check alone
136
+ #: accepts any object announcing the right schema, and the pillar then
137
+ #: reads these as if the producer had promised them.
138
+ _SHAPES: tuple[tuple[str, type | tuple[type, ...]], ...] = (
139
+ ("producer", dict),
140
+ ("practice", dict),
141
+ ("condition", (int, float, type(None))),
142
+ ("posture", (str, type(None))),
143
+ ("evidence_status", (str, type(None))),
144
+ ("coverage", (dict, type(None))),
145
+ ("findings_by_severity", (dict, type(None))),
146
+ ("reported_not_scored", (dict, type(None))),
147
+ )
148
+
149
+ #: The postures this tool knows how to render. A document naming one it
150
+ #: does not is refused rather than printed through: the word is the
151
+ #: reader's whole summary of the pillar.
152
+ _POSTURES = frozenset({"healthy", "managed debt", "unmanaged debt", "unverified"})
153
+
154
+
155
+ def _is_well_formed(payload: dict[str, Any]) -> bool:
156
+ """Whether the document's fields are the shapes the pillar reads.
157
+
158
+ The header said what the document *claims to be*; this asks whether
159
+ it is. Checking only `schema` accepted
160
+ `{"producer": "not-an-object", ...}`, which the pillar then indexed
161
+ as a mapping and crashed on — a well-formed announcement with a
162
+ malformed body, which is the shape a header check cannot see.
163
+
164
+ Also refused: a `posture` this tool cannot render, and a `posture`
165
+ the document's own two axes do not support. The second is the one
166
+ worth stating. `healthy` requires practice at or above
167
+ `HIGH_PRACTICE` and a condition at or above `GOOD_CONDITION`; a
168
+ document claiming `healthy` at practice 1 is either a different
169
+ matrix or a mistake, and either way this tool must not print it.
170
+ Both tools are supposed to mean the same thing by the word — the
171
+ consumer is where that is checkable.
172
+ """
173
+ for name, kinds in _SHAPES:
174
+ if name in payload and not isinstance(payload[name], kinds):
175
+ return False
176
+ practice = payload.get("practice")
177
+ if isinstance(practice, dict) and not isinstance(practice.get("level"), int):
178
+ return False
179
+ posture = payload.get("posture")
180
+ if posture is not None and posture not in _POSTURES:
181
+ return False
182
+ return _posture_agrees(payload)
183
+
184
+
185
+ def _posture_agrees(payload: dict[str, Any]) -> bool:
186
+ """Whether the stated posture follows from the two axes given.
187
+
188
+ Recomputed rather than trusted. The matrix and its thresholds are
189
+ `_pillars.py`'s, copied into the producer by value, and a copied
190
+ constant is a constant that can drift — so the one place the two
191
+ can be compared is here, against a document carrying both inputs
192
+ and the answer.
193
+ """
194
+ from ._pillars import posture as compute
195
+
196
+ stated = payload.get("posture")
197
+ practice = payload.get("practice")
198
+ if stated is None or not isinstance(practice, dict):
199
+ return True
200
+ level = practice.get("level")
201
+ condition = payload.get("condition")
202
+ if not isinstance(level, int):
203
+ return False
204
+ 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.