maintainability-agent 2.7.0__tar.gz → 2.8.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 (326) hide show
  1. {maintainability_agent-2.7.0/src/maintainability_agent.egg-info → maintainability_agent-2.8.0}/PKG-INFO +4 -4
  2. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/README.md +3 -3
  3. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/pyproject.toml +1 -1
  4. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0/src/maintainability_agent.egg-info}/PKG-INFO +4 -4
  5. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_agent.egg-info/SOURCES.txt +4 -0
  6. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/__init__.py +1 -1
  7. maintainability_agent-2.8.0/src/maintainability_audit/_anchor.py +49 -0
  8. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_assets/standard.md +16 -4
  9. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_cognitive.py +16 -6
  10. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_evidence_view.py +30 -0
  11. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_html_view.py +11 -0
  12. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_masking.py +44 -0
  13. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_ranges_cobol.py +15 -5
  14. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_ranges_swift.py +6 -1
  15. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_semantic_ts.py +32 -16
  16. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/config.py +1 -1
  17. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/prompts.py +4 -3
  18. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/renderers.py +1 -0
  19. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/scoring.py +5 -1
  20. maintainability_agent-2.8.0/tests/test_anchor_disclosure.py +176 -0
  21. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_direct_runner_availability_class.py +15 -7
  22. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_platform_claim.py +70 -5
  23. maintainability_agent-2.8.0/tests/test_scanner_unsafe_direction.py +147 -0
  24. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_stage8_contract.py +15 -7
  25. maintainability_agent-2.8.0/tests/test_tree_chosen_spawn.py +171 -0
  26. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_verified_grade.py +5 -4
  27. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/LICENSE +0 -0
  28. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/setup.cfg +0 -0
  29. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_agent.egg-info/dependency_links.txt +0 -0
  30. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_agent.egg-info/entry_points.txt +0 -0
  31. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_agent.egg-info/requires.txt +0 -0
  32. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_agent.egg-info/top_level.txt +0 -0
  33. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/__main__.py +0 -0
  34. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_adapters.py +0 -0
  35. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_analysis.py +0 -0
  36. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_arguments.py +0 -0
  37. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_aspects.py +0 -0
  38. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_assets/analyzer-catalog.json +0 -0
  39. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_attestation.py +0 -0
  40. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_backfill.py +0 -0
  41. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_bands.py +0 -0
  42. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_built_ins.py +0 -0
  43. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_calibration.py +0 -0
  44. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_catalog.py +0 -0
  45. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_charts.py +0 -0
  46. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_config_defaults.py +0 -0
  47. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_conformance.py +0 -0
  48. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_corroborate.py +0 -0
  49. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_coverage_notes.py +0 -0
  50. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_derive.py +0 -0
  51. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_discovery.py +0 -0
  52. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_documents.py +0 -0
  53. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_economics.py +0 -0
  54. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_economics_view.py +0 -0
  55. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_environment.py +0 -0
  56. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_finding_match.py +0 -0
  57. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_first_run.py +0 -0
  58. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_formula.py +0 -0
  59. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_gates.py +0 -0
  60. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_generic.py +0 -0
  61. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_grant_ledger.py +0 -0
  62. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_history_view.py +0 -0
  63. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_hostile_prompt.py +0 -0
  64. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_hotspots.py +0 -0
  65. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_html_report_sections.py +0 -0
  66. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_identity.py +0 -0
  67. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_jvm_adapters.py +0 -0
  68. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_markdown_sections.py +0 -0
  69. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_mcp_audit.py +0 -0
  70. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_mcp_gate.py +0 -0
  71. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_mcp_grants.py +0 -0
  72. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_mcp_refusals.py +0 -0
  73. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_mcp_resources.py +0 -0
  74. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_mcp_setup.py +0 -0
  75. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_metric_adapters.py +0 -0
  76. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_metrics_types.py +0 -0
  77. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_pillars.py +0 -0
  78. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_practice.py +0 -0
  79. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_pressures.py +0 -0
  80. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_ranges_c.py +0 -0
  81. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_ranges_core.py +0 -0
  82. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_ranges_cpp.py +0 -0
  83. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_ranges_csharp.py +0 -0
  84. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_ranges_fortran.py +0 -0
  85. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_ranges_java.py +0 -0
  86. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_ranges_js.py +0 -0
  87. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_ratchet.py +0 -0
  88. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_recurrence.py +0 -0
  89. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_run_comparison.py +0 -0
  90. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_runner.py +0 -0
  91. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_safe_write.py +0 -0
  92. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_scan_history.py +0 -0
  93. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_scan_view.py +0 -0
  94. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_second_source.py +0 -0
  95. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_selection.py +0 -0
  96. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_semantic.py +0 -0
  97. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_semantic_policy.py +0 -0
  98. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_semantic_view.py +0 -0
  99. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_setup_errors.py +0 -0
  100. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_setup_persist.py +0 -0
  101. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_skill_data/SKILL.md +0 -0
  102. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_skill_data/agents/anthropic.yaml +0 -0
  103. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_skill_data/agents/copilot.yaml +0 -0
  104. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_skill_data/agents/openai.yaml +0 -0
  105. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_skill_data/copilot/maintainability-agent.prompt.md +0 -0
  106. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_skill_data/references/finding-taxonomy.md +0 -0
  107. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_skill_install.py +0 -0
  108. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_stored_grants.py +0 -0
  109. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_tdd_view.py +0 -0
  110. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_test_commands.py +0 -0
  111. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_test_execution.py +0 -0
  112. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_test_pairing.py +0 -0
  113. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_tokens.py +0 -0
  114. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_tool_adapters.py +0 -0
  115. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_trends.py +0 -0
  116. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_user_config.py +0 -0
  117. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_verdict_adapters.py +0 -0
  118. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_verification.py +0 -0
  119. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_work_order.py +0 -0
  120. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_work_order_view.py +0 -0
  121. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_work_order_weights.py +0 -0
  122. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/_xml.py +0 -0
  123. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/baseline.py +0 -0
  124. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/cli.py +0 -0
  125. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/deadcode.py +0 -0
  126. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/declarations.py +0 -0
  127. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/duplication.py +0 -0
  128. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/evidence.py +0 -0
  129. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/git_tools.py +0 -0
  130. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/history.py +0 -0
  131. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/idioms.py +0 -0
  132. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/instructions.py +0 -0
  133. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/mcp_server.py +0 -0
  134. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/metrics.py +0 -0
  135. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/report.py +0 -0
  136. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/sarif.py +0 -0
  137. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/similarity.py +0 -0
  138. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/src/maintainability_audit/source.py +0 -0
  139. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_acquisition_trust.py +0 -0
  140. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_adapter_docs.py +0 -0
  141. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_adapter_recordings.py +0 -0
  142. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_adapters.py +0 -0
  143. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_added_lines.py +0 -0
  144. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_analysis_coverage.py +0 -0
  145. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_analysis_execution.py +0 -0
  146. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_analyzer_bridge.py +0 -0
  147. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_analyzer_config_isolation.py +0 -0
  148. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_analyzer_estimate_claims.py +0 -0
  149. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_analyzer_pinning.py +0 -0
  150. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_analyzer_primary_estimate.py +0 -0
  151. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_analyzer_provenance.py +0 -0
  152. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_analyzer_provenance_exclusions.py +0 -0
  153. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_analyzer_scoring_path.py +0 -0
  154. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_analyzer_spread_range.py +0 -0
  155. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_analyzer_xml_bounds.py +0 -0
  156. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_annotations_match_behaviour.py +0 -0
  157. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_anticipated_refusals.py +0 -0
  158. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_architecture.py +0 -0
  159. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_artifact_write_route_class.py +0 -0
  160. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_aspect_coverage.py +0 -0
  161. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_asset_snapshot_class.py +0 -0
  162. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_ast_reading_resolves_spellings.py +0 -0
  163. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_attestation.py +0 -0
  164. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_audit_components.py +0 -0
  165. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_authorization_freshness.py +0 -0
  166. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_authorship_gates.py +0 -0
  167. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_backfill.py +0 -0
  168. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_band_concealment_class.py +0 -0
  169. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_band_concealment_p3.py +0 -0
  170. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_band_pressures.py +0 -0
  171. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_bands.py +0 -0
  172. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_body_or_signature_class.py +0 -0
  173. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_c_declarations.py +0 -0
  174. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_calibration_corpus.py +0 -0
  175. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_calibration_drift.py +0 -0
  176. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_calibration_tooling.py +0 -0
  177. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_catalog_selection.py +0 -0
  178. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_catalog_tier_promise.py +0 -0
  179. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_chat_primary_docs.py +0 -0
  180. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_checkstyle_adapter.py +0 -0
  181. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_checkstyle_catalog.py +0 -0
  182. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_ci_history_recipe.py +0 -0
  183. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_ci_installs_the_analyzer_pool.py +0 -0
  184. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_claimed_languages.py +0 -0
  185. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_cli.py +0 -0
  186. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_clone_group_class.py +0 -0
  187. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_cobol_declarations.py +0 -0
  188. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_cognitive_complexity.py +0 -0
  189. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_cohort_statistics.py +0 -0
  190. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_concealment_properties.py +0 -0
  191. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_config_shape.py +0 -0
  192. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_consent_and_grant.py +0 -0
  193. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_consumer_migration.py +0 -0
  194. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_contract_claims.py +0 -0
  195. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_corroborate.py +0 -0
  196. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_coverage_by_language.py +0 -0
  197. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_cpp_declarations.py +0 -0
  198. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_csharp_declarations.py +0 -0
  199. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_d15_composition.py +0 -0
  200. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_d15_goal_directed.py +0 -0
  201. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_dead_code.py +0 -0
  202. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_declaration_edge_cases.py +0 -0
  203. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_declaration_grading.py +0 -0
  204. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_declaration_ranges.py +0 -0
  205. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_declaration_source.py +0 -0
  206. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_declared_authorship.py +0 -0
  207. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_declared_imports.py +0 -0
  208. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_derivation_mix.py +0 -0
  209. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_determinism.py +0 -0
  210. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_dimension_ratchet.py +0 -0
  211. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_discovery.py +0 -0
  212. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_doc_claims.py +0 -0
  213. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_docs_links.py +0 -0
  214. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_economic_context.py +0 -0
  215. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_empty_output_not_clean.py +0 -0
  216. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_empty_output_not_clean_adapters.py +0 -0
  217. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_environment_work_order.py +0 -0
  218. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_evidence_invariants.py +0 -0
  219. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_evidence_normalization.py +0 -0
  220. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_evidence_properties.py +0 -0
  221. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_exclusion_dialects.py +0 -0
  222. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_falsifier_standard.py +0 -0
  223. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_file_list_cap_class.py +0 -0
  224. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_finding_identity.py +0 -0
  225. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_first_run_elicitation.py +0 -0
  226. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_first_run_prompt.py +0 -0
  227. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_first_run_reelicitation.py +0 -0
  228. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_fix_breadth_window.py +0 -0
  229. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_fixed_form_fortran.py +0 -0
  230. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_format_ask.py +0 -0
  231. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_fortran_declarations.py +0 -0
  232. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_fortran_metrics.py +0 -0
  233. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_fortran_practice.py +0 -0
  234. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_git_argv.py +0 -0
  235. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_git_read_only.py +0 -0
  236. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_git_readonly_class.py +0 -0
  237. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_grant_only_user_tier.py +0 -0
  238. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_history.py +0 -0
  239. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_history_boundary.py +0 -0
  240. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_history_manifest.py +0 -0
  241. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_history_schema2.py +0 -0
  242. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_history_window.py +0 -0
  243. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_hostile_prompt.py +0 -0
  244. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_html_executive.py +0 -0
  245. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_html_section_parity_class.py +0 -0
  246. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_identity_docs.py +0 -0
  247. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_identity_resolution.py +0 -0
  248. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_idiom_divergence.py +0 -0
  249. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_installed_wheel_runtime.py +0 -0
  250. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_instruction_targets.py +0 -0
  251. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_java_range_guard.py +0 -0
  252. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_java_ranges.py +0 -0
  253. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_java_scored.py +0 -0
  254. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_java_wiring.py +0 -0
  255. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_js_complexity_operators.py +0 -0
  256. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_js_declarations.py +0 -0
  257. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_language_coverage.py +0 -0
  258. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_mcp_baseline_payload.py +0 -0
  259. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_mcp_history.py +0 -0
  260. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_mcp_server.py +0 -0
  261. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_migration_1_0.py +0 -0
  262. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_near_duplicates.py +0 -0
  263. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_network_disclosure.py +0 -0
  264. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_one_setup_across_surfaces_class.py +0 -0
  265. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_pattern_budget.py +0 -0
  266. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_phase6_claims.py +0 -0
  267. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_pillars_and_practice.py +0 -0
  268. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_pmd_adapter.py +0 -0
  269. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_pmd_audit_closures.py +0 -0
  270. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_pool_runs_by_config.py +0 -0
  271. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_population_floors.py +0 -0
  272. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_practice_nested_gates_class.py +0 -0
  273. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_presentation_refusals.py +0 -0
  274. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_promises.py +0 -0
  275. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_prompt_escalation_once.py +0 -0
  276. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_prompt_honesty.py +0 -0
  277. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_prompt_location_escalation_class.py +0 -0
  278. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_prove_falsifiers.py +0 -0
  279. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_python_declarations.py +0 -0
  280. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_read_boundary.py +0 -0
  281. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_readme_claims.py +0 -0
  282. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_recurrence.py +0 -0
  283. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_release_plan.py +0 -0
  284. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_release_plan_status.py +0 -0
  285. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_report_remedy_copy.py +0 -0
  286. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_roles_recorded.py +0 -0
  287. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_root_grants.py +0 -0
  288. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_rubric_ignores_project_lint_config.py +0 -0
  289. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_run_comparison.py +0 -0
  290. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_runner.py +0 -0
  291. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_sarif.py +0 -0
  292. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_scan_history.py +0 -0
  293. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_scan_scope.py +0 -0
  294. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_scanning.py +0 -0
  295. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_schema_fidelity.py +0 -0
  296. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_scope_conformance.py +0 -0
  297. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_scoring_calibration.py +0 -0
  298. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_semantic_policy.py +0 -0
  299. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_setup_gate_completeness.py +0 -0
  300. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_setup_gate_independent_contract.py +0 -0
  301. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_setup_precondition.py +0 -0
  302. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_skill_install.py +0 -0
  303. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_source_index.py +0 -0
  304. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_spotbugs_adapter.py +0 -0
  305. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_spotbugs_catalog.py +0 -0
  306. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_suite_isolation.py +0 -0
  307. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_swift_declarations.py +0 -0
  308. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_tdd_structure.py +0 -0
  309. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_test_commands.py +0 -0
  310. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_test_execution_class.py +0 -0
  311. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_three_presentations.py +0 -0
  312. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_trends.py +0 -0
  313. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_type_fields_not_functions_class.py +0 -0
  314. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_uat_falsifiers.py +0 -0
  315. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_unread_code.py +0 -0
  316. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_user_config_tier.py +0 -0
  317. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_verification_audit_closures.py +0 -0
  318. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_version_claim.py +0 -0
  319. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_wheel_contents.py +0 -0
  320. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_work_order.py +0 -0
  321. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_work_order_items.py +0 -0
  322. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_work_order_selection.py +0 -0
  323. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_workflow_supply_chain.py +0 -0
  324. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_write_boundary.py +0 -0
  325. {maintainability_agent-2.7.0 → maintainability_agent-2.8.0}/tests/test_write_route_class.py +0 -0
  326. {maintainability_agent-2.7.0 → maintainability_agent-2.8.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: 2.7.0
3
+ Version: 2.8.0
4
4
  Summary: Maintainability gate and AI remediation prompt generator for CI.
5
5
  Author: Marshall Guillory
6
6
  License: MIT
@@ -44,7 +44,7 @@ Dynamic: license-file
44
44
  **A deterministic, offline maintainability audit whose output is a _bounded
45
45
  work order_ for an AI coding agent** — a copy-paste prompt, per finding, that
46
46
  says *fix exactly these and refactor nothing else*. Chat-primary; CLI for CI.
47
- Version **2.7.0**.
47
+ Version **2.8.0**.
48
48
 
49
49
  **Languages parsed:** Python, Java, C, C++, C#, Swift, Fortran (free-form
50
50
  *and* fixed-form), and the JS/TS family — each by a scanner written for it, and
