maintainability-agent 2.11.2__tar.gz → 3.0.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.
- {maintainability_agent-2.11.2/src/maintainability_agent.egg-info → maintainability_agent-3.0.0}/PKG-INFO +5 -3
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/README.md +4 -2
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/pyproject.toml +1 -1
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0/src/maintainability_agent.egg-info}/PKG-INFO +5 -3
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_agent.egg-info/SOURCES.txt +5 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/__init__.py +1 -1
- maintainability_agent-3.0.0/src/maintainability_audit/_analyzer_sections.py +85 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_calibration.py +89 -5
- maintainability_agent-3.0.0/src/maintainability_audit/_criteria_scope.py +279 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_formula.py +12 -2
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_pressures.py +153 -68
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/config.py +1 -1
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/report.py +23 -61
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/scoring.py +3 -2
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_analyzer_scoring_path.py +2 -2
- maintainability_agent-3.0.0/tests/test_criteria_completion.py +182 -0
- maintainability_agent-3.0.0/tests/test_declaration_criteria_scope.py +312 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_environment_work_order.py +1 -1
- maintainability_agent-3.0.0/tests/test_grammar_token_coverage.py +209 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_pool_runs_by_config.py +35 -5
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_stage8_contract.py +6 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_three_presentations.py +1 -1
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_verified_grade.py +4 -3
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_version_claim.py +9 -2
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/LICENSE +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/setup.cfg +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_agent.egg-info/dependency_links.txt +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_agent.egg-info/entry_points.txt +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_agent.egg-info/requires.txt +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_agent.egg-info/top_level.txt +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/__main__.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_adapters.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_analysis.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_anchor.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_arguments.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_aspects.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_assets/analyzer-catalog.json +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_assets/standard.md +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_attestation.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_backfill.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_bands.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_banner.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_built_ins.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_catalog.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_charts.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_cognitive.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_config_defaults.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_conformance.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_corroborate.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_coverage_notes.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_derive.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_discovery.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_documents.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_economics.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_economics_view.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_environment.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_evidence_view.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_finding_match.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_first_run.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_gates.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_generic.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_grant_ledger.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_history_view.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_hostile_prompt.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_hotspots.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_html_report_sections.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_html_view.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_identity.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_in_loop.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_in_loop_view.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_jvm_adapters.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_markdown_sections.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_masking.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_mcp_audit.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_mcp_gate.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_mcp_grants.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_mcp_refusals.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_mcp_resources.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_mcp_setup.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_metric_adapters.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_metrics_types.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_operator_reads.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_pillars.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_practice.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_precommit.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_precommit_install.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_precommit_view.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_ranges_c.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_ranges_cobol.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_ranges_core.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_ranges_cpp.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_ranges_csharp.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_ranges_fortran.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_ranges_go.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_ranges_java.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_ranges_js.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_ranges_php.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_ranges_ruby.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_ranges_rust.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_ranges_swift.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_ratchet.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_recurrence.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_run_comparison.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_runner.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_safe_write.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_scan_history.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_scan_view.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_second_source.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_selection.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_semantic.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_semantic_policy.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_semantic_ts.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_semantic_view.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_setup_errors.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_setup_persist.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_skill_data/SKILL.md +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_skill_data/agents/anthropic.yaml +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_skill_data/agents/copilot.yaml +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_skill_data/agents/openai.yaml +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_skill_data/copilot/maintainability-agent.prompt.md +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_skill_data/references/finding-taxonomy.md +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_skill_install.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_stored_grants.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_tdd_view.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_test_commands.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_test_execution.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_test_pairing.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_tokens.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_tool_adapters.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_trends.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_user_config.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_verdict_adapters.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_verification.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_work_order.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_work_order_view.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_work_order_weights.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_xml.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/baseline.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/cli.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/deadcode.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/declarations.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/duplication.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/evidence.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/git_tools.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/history.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/idioms.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/instructions.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/mcp_server.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/metrics.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/prompts.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/renderers.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/sarif.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/similarity.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/source.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_acquisition_trust.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_adapter_docs.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_adapter_recordings.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_adapters.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_added_lines.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_analysis_coverage.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_analysis_execution.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_analyzer_bridge.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_analyzer_config_isolation.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_analyzer_estimate_claims.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_analyzer_pinning.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_analyzer_primary_estimate.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_analyzer_provenance.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_analyzer_provenance_exclusions.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_analyzer_spread_range.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_analyzer_xml_bounds.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_anchor_and_mode.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_anchor_disclosure.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_annotations_match_behaviour.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_anticipated_refusals.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_architecture.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_artifact_write_route_class.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_aspect_coverage.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_asset_snapshot_class.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_ast_reading_resolves_spellings.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_attestation.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_audit_components.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_authorization_freshness.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_authorship_gates.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_backfill.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_band_concealment_class.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_band_concealment_p3.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_band_pressures.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_bands.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_body_or_signature_class.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_c_declarations.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_calibration_corpus.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_calibration_drift.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_calibration_tooling.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_catalog_selection.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_catalog_tier_promise.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_chat_primary_docs.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_checkstyle_adapter.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_checkstyle_catalog.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_ci_history_recipe.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_ci_installs_the_analyzer_pool.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_claimed_languages.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_cli.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_clone_group_class.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_cobol_declarations.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_cognitive_complexity.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_cohort_statistics.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_concealment_properties.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_config_shape.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_consent_and_grant.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_consumer_migration.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_contract_claims.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_corroborate.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_coverage_by_language.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_cpp_declarations.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_csharp_declarations.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_d15_composition.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_d15_goal_directed.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_dead_code.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_declaration_edge_cases.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_declaration_grading.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_declaration_ranges.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_declaration_source.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_declared_authorship.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_declared_imports.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_derivation_mix.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_determinism.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_dimension_ratchet.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_direct_runner_availability_class.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_discovery.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_doc_claims.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_docs_links.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_economic_context.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_empty_output_not_clean.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_empty_output_not_clean_adapters.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_evidence_invariants.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_evidence_normalization.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_evidence_properties.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_exclusion_dialects.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_falsifier_standard.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_file_list_cap_class.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_finding_identity.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_first_run_elicitation.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_first_run_prompt.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_first_run_reelicitation.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_fix_breadth_window.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_fixed_form_fortran.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_format_ask.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_fortran_declarations.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_fortran_metrics.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_fortran_practice.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_git_argv.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_git_read_only.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_git_readonly_class.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_go_declarations.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_grammar_constructs.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_grant_only_user_tier.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_history.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_history_boundary.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_history_manifest.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_history_schema2.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_history_window.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_hostile_prompt.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_html_executive.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_html_section_parity_class.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_identity_docs.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_identity_resolution.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_idiom_divergence.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_in_loop_budgets.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_in_loop_check.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_installed_wheel_runtime.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_instruction_targets.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_java_range_guard.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_java_ranges.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_java_scored.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_java_wiring.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_js_complexity_operators.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_js_declarations.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_language_coverage.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_mcp_baseline_payload.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_mcp_history.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_mcp_server.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_migration_1_0.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_near_duplicates.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_network_disclosure.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_one_setup_across_surfaces_class.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_operator_named_paths.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_operator_named_reads.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_pattern_budget.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_phase6_claims.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_php_declarations.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_pillars_and_practice.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_platform_claim.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_pmd_adapter.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_pmd_audit_closures.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_policy_claims.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_population_floors.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_practice_nested_gates_class.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_precommit_staged.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_presentation_refusals.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_promises.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_prompt_escalation_once.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_prompt_honesty.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_prompt_location_escalation_class.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_prove_falsifiers.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_python_complexity.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_python_declarations.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_read_boundary.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_readme_claims.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_recurrence.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_release_plan.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_release_plan_status.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_report_remedy_copy.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_roles_recorded.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_root_grants.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_rubric_ignores_project_lint_config.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_ruby_declarations.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_run_comparison.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_runner.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_rust_declarations.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_sarif.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_scan_history.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_scan_scope.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_scanner_unsafe_direction.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_scanning.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_schema_fidelity.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_scope_conformance.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_scoring_calibration.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_semantic_policy.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_setup_gate_completeness.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_setup_gate_independent_contract.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_setup_precondition.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_skill_install.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_sonar_resolve.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_source_index.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_spotbugs_adapter.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_spotbugs_catalog.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_suite_isolation.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_swift_declarations.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_tdd_structure.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_test_commands.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_test_execution_class.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_tree_chosen_spawn.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_trends.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_type_fields_not_functions_class.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_uat_falsifiers.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_unread_code.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_user_config_tier.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_verification_audit_closures.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_wheel_contents.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_work_order.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_work_order_items.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_work_order_selection.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_workflow_supply_chain.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_write_boundary.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/tests/test_write_route_class.py +0 -0
- {maintainability_agent-2.11.2 → maintainability_agent-3.0.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
|
+
Version: 3.0.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 **
|
|
48
|
+
Version **3.0.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
|
|
@@ -397,7 +397,9 @@ findings — is in the report itself.
|
|
|
397
397
|
## Platform support
|
|
398
398
|
|
|
399
399
|
**POSIX. Windows is not claimed, for a measured reason.** A `windows-latest` probe
|
|
400
|
-
found most failures in
|
|
400
|
+
found most failures in three POSIX-only calls: `os.fchmod`, `os.O_DIRECTORY` and
|
|
401
|
+
`os.O_NONBLOCK`. The third arrived with the one-handle operator read (D130/D131)
|
|
402
|
+
and is now the dominant failure in the probe, which is why the count moved. The
|
|
401
403
|
bounded, symlink-refusing write works through a *file descriptor*, so a validated
|
|
402
404
|
path cannot be swapped for a symlink before the write lands — Windows has no
|
|
403
405
|
equivalent and the portable rewrite is the hole. **An unsupported platform never
|
|
@@ -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 **
|
|
8
|
+
Version **3.0.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
|
|
@@ -357,7 +357,9 @@ findings — is in the report itself.
|
|
|
357
357
|
## Platform support
|
|
358
358
|
|
|
359
359
|
**POSIX. Windows is not claimed, for a measured reason.** A `windows-latest` probe
|
|
360
|
-
found most failures in
|
|
360
|
+
found most failures in three POSIX-only calls: `os.fchmod`, `os.O_DIRECTORY` and
|
|
361
|
+
`os.O_NONBLOCK`. The third arrived with the one-handle operator read (D130/D131)
|
|
362
|
+
and is now the dominant failure in the probe, which is why the count moved. The
|
|
361
363
|
bounded, symlink-refusing write works through a *file descriptor*, so a validated
|
|
362
364
|
path cannot be swapped for a symlink before the write lands — Windows has no
|
|
363
365
|
equivalent and the portable rewrite is the hole. **An unsupported platform never
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "maintainability-agent"
|
|
7
|
-
version = "
|
|
7
|
+
version = "3.0.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
|
+
Version: 3.0.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 **
|
|
48
|
+
Version **3.0.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
|
|
@@ -397,7 +397,9 @@ findings — is in the report itself.
|
|
|
397
397
|
## Platform support
|
|
398
398
|
|
|
399
399
|
**POSIX. Windows is not claimed, for a measured reason.** A `windows-latest` probe
|
|
400
|
-
found most failures in
|
|
400
|
+
found most failures in three POSIX-only calls: `os.fchmod`, `os.O_DIRECTORY` and
|
|
401
|
+
`os.O_NONBLOCK`. The third arrived with the one-handle operator read (D130/D131)
|
|
402
|
+
and is now the dominant failure in the probe, which is why the count moved. The
|
|
401
403
|
bounded, symlink-refusing write works through a *file descriptor*, so a validated
|
|
402
404
|
path cannot be swapped for a symlink before the write lands — Windows has no
|
|
403
405
|
equivalent and the portable rewrite is the hole. **An unsupported platform never
|
|
@@ -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/_analyzer_sections.py
|
|
14
15
|
src/maintainability_audit/_anchor.py
|
|
15
16
|
src/maintainability_audit/_arguments.py
|
|
16
17
|
src/maintainability_audit/_aspects.py
|
|
@@ -27,6 +28,7 @@ src/maintainability_audit/_config_defaults.py
|
|
|
27
28
|
src/maintainability_audit/_conformance.py
|
|
28
29
|
src/maintainability_audit/_corroborate.py
|
|
29
30
|
src/maintainability_audit/_coverage_notes.py
|
|
31
|
+
src/maintainability_audit/_criteria_scope.py
|
|
30
32
|
src/maintainability_audit/_derive.py
|
|
31
33
|
src/maintainability_audit/_discovery.py
|
|
32
34
|
src/maintainability_audit/_documents.py
|
|
@@ -199,10 +201,12 @@ tests/test_contract_claims.py
|
|
|
199
201
|
tests/test_corroborate.py
|
|
200
202
|
tests/test_coverage_by_language.py
|
|
201
203
|
tests/test_cpp_declarations.py
|
|
204
|
+
tests/test_criteria_completion.py
|
|
202
205
|
tests/test_csharp_declarations.py
|
|
203
206
|
tests/test_d15_composition.py
|
|
204
207
|
tests/test_d15_goal_directed.py
|
|
205
208
|
tests/test_dead_code.py
|
|
209
|
+
tests/test_declaration_criteria_scope.py
|
|
206
210
|
tests/test_declaration_edge_cases.py
|
|
207
211
|
tests/test_declaration_grading.py
|
|
208
212
|
tests/test_declaration_ranges.py
|
|
@@ -241,6 +245,7 @@ tests/test_git_read_only.py
|
|
|
241
245
|
tests/test_git_readonly_class.py
|
|
242
246
|
tests/test_go_declarations.py
|
|
243
247
|
tests/test_grammar_constructs.py
|
|
248
|
+
tests/test_grammar_token_coverage.py
|
|
244
249
|
tests/test_grant_only_user_tier.py
|
|
245
250
|
tests/test_history.py
|
|
246
251
|
tests/test_history_boundary.py
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""The analyzer half of a report, and the join that completes it.
|
|
2
|
+
|
|
3
|
+
Split from `report.py` at the 500-line gate this tool enforces on
|
|
4
|
+
everyone else. The seam is real rather than arithmetic: everything here
|
|
5
|
+
is about the external analyzer tier — what it measured, what it could
|
|
6
|
+
not, and how its criterion set is completed from the built-in scanner so
|
|
7
|
+
a language no tool reads cognitively can still be analyzer-driven.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from ._analysis import analyze
|
|
15
|
+
from ._criteria_scope import built_in_readings
|
|
16
|
+
from ._documents import (
|
|
17
|
+
coverage_document,
|
|
18
|
+
findings_document,
|
|
19
|
+
measurement_document,
|
|
20
|
+
unidentified_source_paths,
|
|
21
|
+
)
|
|
22
|
+
from ._environment import environment_work_order
|
|
23
|
+
from ._metrics_types import FunctionMetric
|
|
24
|
+
from ._pressures import (
|
|
25
|
+
ExternalPressures,
|
|
26
|
+
analyzer_pressures,
|
|
27
|
+
analyzer_production_pressures,
|
|
28
|
+
declined_dimensions,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def analyzer_sections(
|
|
33
|
+
root: Path,
|
|
34
|
+
config: dict[str, Any],
|
|
35
|
+
run_analyzers: bool,
|
|
36
|
+
function_metrics: list[FunctionMetric],
|
|
37
|
+
) -> dict[str, Any]:
|
|
38
|
+
"""The three analyzer sections, or their empty forms.
|
|
39
|
+
|
|
40
|
+
Extracted because `build_report` reached complexity 15 against this
|
|
41
|
+
project's own limit. Four conditionals all asking the same question
|
|
42
|
+
belong behind one, and the empty forms are stated here rather than
|
|
43
|
+
repeated at each call site.
|
|
44
|
+
"""
|
|
45
|
+
if not run_analyzers:
|
|
46
|
+
return {"coverage": None, "findings": [], "measurements": {},
|
|
47
|
+
"pressures": None, "environment": []}
|
|
48
|
+
analysis = analyze(root, config)
|
|
49
|
+
coverage = coverage_document(analysis)
|
|
50
|
+
# P8: where the analyzer tier could not drive a dimension, say so on
|
|
51
|
+
# the coverage document rather than leaving the built-in fallback to
|
|
52
|
+
# be inferred from an absent number. Attached here because only this
|
|
53
|
+
# layer holds both the measurements and the rubric's thresholds.
|
|
54
|
+
# The scanner's own reading of each declaration, so the analyzer tier
|
|
55
|
+
# can complete a criterion set no single tool covers (per concept,
|
|
56
|
+
# not per dimension). Keyed the way the join is proven: (path, line).
|
|
57
|
+
prefix = root.as_posix().rstrip("/") + "/"
|
|
58
|
+
built_in = built_in_readings(function_metrics, prefix)
|
|
59
|
+
coverage["dimensions_declined"] = list(
|
|
60
|
+
declined_dimensions(analysis.measurements, False, built_in, prefix))
|
|
61
|
+
return {
|
|
62
|
+
"coverage": coverage,
|
|
63
|
+
# ADR 006 §2c: what did not run and what it would take, for the
|
|
64
|
+
# user to act on. Emitted here because only the analysis knows
|
|
65
|
+
# which tools were *selected*; the agent never runs the commands.
|
|
66
|
+
"environment": environment_work_order(analysis.coverage),
|
|
67
|
+
"findings": findings_document(analysis, root),
|
|
68
|
+
# Path spellings normalization refused (zero or several matches)
|
|
69
|
+
# — visible, so a refusal is never mistaken for an identification.
|
|
70
|
+
"unidentified_source_paths": unidentified_source_paths(analysis, root),
|
|
71
|
+
"measurements": measurement_document(analysis, root),
|
|
72
|
+
# The analyzers' reading of the scorer's dimensions, primary for
|
|
73
|
+
# each it covers (ADR 006 §1); where None the built-in stands, so
|
|
74
|
+
# an unmeasured dimension is unmeasured, not clean. Both
|
|
75
|
+
# populations, since the production aspects read this.
|
|
76
|
+
"pressures": ExternalPressures(
|
|
77
|
+
all_code=analyzer_pressures(
|
|
78
|
+
analysis.measurements, config["thresholds"], built_in, prefix),
|
|
79
|
+
production=analyzer_production_pressures(
|
|
80
|
+
analysis.measurements, config["thresholds"], built_in, prefix),
|
|
81
|
+
# The raw readings ride along so the scorer can price
|
|
82
|
+
# per-concept tool disagreement into the range (3.4).
|
|
83
|
+
measurements=tuple(analysis.measurements),
|
|
84
|
+
),
|
|
85
|
+
}
|
|
@@ -107,11 +107,94 @@ CATEGORIES = ["modularity", "reusability", "analyzability", "modifiability", "te
|
|
|
107
107
|
# the wrong unit of analysis; the languages do not behave as a bloc, and a
|
|
108
108
|
# single dramatic repository predicted the direction of the whole corpus
|
|
109
109
|
# incorrectly. Per-language readings are in `docs/calibration-2.0-study.md`.
|
|
110
|
+
#
|
|
111
|
+
# **2026-09-07, re-measured; the constants held.** The stored rows had been
|
|
112
|
+
# measured at tool versions 1.10.1 and 2.0.0 under scanner fingerprint
|
|
113
|
+
# 3b5e063e95316342. HEAD is 2.11.2 under 1a902618a0600746 — eleven minor
|
|
114
|
+
# releases of scanner work later, including four new language scanners and
|
|
115
|
+
# the D112-D136 fixes. All 112 repositories were re-measured
|
|
116
|
+
# `--with-analyzers` at the same pinned commits, so every move below is
|
|
117
|
+
# attributable to the scanners and to nothing else:
|
|
118
|
+
#
|
|
119
|
+
# file_size 0.0952 -> 0.0952 unchanged
|
|
120
|
+
# declarations 0.0908 -> 0.0903 92 of 112 rows moved
|
|
121
|
+
# duplication 0.3222 -> 0.3222 unchanged
|
|
122
|
+
# risk 0.0826 -> 0.0811 21 rows moved
|
|
123
|
+
# gates 0.05 -> 0.05 fixed, not measured
|
|
124
|
+
# CALIBRATION_C 8.7161 -> 8.7161 re-bisected to the same value
|
|
125
|
+
#
|
|
126
|
+
# The constants are deliberately left as they stand. Both movers sit well
|
|
127
|
+
# inside the 10% the suite tolerates (0.55% and 1.8%), and the curve
|
|
128
|
+
# re-fitted to four identical decimals, so editing them would change the
|
|
129
|
+
# published numbers by less than the sampling error in the numbers
|
|
130
|
+
# themselves - the argument `test_the_curve_constant_still_does_its_job`
|
|
131
|
+
# already makes for c.
|
|
132
|
+
#
|
|
133
|
+
# **No repository changes grade.** Old corpus and new, under the shipped
|
|
134
|
+
# constants and under the re-derived ones, all give A=5, B=52, C=55 with a
|
|
135
|
+
# median of exactly 4.0000. Two repositories move at the published decimal
|
|
136
|
+
# at all: hoppscotch 3.6 -> 3.7 and googletest 4.1 -> 4.0.
|
|
137
|
+
#
|
|
138
|
+
# This is worth recording because the roadmap predicted the opposite - that
|
|
139
|
+
# the recalibration "re-grades every repository, which is a breaking change
|
|
140
|
+
# to every published number". For *scanner* drift that is now measured, and
|
|
141
|
+
# it is false: 92 of 112 declaration readings moved underneath an anchor
|
|
142
|
+
# that did not. It says nothing about extending the corpus to hold the six
|
|
143
|
+
# unanchored languages, which moves the population the medians are drawn
|
|
144
|
+
# from rather than the instrument reading it. That half remains untested.
|
|
145
|
+
#
|
|
146
|
+
# **2026-09-08, the recalibration (3.0.0).** The one this corpus policy
|
|
147
|
+
# scheduled, and the first that re-grades repositories. Three things
|
|
148
|
+
# changed at once and the release exists because they cannot be
|
|
149
|
+
# separated — each moves `scanner_fingerprint` and invalidates every
|
|
150
|
+
# stored row.
|
|
151
|
+
#
|
|
152
|
+
# *The corpus grew to 180.* Swift, Go, Rust, PHP and Ruby are anchored,
|
|
153
|
+
# fourteen repositories each but for Ruby's twelve. COBOL is not, and
|
|
154
|
+
# will not be: one public repository clears 500 stars and what exists is
|
|
155
|
+
# curriculum, Java parsers and JavaScript wrappers rather than COBOL
|
|
156
|
+
# codebases. The 112 existing rows keep their pinned commits.
|
|
157
|
+
#
|
|
158
|
+
# *The analyzer tier now drives declarations almost everywhere.* Under
|
|
159
|
+
# 2.11.2 the criterion set could only be completed for Python, so 6 rows
|
|
160
|
+
# of 180 carried an analyzer reading. Completing it from the built-in
|
|
161
|
+
# tier per concept — `lizard` for cyclomatic complexity and declaration
|
|
162
|
+
# lines, the scanner for the cognitive complexity no FOSS tool supplies
|
|
163
|
+
# — takes that to **173 of 180**.
|
|
164
|
+
#
|
|
165
|
+
# *D137 fixed the criterion check's scope*, so one Python file can no
|
|
166
|
+
# longer buy the criterion for a repository's C.
|
|
167
|
+
#
|
|
168
|
+
# file_size 0.0952 -> 0.0938
|
|
169
|
+
# declarations 0.0908 -> 0.0962
|
|
170
|
+
# duplication 0.3222 -> 0.3144
|
|
171
|
+
# risk 0.0826 -> 0.0341
|
|
172
|
+
# gates 0.05 -> 0.05
|
|
173
|
+
# CALIBRATION_C 8.7161 -> 4.2565
|
|
174
|
+
#
|
|
175
|
+
# **`risk` more than halved, and the languages explain it.** Risk
|
|
176
|
+
# patterns are configured policy and this project's defaults were written
|
|
177
|
+
# against Python and C. Measured as a multiple of the new reference, the
|
|
178
|
+
# five added languages read swift 0.00x, php 0.01x, ruby 0.04x, go 0.10x
|
|
179
|
+
# and rust 0.26x, against c 5.82x, cpp 4.95x and python 4.24x. The median
|
|
180
|
+
# fell because the corpus stopped being dominated by the two ecosystems
|
|
181
|
+
# the default patterns were written for. That is the anchor working: the
|
|
182
|
+
# old reference was measuring the pattern set, not the code.
|
|
183
|
+
#
|
|
184
|
+
# **This release re-grades repositories, which the previous one did not.**
|
|
185
|
+
# 33 of the 112 repositories in both corpora change grade — 15 B->C,
|
|
186
|
+
# 7 C->B, 5 A->B, 4 C->D and 2 B->A — and the corpus gains its first D
|
|
187
|
+
# grades. The 2026-09-07 re-measure moved nothing and said so; this one
|
|
188
|
+
# moves a third of them. Both statements are measured, which is why only
|
|
189
|
+
# this one is a major version.
|
|
190
|
+
#
|
|
191
|
+
# The median still rolls up to exactly 4.0000, which is the anchor's
|
|
192
|
+
# whole job and the one thing that must not move.
|
|
110
193
|
DIMENSION_REFERENCES: dict[str, float] = {
|
|
111
|
-
"file_size": 0.
|
|
112
|
-
"declarations": 0.
|
|
113
|
-
"duplication": 0.
|
|
114
|
-
"risk": 0.
|
|
194
|
+
"file_size": 0.0938,
|
|
195
|
+
"declarations": 0.0962,
|
|
196
|
+
"duplication": 0.3144,
|
|
197
|
+
"risk": 0.0341,
|
|
115
198
|
# Fixed, not corpus-derived — see ``_derive.FIXED_REFERENCES``, which
|
|
116
199
|
# is the authority for this value and carries the reasoning. Stated
|
|
117
200
|
# again here rather than imported, so the two are independent claims
|
|
@@ -175,7 +258,8 @@ DIMENSION_WEIGHTS: dict[str, float] = {
|
|
|
175
258
|
# 2026-08-14: 2.6279 -> 2.2658, fitted against the analyzer-primary mix.
|
|
176
259
|
# 2026-08-31: 2.2658 -> 5.8843, re-fitted after the corpus re-measure above
|
|
177
260
|
# (three of five references moved at once, `duplication` most of all).
|
|
178
|
-
|
|
261
|
+
# 2026-09-08: 8.7161 -> 4.2565, the 3.0.0 recalibration above.
|
|
262
|
+
CALIBRATION_C = 4.2565
|
|
179
263
|
|
|
180
264
|
# A failure is a threshold breach; a warning is an approach to one.
|
|
181
265
|
WARN_WEIGHT = 0.3
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"""Which declarations the analyzer tier may score — the D137 seam.
|
|
2
|
+
|
|
3
|
+
Split out of `_pressures.py` when that module crossed the 500-line gate
|
|
4
|
+
this tool enforces on everyone else. The seam is real rather than
|
|
5
|
+
arithmetic: everything here answers one question — *may this declaration
|
|
6
|
+
be scored by an analyzer reading at all?* — and nothing else answers it.
|
|
7
|
+
|
|
8
|
+
That question has been got wrong five times. `test_analyzer_bridge`
|
|
9
|
+
records the first four, each a ratio published before the comparison
|
|
10
|
+
could be trusted. The fifth, D137, was this: the completeness check took
|
|
11
|
+
a union of concepts across the whole repository while the units it gated
|
|
12
|
+
were scored one at a time, so a C repository holding a single `setup.py`
|
|
13
|
+
had its C declarations scored on two criteria of three. Keeping the
|
|
14
|
+
decision in one module, returning the population rather than a verdict,
|
|
15
|
+
is what stops a sixth from arriving by scoping the check differently
|
|
16
|
+
from the thing it gates.
|
|
17
|
+
"""
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import posixpath
|
|
21
|
+
from collections import defaultdict
|
|
22
|
+
from pathlib import PurePosixPath
|
|
23
|
+
|
|
24
|
+
from ._metrics_types import KNOWN_SOURCE_SUFFIXES, Measurement
|
|
25
|
+
|
|
26
|
+
# The concepts the `declarations` dimension needs, and the rubric
|
|
27
|
+
# thresholds that decide a breach. All three, because `function_status`
|
|
28
|
+
# fails a declaration on **lines OR complexity OR cognitive complexity**
|
|
29
|
+
# — a bridge counting only complexity measures something narrower and
|
|
30
|
+
# cannot be compared against it. Three ratios were quoted from that
|
|
31
|
+
# mistake before a test comparing every criterion caught it.
|
|
32
|
+
DECLARATION_CRITERIA: tuple[tuple[str, str, str], ...] = (
|
|
33
|
+
("cyclomatic_complexity", "warn_complexity", "max_complexity"),
|
|
34
|
+
("declaration_lines", "warn_function_lines", "max_function_lines"),
|
|
35
|
+
("cognitive_complexity", "warn_cognitive_complexity", "max_cognitive_complexity"),
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Dimensions an analyzer can supply at all. `file_size` needs per-file
|
|
39
|
+
# line counts no permissively-licensed tool in the pool reports, and
|
|
40
|
+
# `risk` and `gates` are configured policy with no external equivalent.
|
|
41
|
+
ANALYZER_DIMENSIONS: tuple[str, ...] = ("declarations",)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def declaration_concepts_missing(covered: set[str]) -> tuple[str, ...]:
|
|
45
|
+
"""Criteria the analyzers did not supply, in rubric order.
|
|
46
|
+
|
|
47
|
+
The fallback this decides is correct and used to be silent, which P8
|
|
48
|
+
forbids: the reader saw a declarations rate with nothing saying what
|
|
49
|
+
produced it. It is not a rare path either. lizard reports cyclomatic
|
|
50
|
+
complexity and declaration lines and no cognitive complexity, so a
|
|
51
|
+
JavaScript repository with lizard installed and nothing else takes
|
|
52
|
+
this branch on *every* run -- by construction, and an audit had to
|
|
53
|
+
point out that the page justifying JavaScript support credited the
|
|
54
|
+
analyzer pool for work the built-in scanner was doing.
|
|
55
|
+
"""
|
|
56
|
+
return tuple(
|
|
57
|
+
concept for concept, _warn, _fail in DECLARATION_CRITERIA
|
|
58
|
+
if concept not in covered
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _unit_languages(measurements: list[Measurement]) -> dict[str, str]:
|
|
63
|
+
"""Each unit's language, by the suffix of the file it was read from.
|
|
64
|
+
|
|
65
|
+
Units whose suffix names no language this project knows are left out
|
|
66
|
+
rather than pooled under one unknown bucket. The gate below asks
|
|
67
|
+
whether a *language* is missing a criterion, and a question about a
|
|
68
|
+
language cannot be asked of a file whose language was never
|
|
69
|
+
established — pooling them would let `pmd`'s XML readings, which
|
|
70
|
+
carry no declaration lines, fail the check for a Java repository
|
|
71
|
+
that is otherwise completely measured.
|
|
72
|
+
"""
|
|
73
|
+
languages: dict[str, str] = {}
|
|
74
|
+
for measurement in measurements:
|
|
75
|
+
path = measurement.path or measurement.unit
|
|
76
|
+
suffix = PurePosixPath(path).suffix
|
|
77
|
+
language = KNOWN_SOURCE_SUFFIXES.get(suffix)
|
|
78
|
+
if language:
|
|
79
|
+
languages[measurement.unit] = language
|
|
80
|
+
return languages
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _concepts_by_language(
|
|
84
|
+
per_unit: dict[str, dict[str, float]], unit_languages: dict[str, str]
|
|
85
|
+
) -> dict[str, set[str]]:
|
|
86
|
+
"""What the analyzers measured, gathered per language."""
|
|
87
|
+
covered: dict[str, set[str]] = defaultdict(set)
|
|
88
|
+
for unit, values in per_unit.items():
|
|
89
|
+
language = unit_languages.get(unit)
|
|
90
|
+
if language:
|
|
91
|
+
covered[language].update(values)
|
|
92
|
+
return dict(covered)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _languages_missing_a_criterion(
|
|
96
|
+
per_unit: dict[str, dict[str, float]], unit_languages: dict[str, str]
|
|
97
|
+
) -> tuple[tuple[str, tuple[str, ...]], ...]:
|
|
98
|
+
"""Languages the analyzers did not measure on all three criteria.
|
|
99
|
+
|
|
100
|
+
One incomplete language disqualifies the whole reading, because the
|
|
101
|
+
value returned here replaces the built-in `declarations` pressure for
|
|
102
|
+
the entire repository — there is no seam to hand back a blend. Making
|
|
103
|
+
that seam is a larger change: the built-in path carries one aggregate
|
|
104
|
+
`declaration_band_pressure`, not a per-language one, so a genuine
|
|
105
|
+
per-language blend needs per-language aggregates plumbed through the
|
|
106
|
+
summary. Recorded rather than attempted here, because the honest
|
|
107
|
+
intermediate is to fall back for the repository when any language it
|
|
108
|
+
would speak for is incomplete, which is strictly more correct than
|
|
109
|
+
speaking for all of them on the strength of one.
|
|
110
|
+
"""
|
|
111
|
+
return tuple(
|
|
112
|
+
(language, missing)
|
|
113
|
+
for language, concepts in sorted(
|
|
114
|
+
_concepts_by_language(per_unit, unit_languages).items())
|
|
115
|
+
if (missing := declaration_concepts_missing(concepts))
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def complete_from_built_ins(
|
|
120
|
+
per_unit: dict[str, dict[str, float]],
|
|
121
|
+
unit_locations: dict[str, tuple[str, int]],
|
|
122
|
+
built_in: dict[tuple[str, int], dict[str, float]],
|
|
123
|
+
) -> dict[str, dict[str, float]]:
|
|
124
|
+
"""Fill criteria an analyzer did not measure from the built-in reading.
|
|
125
|
+
|
|
126
|
+
**Per concept, not per dimension.** The rule above asks whether a
|
|
127
|
+
language carries all three criteria; this is what lets a language
|
|
128
|
+
answer yes when no single tier measures all three. `lizard` reports
|
|
129
|
+
cyclomatic complexity and declaration lines for sixteen languages and
|
|
130
|
+
cognitive complexity for none; the built-in scanners report all three
|
|
131
|
+
for fourteen. Neither tier is complete alone, and the union of them,
|
|
132
|
+
per declaration, is.
|
|
133
|
+
|
|
134
|
+
Without this the analyzer tier can drive `declarations` for **Python
|
|
135
|
+
and nothing else** — measured, not inferred: of the 112 corpus
|
|
136
|
+
repositories, zero satisfied the criterion set without `complexipy`,
|
|
137
|
+
which reads Python only. PMD is the sole other wired tool that speaks
|
|
138
|
+
cognitive complexity, and it emits *violations above a threshold*
|
|
139
|
+
rather than a reading per declaration, so it can never supply a rate
|
|
140
|
+
over a population.
|
|
141
|
+
|
|
142
|
+
The join is `(path, start_line)`, and it is exact rather than
|
|
143
|
+
approximate: measured on this package, all 1,008 declarations lizard
|
|
144
|
+
reports match a built-in declaration at the same start line — 100% of
|
|
145
|
+
lizard's population, 92% of the built-in's, the difference being
|
|
146
|
+
declarations lizard does not count as functions.
|
|
147
|
+
|
|
148
|
+
Mixing tiers inside one unit is the point rather than a compromise.
|
|
149
|
+
The comparability the criterion set protects is that every
|
|
150
|
+
declaration was *eligible* to fail on all three, not that one
|
|
151
|
+
instrument measured all three; ADR 006 already makes the analyzer
|
|
152
|
+
primary for what it measures, and the bands both tiers are scored
|
|
153
|
+
through come from the same thresholds.
|
|
154
|
+
"""
|
|
155
|
+
completed: dict[str, dict[str, float]] = {}
|
|
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
|
|
188
|
+
return completed
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def built_in_readings(
|
|
192
|
+
metrics: object, root: str = ""
|
|
193
|
+
) -> dict[tuple[str, int], dict[str, float]]:
|
|
194
|
+
"""The scanner's three criteria per declaration, keyed for the join.
|
|
195
|
+
|
|
196
|
+
Lives beside the join rather than at the call site so both halves of
|
|
197
|
+
it share one path vocabulary. They did not, once, and the merge
|
|
198
|
+
shipped inert with the whole suite green.
|
|
199
|
+
"""
|
|
200
|
+
return {
|
|
201
|
+
(canonical_path(root, metric.path), metric.start_line): {
|
|
202
|
+
"cyclomatic_complexity": float(metric.complexity),
|
|
203
|
+
"declaration_lines": float(metric.lines),
|
|
204
|
+
"cognitive_complexity": float(metric.cognitive),
|
|
205
|
+
}
|
|
206
|
+
for metric in metrics # type: ignore[attr-defined]
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def canonical_path(root: str, path: str) -> str:
|
|
211
|
+
"""One spelling for a file, so two tiers can be joined on it.
|
|
212
|
+
|
|
213
|
+
Three vocabularies meet at this join and no two agree: `lizard`
|
|
214
|
+
reports `./src/x.py`, `complexipy` reports the absolute path, and the
|
|
215
|
+
built-in scanner reports `src/x.py` relative to the scanned root.
|
|
216
|
+
Keying the join on any of them as given matches nothing, which is not
|
|
217
|
+
a hypothetical — it is how this merge shipped inert the first time,
|
|
218
|
+
with the whole suite green, because every test built both sides from
|
|
219
|
+
the same string.
|
|
220
|
+
"""
|
|
221
|
+
text = path.replace("\\", "/")
|
|
222
|
+
if root and text.startswith(root):
|
|
223
|
+
text = text[len(root):]
|
|
224
|
+
return posixpath.normpath(text).lstrip("./") or text
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def unit_locations(
|
|
228
|
+
measurements: list[Measurement], root: str = ""
|
|
229
|
+
) -> dict[str, tuple[str, int]]:
|
|
230
|
+
"""Where each unit starts, for the join above.
|
|
231
|
+
|
|
232
|
+
A measurement with no line cannot be joined and is simply absent
|
|
233
|
+
here: `complexipy` reports no start line, and needs none, because it
|
|
234
|
+
supplies the one concept the built-in tier would have been asked for.
|
|
235
|
+
"""
|
|
236
|
+
located: dict[str, tuple[str, int]] = {}
|
|
237
|
+
for measurement in measurements:
|
|
238
|
+
path = measurement.path or ""
|
|
239
|
+
if path and measurement.line is not None:
|
|
240
|
+
located[measurement.unit] = (
|
|
241
|
+
canonical_path(root, path), int(measurement.line))
|
|
242
|
+
return located
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _scored_units(
|
|
246
|
+
per_unit: dict[str, dict[str, float]], unit_languages: dict[str, str]
|
|
247
|
+
) -> dict[str, dict[str, float]] | None:
|
|
248
|
+
"""The declarations the analyzer tier may score, or `None` for none.
|
|
249
|
+
|
|
250
|
+
**The one place eligibility is decided.** The rule it enforces is a
|
|
251
|
+
single sentence, and it is the sentence this bridge exists to keep
|
|
252
|
+
true: *every declaration contributing to the analyzer reading was
|
|
253
|
+
eligible to fail on all three criteria the built-in path fails on.*
|
|
254
|
+
A unit is eligible when its own language was measured on cyclomatic
|
|
255
|
+
complexity, declaration lines and cognitive complexity — not when
|
|
256
|
+
some other language in the repository was.
|
|
257
|
+
|
|
258
|
+
Returning the population rather than a verdict is the point. Four
|
|
259
|
+
earlier fixes to this bridge each corrected a formula and left the
|
|
260
|
+
eligibility question living somewhere else, and D137 was the fifth:
|
|
261
|
+
the check took a union across the repository while the units it
|
|
262
|
+
gated were scored one at a time, so a C repository holding one
|
|
263
|
+
`setup.py` had its C declarations scored on two criteria of three.
|
|
264
|
+
A caller cannot now score a set this function did not hand back, so
|
|
265
|
+
the two cannot be scoped differently again.
|
|
266
|
+
|
|
267
|
+
The verdict is all-or-nothing for the repository because the value
|
|
268
|
+
the caller produces replaces the built-in `declarations` pressure for
|
|
269
|
+
the whole of it; there is no seam to hand back a blend. Handing back
|
|
270
|
+
only the eligible units would trade a wrong-criteria reading for a
|
|
271
|
+
wrong-population one — a confident rate describing the 2% of a C
|
|
272
|
+
codebase that happens to be Python. A genuine per-language blend
|
|
273
|
+
needs per-language aggregates plumbed through the summary evidence,
|
|
274
|
+
which the built-in path does not carry today.
|
|
275
|
+
"""
|
|
276
|
+
incomplete = _languages_missing_a_criterion(per_unit, unit_languages)
|
|
277
|
+
if incomplete:
|
|
278
|
+
return None
|
|
279
|
+
return per_unit
|
{maintainability_agent-2.11.2 → maintainability_agent-3.0.0}/src/maintainability_audit/_formula.py
RENAMED
|
@@ -173,9 +173,19 @@ CATEGORY_ASPECTS: dict[str, dict[str, float]] = {
|
|
|
173
173
|
ROOT_POPULATIONS: tuple[str, ...] = ("files_scanned", "declarations_scanned")
|
|
174
174
|
|
|
175
175
|
POPULATION_FLOORS: dict[str, int] = {
|
|
176
|
-
|
|
176
|
+
# The 180-repo corpus minima (3.0.0), taken from `corpus.json`'s own
|
|
177
|
+
# `source_files` and `declarations` — the columns the floor test
|
|
178
|
+
# compares against — rather than from the measurement evidence,
|
|
179
|
+
# which counts a different population and gave 26 / 164.
|
|
180
|
+
#
|
|
181
|
+
# Were 32 / 139 / 36 against the 112-repo corpus. The added languages
|
|
182
|
+
# brought smaller repositories: SwiftyJSON and fd at 24 source files,
|
|
183
|
+
# express at 139 declarations. A floor above the corpus minimum is
|
|
184
|
+
# not a floor, it is a repository the scale cannot score while
|
|
185
|
+
# claiming to be calibrated by it.
|
|
186
|
+
"files_scanned": 24,
|
|
177
187
|
"declarations_scanned": 139,
|
|
178
|
-
"production_declarations_scanned":
|
|
188
|
+
"production_declarations_scanned": 53,
|
|
179
189
|
}
|
|
180
190
|
|
|
181
191
|
# Which population each aspect's rate divides by. An aspect absent from
|