@@ -383,8 +383,8 @@ Based on ISO/IEC 25010 maintainability — modularity, reusability,
383
383
  analyzability, modifiability, testability. Scores are **rates calibrated
384
384
  against real code**, not counts: every pressure is normalized against the
385
385
  median a pinned 112-repo corpus of mature projects actually carries —
386
- spanning every language the scanner parses (django, angular, spring-framework,
387
- ghidra, LAPACK, …). How that corpus was selected, what each language actually
386
+ eight of the ten parsed languages (django, angular, spring-framework,
387
+ ghidra, LAPACK, …); Swift and COBOL are parsed but unanchored. How that corpus was selected, what each language actually
388
388
  reads, and what the numbers do *not* establish:
389
389
  [the calibration study](docs/calibration-2.0-study.md), so `2.5x` means
390
390
  "two and a half times what well-maintained real code shows." The corpus median
@@ -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 **2.7.0**.
8
+ Version **2.8.0**.
9
9
 
10
10
  **Languages parsed:** Python, Java, C, C++, C#, Swift, Fortran (free-form
11
11
  *and* fixed-form), and the JS/TS family — each by a scanner written for it, and
@@ -344,8 +344,8 @@ Based on ISO/IEC 25010 maintainability — modularity, reusability,
344
344
  analyzability, modifiability, testability. Scores are **rates calibrated
345
345
  against real code**, not counts: every pressure is normalized against the
346
346
  median a pinned 112-repo corpus of mature projects actually carries —
347
- spanning every language the scanner parses (django, angular, spring-framework,
348
- ghidra, LAPACK, …). How that corpus was selected, what each language actually
347
+ eight of the ten parsed languages (django, angular, spring-framework,
348
+ ghidra, LAPACK, …); Swift and COBOL are parsed but unanchored. How that corpus was selected, what each language actually
349
349
  reads, and what the numbers do *not* establish:
350
350
  [the calibration study](docs/calibration-2.0-study.md), so `2.5x` means
351
351
  "two and a half times what well-maintained real code shows." The corpus median
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "maintainability-agent"
7
- version = "2.7.0"
7
+ version = "2.8.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: 2.7.0
3
+ Version: 2.8.0
4
4
  Summary: Maintainability gate and AI remediation prompt generator for CI.
5
5
  Author: Marshall Guillory
6
6
  License: MIT
@@ -44,7 +44,7 @@ Dynamic: license-file
44
44
  **A deterministic, offline maintainability audit whose output is a _bounded
45
45
  work order_ for an AI coding agent** — a copy-paste prompt, per finding, that
46
46
  says *fix exactly these and refactor nothing else*. Chat-primary; CLI for CI.
47
- Version **2.7.0**.
47
+ Version **2.8.0**.
48
48
 
49
49
  **Languages parsed:** Python, Java, C, C++, C#, Swift, Fortran (free-form
50
50
  *and* fixed-form), and the JS/TS family — each by a scanner written for it, and
@@ -383,8 +383,8 @@ Based on ISO/IEC 25010 maintainability — modularity, reusability,
383
383
  analyzability, modifiability, testability. Scores are **rates calibrated
384
384
  against real code**, not counts: every pressure is normalized against the
385
385
  median a pinned 112-repo corpus of mature projects actually carries —
386
- spanning every language the scanner parses (django, angular, spring-framework,
387
- ghidra, LAPACK, …). How that corpus was selected, what each language actually
386
+ eight of the ten parsed languages (django, angular, spring-framework,
387
+ ghidra, LAPACK, …); Swift and COBOL are parsed but unanchored. How that corpus was selected, what each language actually
388
388
  reads, and what the numbers do *not* establish:
389
389
  [the calibration study](docs/calibration-2.0-study.md), so `2.5x` means
390
390
  "two and a half times what well-maintained real code shows." The corpus median
@@ -11,6 +11,7 @@ src/maintainability_audit/__init__.py
11
11
  src/maintainability_audit/__main__.py
12
12
  src/maintainability_audit/_adapters.py
13
13
  src/maintainability_audit/_analysis.py
14
+ src/maintainability_audit/_anchor.py
14
15
  src/maintainability_audit/_arguments.py
15
16
  src/maintainability_audit/_aspects.py
16
17
  src/maintainability_audit/_attestation.py
@@ -143,6 +144,7 @@ tests/test_analyzer_provenance_exclusions.py
143
144
  tests/test_analyzer_scoring_path.py
144
145
  tests/test_analyzer_spread_range.py
145
146
  tests/test_analyzer_xml_bounds.py
147
+ tests/test_anchor_disclosure.py
146
148
  tests/test_annotations_match_behaviour.py
147
149
  tests/test_anticipated_refusals.py
148
150
  tests/test_architecture.py
@@ -283,6 +285,7 @@ tests/test_runner.py
283
285
  tests/test_sarif.py
284
286
  tests/test_scan_history.py
285
287
  tests/test_scan_scope.py
288
+ tests/test_scanner_unsafe_direction.py
286
289
  tests/test_scanning.py
287
290
  tests/test_schema_fidelity.py
288
291
  tests/test_scope_conformance.py
@@ -302,6 +305,7 @@ tests/test_tdd_structure.py
302
305
  tests/test_test_commands.py
303
306
  tests/test_test_execution_class.py
304
307
  tests/test_three_presentations.py
308
+ tests/test_tree_chosen_spawn.py
305
309
  tests/test_trends.py
306
310
  tests/test_type_fields_not_functions_class.py
307
311
  tests/test_uat_falsifiers.py
@@ -1,3 +1,3 @@
1
1
  """Maintainability Audit CI."""
2
2
 
3
- __version__ = "2.7.0"
3
+ __version__ = "2.8.0"
@@ -0,0 +1,49 @@
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. `tools/calibration/corpus.json` is a
29
+ #: repository file and does not ship in the wheel, so a runtime read would
30
+ #: be unavailable exactly where a user reads a grade.
31
+ #: `test_anchor_disclosure` recomputes `parsed - corpus` from both sources
32
+ #: and fails if this stops matching, so the list cannot quietly go stale.
33
+ UNANCHORED_LANGUAGES: tuple[str, ...] = ("Swift", "COBOL")
34
+
35
+
36
+ def unanchored_names() -> str:
37
+ """The languages, joined for a sentence. Empty when there are none."""
38
+ return " and ".join(UNANCHORED_LANGUAGES)
39
+
40
+
41
+ def unanchored_sentence() -> str:
42
+ """One sentence naming what the anchor omits, for the corpus note."""
43
+ if not UNANCHORED_LANGUAGES:
44
+ return ""
45
+ return (
46
+ f"{unanchored_names()} are parsed but absent from it, so their "
47
+ "findings are as good as their parser while their grade is "
48
+ "provisional."
49
+ )
@@ -132,10 +132,22 @@ about LAPACK relative to mature OSS *web* code, and not a statement about
132
132
  typical Fortran, because no typical Fortran was in the comparison set.
133
133
 
134
134
  **2.0.0 extended the corpus rather than the caveat.** It is now 112
135
- repositories spanning every language this scanner parses: Python,
136
- TypeScript, JavaScript, Java, C, C++, C# and Fortran. The original 40 rows
137
- are unchanged and still pinned to the commits they were measured at, so
138
- what moved the constants is the languages added and nothing else.
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
+ **Two parsed languages are not in it.** Swift (2.4.0) and COBOL (2.7.0)
141
+ shipped after that measurement and are **unanchored**: a scanner reads
142
+ them, and the medians their rates are compared against were measured on
143
+ 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.
139
151
 
140
152
  Three limits remain, and they are narrower than the one they replace:
141
153
 
@@ -35,11 +35,11 @@ import re
35
35
 
36
36
  from ._masking import mask_fortran_lines, mask_lines
37
37
 
38
- # Both live in `parsing`, and `_ranges_core` reaches nothing here, so this
39
- # is a sibling import rather than a cycle. COBOL's mask is not in
40
- # `_masking` with the others because it needs the whole file to know which
41
- # division a line is in — see `_ranges_cobol.mask_cobol_lines`.
42
- from ._ranges_cobol import mask_cobol_lines
38
+ # No COBOL masker is imported here, deliberately: `cobol_cognitive` takes
39
+ # lines that are already masked. COBOL's mask is the only file-level one —
40
+ # what a line means depends on which division it sits in — so re-masking
41
+ # the paragraph slice `detect_functions` hands over blanked it entirely
42
+ # and scored every COBOL paragraph 0. See `cobol_cognitive`.
43
43
 
44
44
  # Constructs that both cost a point and deepen the nesting for whatever
45
45
  # they contain.
@@ -207,8 +207,18 @@ def cobol_cognitive(lines: list[str]) -> int:
207
207
  a call and opens nothing; `PERFORM UNTIL DONE` is a loop and does.
208
208
  Counting every `PERFORM` as nesting would make an ordinary
209
209
  call-per-line paragraph read as deeply nested.
210
+
211
+ **Takes lines that are already masked, and does not mask them again.**
212
+ Every other reader here may re-mask freely because its masker is
213
+ line-local; COBOL's is the only file-level one, since deciding what a
214
+ line means requires knowing which division it sits in. `detect_functions`
215
+ hands this a *slice* of the masked file, and a paragraph slice contains
216
+ no `PROCEDURE DIVISION` — so re-masking blanked the whole thing and
217
+ every COBOL paragraph in production scored exactly 0, including deeply
218
+ nested ones. The souvenir test passed throughout because it called this
219
+ with a whole file. Found by a Grok audit on 2026-09-04.
210
220
  """
211
- masked = mask_cobol_lines(lines)
221
+ masked = lines
212
222
  score = 0
213
223
  depth = 0
214
224
  for line in masked:
@@ -339,3 +339,33 @@ def test_suite_lines(block: dict[str, Any] | None) -> list[str]:
339
339
  if detail and not block.get("passed"):
340
340
  lines.append(f"Detail: {detail}")
341
341
  return lines
342
+
343
+
344
+ def unanchored_languages(score: dict[str, Any]) -> tuple[str, ...]:
345
+ """Parsed languages the reference corpus does not hold, from the report.
346
+
347
+ Read off `score.reference` rather than imported from `_anchor`, so a
348
+ report rendered from stored JSON discloses what *that* run's anchor
349
+ omitted rather than what today's build omits.
350
+ """
351
+ return tuple((score.get("reference") or {}).get("unanchored_languages") or ())
352
+
353
+
354
+ def unanchored_caveat(score: dict[str, Any]) -> list[str]:
355
+ """The anchor's gap, in one sentence, for any skin that prints a grade.
356
+
357
+ Lives here because all three skins were about to carry their own copy
358
+ of the same lookup and the same sentence. `score.reference` rides on
359
+ the JSON report, and the JSON is not what most people read — a limit
360
+ disclosed only there is disclosed nowhere that matters, which is the
361
+ shape this project already shipped once as F1.
362
+ """
363
+ names = unanchored_languages(score)
364
+ if not names:
365
+ return []
366
+ return ["", (
367
+ f"*{' and '.join(names)} are parsed but are not in the reference "
368
+ "corpus, so a grade or multiple reported for code in them is "
369
+ "provisional: the findings are as good as the parser, the rate "
370
+ "they are compared against was measured on other languages.*"
371
+ ), ""]
@@ -89,6 +89,16 @@ def render_html(report: dict[str, Any], records: list[Any]) -> str:
89
89
  return "\n".join(parts)
90
90
 
91
91
 
92
+ def _unanchored_html(score: dict[str, Any]) -> list[str]:
93
+ """The anchor's gap, beside the letter rather than only in the JSON."""
94
+ names = view.unanchored_languages(score)
95
+ if not names:
96
+ return []
97
+ return [f"<div class='caveat'>{escape(' and '.join(names))} are parsed but "
98
+ "are not in the reference corpus, so a grade for code in them is "
99
+ "provisional.</div>"]
100
+
101
+
92
102
  def _executive_strip(report: dict[str, Any], score: dict[str, Any],
93
103
  records: list[Any]) -> list[str]:
94
104
  """The result, the gate, the finding counts, the direction. First."""
@@ -110,6 +120,7 @@ def _executive_strip(report: dict[str, Any], score: dict[str, Any],
110
120
  f"Source: {escape(view.estimate_source(score))}</div>",
111
121
  f"<div>{escape(view.status_sentence(score, report.get('analyzer_coverage') is not None))}</div>",
112
122
  f"<div>{gate}</div>",
123
+ *_unanchored_html(score),
113
124
  f"<div>{escape(_direction_sentence(records))}</div>",
114
125
  "<table><tr><th>Severity</th><th>Findings</th></tr>",
115
126
  *count_rows,
@@ -254,3 +254,47 @@ def mask_lines(lines: list[str]) -> list[str]:
254
254
  text, in_block, in_template = _mask_line(line, in_block, in_template)
255
255
  masked.append(text)
256
256
  return masked
257
+
258
+
259
+ #: Swift's multiline literal, in both spellings: `"""` and the raw form
260
+ #: `#"""` … `"""#` (any number of `#`). Matched loosely on purpose — the
261
+ #: delimiter is what matters, not how many hashes surround it.
262
+ _SWIFT_MULTILINE_RE = re.compile(r'#*"""#*')
263
+
264
+
265
+ def mask_swift_lines(lines: list[str]) -> list[str]:
266
+ """Swift, with multiline string literals blanked before anything reads it.
267
+
268
+ `mask_lines` is line-local: it blanks `"a"` on the line it appears on
269
+ and knows nothing about a literal that spans lines. Swift's
270
+ triple-quoted block therefore survived it, and every line between the
271
+ delimiters was read as code — so a `func` written inside a
272
+ documentation string came back as a declaration, with a name, a length
273
+ and a complexity nobody wrote.
274
+
275
+ `_ranges_java` discloses exactly this hole for Java text blocks. It is
276
+ disclosed there and closed here rather than copied: Swift's multiline
277
+ strings carry sample code far more often than Java's, and a
278
+ declaration invented out of documentation is the P7 failure — a number
279
+ a reader with the file in front of them would call absurd.
280
+
281
+ The delimiter lines keep their own text up to and after the marker, so
282
+ `let a = \"\"\"` still reads as an assignment. Length is preserved per
283
+ line, as everywhere else here, or every range after the literal would
284
+ shift.
285
+ """
286
+ masked: list[str] = []
287
+ inside = False
288
+ for line in lines:
289
+ out = []
290
+ position = 0
291
+ for match in _SWIFT_MULTILINE_RE.finditer(line):
292
+ segment = line[position:match.start()]
293
+ out.append(" " * len(segment) if inside else segment)
294
+ out.append(match.group(0))
295
+ inside = not inside
296
+ position = match.end()
297
+ rest = line[position:]
298
+ out.append(" " * len(rest) if inside else rest)
299
+ masked.append("".join(out))
300
+ return mask_lines(masked)
@@ -72,11 +72,21 @@ _SECTION_RE = re.compile(r"^\s{0,3}([A-Za-z0-9][A-Za-z0-9-]*)\s+SECTION\s*\.", r
72
72
  #: paragraph headers in columns 8-11 (Area A) and statements in 12-72
73
73
  #: (Area B); after the sequence and indicator columns are stripped, Area A
74
74
  #: is offset 0-3 and Area B begins at offset 4 — which is why the bound is
75
- #: three spaces and not four. A statement is therefore indented past this
76
- #: pattern and cannot be read as a paragraph. Written `{0,4}` first, and a
77
- #: masked `DISPLAY "A".` sitting at column 12 came straight back as a
78
- #: declaration named DISPLAY — one per statement in the file.
79
- _PARAGRAPH_RE = re.compile(r"^\s{0,3}([A-Za-z0-9][A-Za-z0-9-]*)\s*\.\s*$")
75
+ #: three spaces and not four. Written `{0,4}` first, and a masked
76
+ #: `DISPLAY "A".` sitting at column 12 came straight back as a declaration
77
+ #: named DISPLAY — one per statement in the file.
78
+ #:
79
+ #: **The period abuts the name**, and that is the load-bearing half. Area A
80
+ #: only separates a header from an *indented* statement; free-form COBOL
81
+ #: writes statements in column 1 too, and once `"A"` is blanked to spaces
82
+ #: `DISPLAY "A".` is a word, a gap, and a period — the exact shape of a
83
+ #: header. The gap is what gives it away, because a real header has
84
+ #: nothing between its name and its period. That rejects the whole class
85
+ #: of operand-bearing statements (`CALL "SUB".`, `MOVE 1 TO X.`) rather
86
+ #: than a list of verbs, which would need a new entry per verb forever.
87
+ #: `_STATEMENT_WORDS` still covers the operand-*less* ones, where no gap
88
+ #: exists to see.
89
+ _PARAGRAPH_RE = re.compile(r"^\s{0,3}([A-Za-z0-9][A-Za-z0-9-]*)\.\s*$")
80
90
  _DIVISION_RE = re.compile(r"^\s*[A-Za-z0-9-]+\s+DIVISION\s*\.", re.I)
81
91
  _PROCEDURE_RE = re.compile(r"^\s*PROCEDURE\s+DIVISION\b", re.I)
82
92
  _END_PROGRAM_RE = re.compile(r"^\s*END\s+PROGRAM\b", re.I)
@@ -41,6 +41,7 @@ from __future__ import annotations
41
41
 
42
42
  import re
43
43
 
44
+ from ._masking import mask_swift_lines
44
45
  from ._metrics_types import DeclRange
45
46
  from ._ranges_core import _mask_generics, scan_bounded
46
47
 
@@ -160,7 +161,11 @@ def swift_declaration_ranges(lines: list[str]) -> tuple[list[DeclRange], list[st
160
161
  disabled `#if` arm still counts.
161
162
  """
162
163
  ranges, masked = scan_bounded(
163
- lines, _swift_declaration, descend=("class",), skip_preprocessor=True
164
+ lines, _swift_declaration, descend=("class",), skip_preprocessor=True,
165
+ # Multiline literals are blanked before anything reads a line. The
166
+ # default line-local mask left a `func` inside a `"""` block
167
+ # looking like a declaration — see `mask_swift_lines`.
168
+ mask=mask_swift_lines,
164
169
  )
165
170
  return _qualify_extension_members(ranges, masked), masked
166
171
 
@@ -66,10 +66,11 @@ def local_tsc_analysis(root: Path) -> dict[str, Any] | None:
66
66
  Runs across the repository root *and* any workspace beneath it that
67
67
  carries a `tsconfig.json` — TS monorepos keep the config in `web/` or
68
68
  `packages/*`, not the root, and a root-only check went blind on exactly
69
- the repos this is for. Uses a `tsc` that is already present: the
70
- workspace's own `node_modules/.bin/tsc`, the root's, or one on the PATH.
71
- Never installs, never runs `npx --yes`, never touches the network — a
72
- project-local compiler is the standard install, not an acquisition.
69
+ the repos this is for. Uses a `tsc` **this machine** already provides,
70
+ found on PATH. Never installs, never runs `npx --yes`, never touches
71
+ the network — and never the audited tree's own
72
+ `node_modules/.bin/tsc`, which would let the repository choose the
73
+ binary this process runs (Decision 9; see `_tsc_on_path`).
73
74
 
74
75
  An absent checker, or a `tsc` that cannot run usably, leaves coverage
75
76
  *unknown*, not clean: a config error (FAILED, exit 3+) or a findings
@@ -81,13 +82,14 @@ def local_tsc_analysis(root: Path) -> dict[str, Any] | None:
81
82
  """
82
83
  diagnostics: list[dict[str, Any]] = []
83
84
  ran = False
85
+ if not _tsc_on_path():
86
+ return None
84
87
  for project in _tsconfig_project_dirs(root):
85
- tsc = _resolve_tsc(root, project)
86
- if tsc is None:
87
- continue
88
88
  result = run(
89
89
  "typescript",
90
- Invocation(argv=(tsc, "--noEmit", "--pretty", "false"),
90
+ # A constant, resolved by PATH. Never a path built from the
91
+ # audited tree — see `_tsc_on_path`.
92
+ Invocation(argv=("tsc", "--noEmit", "--pretty", "false"),
91
93
  findings_exit_codes=(0, 1, 2)),
92
94
  cwd=project,
93
95
  )
@@ -125,14 +127,28 @@ def _tsconfig_project_dirs(root: Path) -> list[Path]:
125
127
  return sorted(dirs)
126
128
 
127
129
 
128
- def _resolve_tsc(root: Path, project: Path) -> str | None:
129
- """An already-installed `tsc`: the workspace's own, then the root's,
130
- then one on the PATH. Never installs and never fetches."""
131
- for base in (project, root):
132
- local = base / "node_modules" / ".bin" / "tsc"
133
- if local.is_file():
134
- return str(local)
135
- return "tsc" if locate("tsc") else None
130
+ def _tsc_on_path() -> bool:
131
+ """Whether a `tsc` **this machine** provides is available.
132
+
133
+ PATH only, and that is the whole point. This preferred the audited
134
+ tree's own `node_modules/.bin/tsc` and fell back to PATH, which let
135
+ the repository under audit choose the binary this process executes —
136
+ a `tsconfig.json` and a writable `node_modules/.bin/tsc` are all a
137
+ hostile tree needs, and it does not even have to contain TypeScript.
138
+
139
+ SECURITY.md says the sole path by which this agent executes code from
140
+ the tree is the operator's opt-in test command (Decision 9). That was
141
+ false while this function existed, so the resolution is deleted
142
+ rather than guarded: there is no safe way to run a binary whose
143
+ identity the audited repository controls.
144
+
145
+ The cost is disclosed and small. A project whose only compiler is
146
+ project-local now leaves type coverage **unknown** rather than clean,
147
+ which is the honest fallback the evidence model already has a word
148
+ for — and reading an absent checker as a clean one is the
149
+ absence-as-a-pass this module's own docstring forbids.
150
+ """
151
+ return locate("tsc") is not None
136
152
 
137
153
 
138
154
  def _parse_diagnostics(stdout: str, prefix: Path) -> list[dict[str, Any]]:
@@ -11,7 +11,7 @@ from ._config_defaults import DEFAULT_CONFIG, DEFAULT_IDIOM_GROUPS
11
11
  # still the door every caller comes through.
12
12
  __all__ = ["DEFAULT_CONFIG", "DEFAULT_IDIOM_GROUPS", "PROJECT_URL", "VERSION"]
13
13
 
14
- VERSION = "2.7.0"
14
+ VERSION = "2.8.0"
15
15
 
16
16
  PROJECT_URL = "https://github.com/marshallguillory86/maintainability-agent"
17
17
 
@@ -258,13 +258,14 @@ def prompt_pressure_section(score: dict[str, Any]) -> list[str]:
258
258
  if value is not None and value > 1.0),
259
259
  key=lambda item: -item[1],
260
260
  )
261
- lines: list[str] = []
261
+ # Unanchored languages are provisional here too, elevated or not.
262
+ lines: list[str] = view.unanchored_caveat(score)
262
263
  if elevated:
263
264
  lines.extend(
264
265
  [
265
266
  "Where this repo is worse than typical real-world code",
266
- "(1.0x = the median of a mature open-source corpus of Python, TypeScript "
267
- "and JavaScript; only elevated dimensions are listed):",
267
+ "(1.0x = the median of a mature open-source corpus of eight "
268
+ "of the ten parsed languages; elevated dimensions only):",
268
269
  "",
269
270
  ]
270
271
  )
@@ -63,6 +63,7 @@ def summary_table(summary: dict[str, int], score: dict[str, Any],
63
63
  f"| Duplicate blocks | {summary['duplicate_blocks']} |",
64
64
  f"| Risk findings | {summary['risk_findings']} |",
65
65
  f"| Hard gate failures | {summary['hard_gate_failures']} |",
66
+ *view.unanchored_caveat(score),
66
67
  ]
67
68
 
68
69
 
@@ -37,6 +37,7 @@ from __future__ import annotations
37
37
  from math import inf
38
38
  from typing import Any
39
39
 
40
+ from ._anchor import UNANCHORED_LANGUAGES, unanchored_sentence
40
41
  from ._aspects import (
41
42
  aspect_scores,
42
43
  evidence_aspect_scores,
@@ -415,9 +416,12 @@ def _reference_block() -> dict[str, object]:
415
416
  "corpus_languages": [
416
417
  "Python", "TypeScript", "JavaScript", "Java", "C", "C++", "C#", "Fortran",
417
418
  ],
419
+ # Parsed by a scanner, absent from the anchor — see `_anchor`.
420
+ "unanchored_languages": UNANCHORED_LANGUAGES,
418
421
  "corpus_note": (
419
422
  "Reference medians are drawn from 112 mature repositories across "
420
- "every language this scanner parses. Fortran entered at a lower "
423
+ "eight of the ten languages this scanner parses. "
424
+ f"{unanchored_sentence()} Fortran entered at a lower "
421
425
  "star threshold than the rest, because its ecosystem has none "
422
426
  "above theirs; see docs/standard.md#the-reference-corpus."
423
427
  ),