maintainability-agent 2.1.0__tar.gz → 2.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {maintainability_agent-2.1.0/src/maintainability_agent.egg-info → maintainability_agent-2.3.0}/PKG-INFO +2 -2
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/README.md +1 -1
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/pyproject.toml +1 -1
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0/src/maintainability_agent.egg-info}/PKG-INFO +2 -2
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_agent.egg-info/SOURCES.txt +5 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/__init__.py +1 -1
- maintainability_agent-2.3.0/src/maintainability_audit/_attestation.py +166 -0
- maintainability_agent-2.3.0/src/maintainability_audit/_gates.py +122 -0
- maintainability_agent-2.3.0/src/maintainability_audit/_ratchet.py +109 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/cli.py +26 -46
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/config.py +1 -1
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/prompts.py +1 -0
- maintainability_agent-2.3.0/tests/test_attestation.py +151 -0
- maintainability_agent-2.3.0/tests/test_dimension_ratchet.py +155 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_identity_resolution.py +5 -1
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_scope_conformance.py +6 -4
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/LICENSE +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/setup.cfg +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_agent.egg-info/dependency_links.txt +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_agent.egg-info/entry_points.txt +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_agent.egg-info/requires.txt +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_agent.egg-info/top_level.txt +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/__main__.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_adapters.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_analysis.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_aspects.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_assets/analyzer-catalog.json +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_assets/standard.md +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_backfill.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_bands.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_built_ins.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_calibration.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_catalog.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_charts.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_cognitive.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_config_defaults.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_conformance.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_corroborate.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_coverage_notes.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_derive.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_discovery.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_documents.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_economics.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_economics_view.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_environment.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_evidence_view.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_finding_match.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_first_run.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_formula.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_generic.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_grant_ledger.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_history_view.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_hostile_prompt.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_hotspots.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_html_report_sections.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_html_view.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_identity.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_jvm_adapters.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_markdown_sections.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_masking.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_mcp_audit.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_mcp_gate.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_mcp_grants.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_mcp_refusals.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_mcp_resources.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_mcp_setup.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_metric_adapters.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_metrics_types.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_pillars.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_practice.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_pressures.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_ranges_c.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_ranges_core.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_ranges_cpp.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_ranges_csharp.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_ranges_fortran.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_ranges_java.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_ranges_js.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_recurrence.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_runner.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_safe_write.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_scan_history.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_scan_view.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_second_source.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_selection.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_semantic.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_semantic_policy.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_semantic_ts.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_semantic_view.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_setup_errors.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_setup_persist.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_skill_data/SKILL.md +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_skill_data/agents/anthropic.yaml +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_skill_data/agents/copilot.yaml +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_skill_data/agents/openai.yaml +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_skill_data/copilot/maintainability-agent.prompt.md +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_skill_data/references/finding-taxonomy.md +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_skill_install.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_stored_grants.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_tdd_view.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_test_execution.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_test_pairing.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_tokens.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_tool_adapters.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_trends.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_user_config.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_verdict_adapters.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_verification.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_work_order.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_work_order_view.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_work_order_weights.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/_xml.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/baseline.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/deadcode.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/declarations.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/duplication.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/evidence.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/git_tools.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/history.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/idioms.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/instructions.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/mcp_server.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/metrics.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/renderers.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/report.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/sarif.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/scoring.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/similarity.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/source.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_acquisition_trust.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_adapter_docs.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_adapter_recordings.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_adapters.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_added_lines.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_analysis_coverage.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_analysis_execution.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_analyzer_bridge.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_analyzer_config_isolation.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_analyzer_estimate_claims.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_analyzer_pinning.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_analyzer_primary_estimate.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_analyzer_provenance.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_analyzer_provenance_exclusions.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_analyzer_scoring_path.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_analyzer_spread_range.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_analyzer_xml_bounds.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_annotations_match_behaviour.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_anticipated_refusals.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_architecture.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_artifact_write_route_class.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_aspect_coverage.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_asset_snapshot_class.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_ast_reading_resolves_spellings.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_audit_components.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_authorization_freshness.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_authorship_gates.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_backfill.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_band_concealment_class.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_band_concealment_p3.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_band_pressures.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_bands.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_body_or_signature_class.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_c_declarations.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_calibration_corpus.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_calibration_drift.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_calibration_tooling.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_catalog_selection.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_catalog_tier_promise.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_chat_primary_docs.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_checkstyle_adapter.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_checkstyle_catalog.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_ci_history_recipe.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_ci_installs_the_analyzer_pool.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_claimed_languages.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_cli.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_clone_group_class.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_cognitive_complexity.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_cohort_statistics.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_concealment_properties.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_config_shape.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_consent_and_grant.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_consumer_migration.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_contract_claims.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_corroborate.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_coverage_by_language.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_cpp_declarations.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_csharp_declarations.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_d15_composition.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_d15_goal_directed.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_dead_code.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_declaration_edge_cases.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_declaration_grading.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_declaration_ranges.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_declaration_source.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_declared_authorship.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_declared_imports.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_derivation_mix.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_determinism.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_direct_runner_availability_class.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_discovery.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_doc_claims.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_docs_links.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_economic_context.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_empty_output_not_clean.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_empty_output_not_clean_adapters.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_environment_work_order.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_evidence_invariants.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_evidence_normalization.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_evidence_properties.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_exclusion_dialects.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_falsifier_standard.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_file_list_cap_class.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_finding_identity.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_first_run_elicitation.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_first_run_prompt.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_first_run_reelicitation.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_fix_breadth_window.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_fixed_form_fortran.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_format_ask.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_fortran_declarations.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_fortran_metrics.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_fortran_practice.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_git_argv.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_git_read_only.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_git_readonly_class.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_grant_only_user_tier.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_history.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_history_boundary.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_history_manifest.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_history_schema2.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_history_window.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_hostile_prompt.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_html_executive.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_html_section_parity_class.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_identity_docs.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_idiom_divergence.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_installed_wheel_runtime.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_instruction_targets.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_java_range_guard.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_java_ranges.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_java_scored.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_java_wiring.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_js_complexity_operators.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_js_declarations.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_language_coverage.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_mcp_baseline_payload.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_mcp_history.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_mcp_server.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_migration_1_0.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_near_duplicates.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_network_disclosure.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_one_setup_across_surfaces_class.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_pattern_budget.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_phase6_claims.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_pillars_and_practice.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_platform_claim.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_pmd_adapter.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_pmd_audit_closures.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_pool_runs_by_config.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_population_floors.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_practice_nested_gates_class.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_presentation_refusals.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_promises.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_prompt_escalation_once.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_prompt_honesty.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_prompt_location_escalation_class.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_prove_falsifiers.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_python_declarations.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_read_boundary.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_readme_claims.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_recurrence.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_release_plan.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_release_plan_status.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_report_remedy_copy.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_roles_recorded.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_root_grants.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_rubric_ignores_project_lint_config.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_runner.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_sarif.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_scan_history.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_scan_scope.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_scanning.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_schema_fidelity.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_scoring_calibration.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_semantic_policy.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_setup_gate_completeness.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_setup_gate_independent_contract.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_setup_precondition.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_skill_install.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_source_index.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_spotbugs_adapter.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_spotbugs_catalog.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_stage8_contract.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_tdd_structure.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_test_execution_class.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_three_presentations.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_trends.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_type_fields_not_functions_class.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_uat_falsifiers.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_unread_code.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_user_config_tier.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_verification_audit_closures.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_verified_grade.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_version_claim.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_wheel_contents.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_work_order.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_work_order_items.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_work_order_selection.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_workflow_supply_chain.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_write_boundary.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_write_route_class.py +0 -0
- {maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/tests/test_written_record.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: maintainability-agent
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.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.
|
|
47
|
+
Version **2.3.0**.
|
|
48
48
|
|
|
49
49
|
**Languages parsed:** Python, Java, C, C++, C#, Fortran (free-form *and*
|
|
50
50
|
fixed-form), and the JS/TS family — each by a scanner written for it, and
|
|
@@ -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.
|
|
8
|
+
Version **2.3.0**.
|
|
9
9
|
|
|
10
10
|
**Languages parsed:** Python, Java, C, C++, C#, Fortran (free-form *and*
|
|
11
11
|
fixed-form), and the JS/TS family — each by a scanner written for it, and
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "maintainability-agent"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.3.0"
|
|
8
8
|
description = "Maintainability gate and AI remediation prompt generator for CI."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
# 3.11, not 3.10. `_discovery`, `_pillars` and `_runner` all import
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: maintainability-agent
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.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.
|
|
47
|
+
Version **2.3.0**.
|
|
48
48
|
|
|
49
49
|
**Languages parsed:** Python, Java, C, C++, C#, Fortran (free-form *and*
|
|
50
50
|
fixed-form), and the JS/TS family — each by a scanner written for it, and
|
|
@@ -12,6 +12,7 @@ src/maintainability_audit/__main__.py
|
|
|
12
12
|
src/maintainability_audit/_adapters.py
|
|
13
13
|
src/maintainability_audit/_analysis.py
|
|
14
14
|
src/maintainability_audit/_aspects.py
|
|
15
|
+
src/maintainability_audit/_attestation.py
|
|
15
16
|
src/maintainability_audit/_backfill.py
|
|
16
17
|
src/maintainability_audit/_bands.py
|
|
17
18
|
src/maintainability_audit/_built_ins.py
|
|
@@ -33,6 +34,7 @@ src/maintainability_audit/_evidence_view.py
|
|
|
33
34
|
src/maintainability_audit/_finding_match.py
|
|
34
35
|
src/maintainability_audit/_first_run.py
|
|
35
36
|
src/maintainability_audit/_formula.py
|
|
37
|
+
src/maintainability_audit/_gates.py
|
|
36
38
|
src/maintainability_audit/_generic.py
|
|
37
39
|
src/maintainability_audit/_grant_ledger.py
|
|
38
40
|
src/maintainability_audit/_history_view.py
|
|
@@ -62,6 +64,7 @@ src/maintainability_audit/_ranges_csharp.py
|
|
|
62
64
|
src/maintainability_audit/_ranges_fortran.py
|
|
63
65
|
src/maintainability_audit/_ranges_java.py
|
|
64
66
|
src/maintainability_audit/_ranges_js.py
|
|
67
|
+
src/maintainability_audit/_ratchet.py
|
|
65
68
|
src/maintainability_audit/_recurrence.py
|
|
66
69
|
src/maintainability_audit/_runner.py
|
|
67
70
|
src/maintainability_audit/_safe_write.py
|
|
@@ -142,6 +145,7 @@ tests/test_artifact_write_route_class.py
|
|
|
142
145
|
tests/test_aspect_coverage.py
|
|
143
146
|
tests/test_asset_snapshot_class.py
|
|
144
147
|
tests/test_ast_reading_resolves_spellings.py
|
|
148
|
+
tests/test_attestation.py
|
|
145
149
|
tests/test_audit_components.py
|
|
146
150
|
tests/test_authorization_freshness.py
|
|
147
151
|
tests/test_authorship_gates.py
|
|
@@ -187,6 +191,7 @@ tests/test_declared_authorship.py
|
|
|
187
191
|
tests/test_declared_imports.py
|
|
188
192
|
tests/test_derivation_mix.py
|
|
189
193
|
tests/test_determinism.py
|
|
194
|
+
tests/test_dimension_ratchet.py
|
|
190
195
|
tests/test_direct_runner_availability_class.py
|
|
191
196
|
tests/test_discovery.py
|
|
192
197
|
tests/test_doc_claims.py
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""The record a generator cannot produce about its own output.
|
|
2
|
+
|
|
3
|
+
The three mechanisms — scope conformance, suppression detection, the
|
|
4
|
+
dimension ratchet — each answer one question about a change. This composes
|
|
5
|
+
them into the artifact they were built for: a per-change record of what was
|
|
6
|
+
measured, what the agent was told to change, what it actually changed, and
|
|
7
|
+
what moved.
|
|
8
|
+
|
|
9
|
+
**Why an independent process has to emit it.** A platform that generates
|
|
10
|
+
code and then reports on its quality is producing a self-assessment. That
|
|
11
|
+
is a property of the arrangement, not a criticism of any particular one,
|
|
12
|
+
and no amount of engineering removes it. The question a reviewer asks is
|
|
13
|
+
*who checked this, and can they re-derive the check months from now* — and
|
|
14
|
+
an author cannot be the answer. This tool writes nothing, runs no model,
|
|
15
|
+
and returns the same verdict from the same inputs, which is the only reason
|
|
16
|
+
its answer can be evidence rather than an opinion.
|
|
17
|
+
|
|
18
|
+
**It is reproducible, not signed.** Every field is derived from the report
|
|
19
|
+
and the pinned inputs, and the digest at the end is over the record's own
|
|
20
|
+
content, so two runs on the same commit with the same analyzer versions
|
|
21
|
+
produce the same digest. That makes it *verifiable* — anyone can re-run and
|
|
22
|
+
compare. It is deliberately not called a signature: nothing here holds a
|
|
23
|
+
key, and claiming a cryptographic guarantee this does not provide would be
|
|
24
|
+
exactly the kind of overclaim the rest of the project spends its time
|
|
25
|
+
removing. Signing the artifact, if a caller wants that, is a step they take
|
|
26
|
+
with their own key on a file whose bytes are stable.
|
|
27
|
+
|
|
28
|
+
**What it does not establish.** The record says which files the work order
|
|
29
|
+
named and whether a checker was switched off — not whether the edits were
|
|
30
|
+
correct. It never opens the diff to judge it, and it never touches a score.
|
|
31
|
+
Review is still review.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
import hashlib
|
|
37
|
+
import json
|
|
38
|
+
from typing import Any
|
|
39
|
+
|
|
40
|
+
ARTIFACT_VERSION = 1
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _verdicts(report: dict[str, Any]) -> list[str]:
|
|
44
|
+
"""One line per question that was actually asked."""
|
|
45
|
+
lines: list[str] = []
|
|
46
|
+
conformance = report.get("scope_conformance")
|
|
47
|
+
if conformance:
|
|
48
|
+
out = len(conformance["out_of_scope"])
|
|
49
|
+
silenced = conformance["suppressions_on_named_paths"]
|
|
50
|
+
lines.append(
|
|
51
|
+
f"- **Stayed inside the work order:** "
|
|
52
|
+
f"{'yes' if conformance['conformant'] else f'no — {out} file(s) outside it'}"
|
|
53
|
+
)
|
|
54
|
+
lines.append(
|
|
55
|
+
f"- **Without silencing a finding:** "
|
|
56
|
+
f"{'yes' if not silenced else f'no — {silenced} suppression(s) on flagged files'}"
|
|
57
|
+
)
|
|
58
|
+
ratchet = report.get("dimension_ratchet")
|
|
59
|
+
if ratchet:
|
|
60
|
+
if not ratchet["comparable"]:
|
|
61
|
+
lines.append(
|
|
62
|
+
f"- **No dimension regressed:** not established — {ratchet['reason']}"
|
|
63
|
+
)
|
|
64
|
+
elif ratchet["regressed"]:
|
|
65
|
+
names = ", ".join(item["name"] for item in ratchet["regressed"])
|
|
66
|
+
lines.append(f"- **No dimension regressed:** no — {names}")
|
|
67
|
+
else:
|
|
68
|
+
lines.append("- **No dimension regressed:** yes")
|
|
69
|
+
return lines
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _measured(report: dict[str, Any]) -> list[str]:
|
|
73
|
+
score = report.get("score") or {}
|
|
74
|
+
coverage = report.get("analyzer_coverage") or {}
|
|
75
|
+
status = score.get("evidence_status")
|
|
76
|
+
if isinstance(status, dict):
|
|
77
|
+
status = status.get("status")
|
|
78
|
+
ran = (coverage.get("by_outcome") or {}).get("ran") or []
|
|
79
|
+
return [
|
|
80
|
+
f"- Estimate: {score.get('maintainability_estimate', 'withheld')}",
|
|
81
|
+
f"- Verified grade: {score.get('verified_grade') or 'withheld'}",
|
|
82
|
+
f"- Evidence: {status or 'unstated'}",
|
|
83
|
+
f"- Analyzers that ran: {len(ran)}",
|
|
84
|
+
f"- Hard gate failures: {len(report.get('hard_gate_failures') or [])}",
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def attestation_record(report: dict[str, Any]) -> dict[str, Any]:
|
|
89
|
+
"""The structured record, before rendering."""
|
|
90
|
+
conformance = report.get("scope_conformance") or {}
|
|
91
|
+
ratchet = report.get("dimension_ratchet") or {}
|
|
92
|
+
score = report.get("score") or {}
|
|
93
|
+
record = {
|
|
94
|
+
"artifact_version": ARTIFACT_VERSION,
|
|
95
|
+
"tool_version": report.get("tool_version"),
|
|
96
|
+
"commit": report.get("git_commit"),
|
|
97
|
+
"branch": report.get("git_branch"),
|
|
98
|
+
# A finding reported against "the commit" that actually depends on
|
|
99
|
+
# uncommitted work is unreproducible, so the state of the tree is
|
|
100
|
+
# part of what is attested rather than a footnote.
|
|
101
|
+
"tree_dirty": bool(report.get("git_status_short")),
|
|
102
|
+
"revspec": conformance.get("revspec"),
|
|
103
|
+
"estimate": score.get("maintainability_estimate"),
|
|
104
|
+
"verified_grade": score.get("verified_grade"),
|
|
105
|
+
"stayed_in_scope": conformance.get("conformant"),
|
|
106
|
+
"no_finding_silenced": (
|
|
107
|
+
None if not conformance else conformance["suppressions_on_named_paths"] == 0
|
|
108
|
+
),
|
|
109
|
+
"no_dimension_regressed": (
|
|
110
|
+
None if not ratchet or not ratchet.get("comparable")
|
|
111
|
+
else not ratchet["regressed"]
|
|
112
|
+
),
|
|
113
|
+
"scope_conformance": conformance or None,
|
|
114
|
+
"dimension_ratchet": ratchet or None,
|
|
115
|
+
}
|
|
116
|
+
# The digest is over the record without it, so it is reproducible: the
|
|
117
|
+
# same commit measured with the same analyzer versions yields the same
|
|
118
|
+
# value, and anyone can recompute it rather than trust it.
|
|
119
|
+
record["digest"] = hashlib.sha256(
|
|
120
|
+
json.dumps(record, sort_keys=True, default=str).encode("utf-8")
|
|
121
|
+
).hexdigest()[:16]
|
|
122
|
+
return record
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def render_attestation(report: dict[str, Any]) -> str:
|
|
126
|
+
"""The record as text, for a reviewer or an audit trail."""
|
|
127
|
+
record = attestation_record(report)
|
|
128
|
+
dirty = " (uncommitted changes present)" if record["tree_dirty"] else ""
|
|
129
|
+
lines = [
|
|
130
|
+
"# Maintainability attestation",
|
|
131
|
+
"",
|
|
132
|
+
"An independent, deterministic check on a change. Produced by a "
|
|
133
|
+
"process that generated none of the code it examined.",
|
|
134
|
+
"",
|
|
135
|
+
f"- Commit: `{record['commit'] or 'unknown'}`{dirty}",
|
|
136
|
+
f"- Branch: `{record['branch'] or 'unknown'}`",
|
|
137
|
+
f"- Tool version: {record['tool_version'] or 'unknown'}",
|
|
138
|
+
f"- Compared: `{record['revspec'] or 'not asked'}`",
|
|
139
|
+
"",
|
|
140
|
+
"## What was measured",
|
|
141
|
+
"",
|
|
142
|
+
*_measured(report),
|
|
143
|
+
"",
|
|
144
|
+
"## What the change did",
|
|
145
|
+
"",
|
|
146
|
+
]
|
|
147
|
+
verdicts = _verdicts(report)
|
|
148
|
+
lines += verdicts or ["- Nothing was asked: run with `--conformance` and "
|
|
149
|
+
"`--fail-on-regression` to populate this section."]
|
|
150
|
+
lines += [
|
|
151
|
+
"",
|
|
152
|
+
"## What this does not establish",
|
|
153
|
+
"",
|
|
154
|
+
"- It compares paths and reads added lines. A file in scope means the "
|
|
155
|
+
"work order named it, **not** that the change to it was correct — this "
|
|
156
|
+
"never opens the diff to judge it.",
|
|
157
|
+
"- It touches no score. Whether a change obeyed its instructions is a "
|
|
158
|
+
"fact about behaviour, not evidence about the code's condition.",
|
|
159
|
+
"- It is reproducible, **not signed**. The digest is over this "
|
|
160
|
+
"record's own content, so the same inputs reproduce it and anyone can "
|
|
161
|
+
"recompute it. Nothing here holds a key.",
|
|
162
|
+
"",
|
|
163
|
+
f"Digest: `{record['digest']}`",
|
|
164
|
+
"",
|
|
165
|
+
]
|
|
166
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""What happens after the audit and before the exit code.
|
|
2
|
+
|
|
3
|
+
Split from `cli.py` when it crossed this project's 500-line file gate —
|
|
4
|
+
the same reason `_mcp_setup`, `_adapters` and `test_architecture` were
|
|
5
|
+
split before it.
|
|
6
|
+
|
|
7
|
+
The split is not arbitrary. Everything here composes from the **finished
|
|
8
|
+
report**: whether the diff obeyed its work order, whether a dimension
|
|
9
|
+
slipped since the last comparable scan, and what those two facts mean for
|
|
10
|
+
the process exit code. None of it measures a tree and none of it may reach
|
|
11
|
+
scoring — a fact about an agent's behaviour, or about the difference
|
|
12
|
+
between two scans, is not evidence about the code's condition.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
from .baseline import findings_not_in_baseline
|
|
22
|
+
from .git_tools import added_lines, changed_paths
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def attach_conformance(args: argparse.Namespace, report: dict) -> None:
|
|
26
|
+
"""Record how the diff relates to the work order, when asked.
|
|
27
|
+
|
|
28
|
+
Assembly, not scoring: the record is attached to the report and reaches
|
|
29
|
+
no dimension, because whether a diff was obedient is a fact about an
|
|
30
|
+
agent's behaviour and not evidence about the code's condition.
|
|
31
|
+
"""
|
|
32
|
+
if not args.conformance:
|
|
33
|
+
return
|
|
34
|
+
from ._conformance import scope_conformance
|
|
35
|
+
|
|
36
|
+
root = Path(report["root"])
|
|
37
|
+
changed = changed_paths(root, args.conformance)
|
|
38
|
+
added = added_lines(root, args.conformance)
|
|
39
|
+
report["scope_conformance"] = scope_conformance(
|
|
40
|
+
report, changed, args.conformance, added
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def attach_ratchet(report: dict, history_path: Path) -> None:
|
|
45
|
+
"""Record whether any category slipped since the last comparable scan."""
|
|
46
|
+
from ._ratchet import dimension_ratchet
|
|
47
|
+
from ._scan_history import read_history
|
|
48
|
+
|
|
49
|
+
if not history_path.exists():
|
|
50
|
+
return
|
|
51
|
+
report["dimension_ratchet"] = dimension_ratchet(read_history(history_path))
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _attach_post_audit_records(
|
|
55
|
+
args: argparse.Namespace, report: dict, history_path: Path
|
|
56
|
+
) -> None:
|
|
57
|
+
"""Records composed from the finished report, before it is rendered.
|
|
58
|
+
|
|
59
|
+
Both read the report rather than the tree, and neither reaches scoring:
|
|
60
|
+
whether a diff obeyed its work order, and whether a dimension slipped
|
|
61
|
+
since the last comparable scan, are facts about a change and its
|
|
62
|
+
history — not evidence about the code's condition.
|
|
63
|
+
"""
|
|
64
|
+
attach_conformance(args, report)
|
|
65
|
+
attach_ratchet(report, history_path)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _conformance_exit(args: argparse.Namespace, report: dict) -> int:
|
|
69
|
+
"""0 to continue; otherwise the exit code the conformance gate demands."""
|
|
70
|
+
if not args.fail_on_out_of_scope:
|
|
71
|
+
return 0
|
|
72
|
+
record = report.get("scope_conformance")
|
|
73
|
+
if record is None:
|
|
74
|
+
print("--fail-on-out-of-scope needs --conformance REVSPEC", file=sys.stderr)
|
|
75
|
+
return 2
|
|
76
|
+
# `clean`, not `conformant`: staying in scope while silencing the finding
|
|
77
|
+
# is the evasion the pairing exists to catch, and a gate that accepted it
|
|
78
|
+
# would be satisfied by the thing it guards against.
|
|
79
|
+
return 0 if record["clean"] else 1
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _regression_exit(args: argparse.Namespace, report: dict) -> int:
|
|
83
|
+
"""0 to continue; otherwise the exit code the ratchet demands."""
|
|
84
|
+
if not args.fail_on_regression:
|
|
85
|
+
return 0
|
|
86
|
+
ratchet = report.get("dimension_ratchet")
|
|
87
|
+
if ratchet is None:
|
|
88
|
+
print("--fail-on-regression needs a recorded history (--record-history)",
|
|
89
|
+
file=sys.stderr)
|
|
90
|
+
return 2
|
|
91
|
+
# A run that could not compare does not pass quietly: exiting 0 where the
|
|
92
|
+
# question was never asked is the "absence read as a pass" shape.
|
|
93
|
+
#
|
|
94
|
+
# The reason is not interpolated — it derives from recorded scan history,
|
|
95
|
+
# and code scanning is right that history fields have no business in a log
|
|
96
|
+
# line. It stays in the report, which a caller reads deliberately.
|
|
97
|
+
# Suppressing that alert was refused: this release ships the check that
|
|
98
|
+
# treats a suppression as a finding rather than a fix.
|
|
99
|
+
if not ratchet["comparable"]:
|
|
100
|
+
print("no comparable scan for --fail-on-regression; see "
|
|
101
|
+
"dimension_ratchet.reason in the report", file=sys.stderr)
|
|
102
|
+
return 2
|
|
103
|
+
return 1 if ratchet["regressed"] else 0
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def audit_exit_code(args: argparse.Namespace, report: dict) -> int:
|
|
107
|
+
if args.fail_on_new:
|
|
108
|
+
# Structured matching, never a label-set difference: a label
|
|
109
|
+
# changes under `git mv` and same-name reorder, and failing a
|
|
110
|
+
# build over either is a false report about the change.
|
|
111
|
+
root = Path(report["root"])
|
|
112
|
+
if findings_not_in_baseline(report, args.baseline, root):
|
|
113
|
+
return 1
|
|
114
|
+
if args.fail_on_gate and report["hard_gate_failures"]:
|
|
115
|
+
return 1
|
|
116
|
+
conformance = _conformance_exit(args, report)
|
|
117
|
+
if conformance:
|
|
118
|
+
return conformance
|
|
119
|
+
regression = _regression_exit(args, report)
|
|
120
|
+
if regression:
|
|
121
|
+
return regression
|
|
122
|
+
return 0
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Did any dimension get worse than the last comparable scan?
|
|
2
|
+
|
|
3
|
+
The third mechanism behind the attestation. `--fail-on-new` already catches
|
|
4
|
+
a finding that clears and returns, using structured identity so a rename is
|
|
5
|
+
not a new finding. What nothing caught is the slower failure: a change that
|
|
6
|
+
improves one dimension while quietly regressing another. Both scans are
|
|
7
|
+
green by every existing gate, the overall estimate can even rise, and the
|
|
8
|
+
thing that got worse is never named.
|
|
9
|
+
|
|
10
|
+
**The comparison is only honest inside one segment.** History is already
|
|
11
|
+
split wherever the instrument changed — a different calibration constant, a
|
|
12
|
+
different analyzer population — because two scans taken with different
|
|
13
|
+
instruments cannot be subtracted. `_scan_history.segments` owns that rule,
|
|
14
|
+
and this module refuses rather than reaching across a break. A ratchet that
|
|
15
|
+
compared a 1.x score to a 2.0 score would have reported every repository on
|
|
16
|
+
earth as catastrophically regressed on the day the corpus was extended, and
|
|
17
|
+
it would have been the ratchet that was wrong.
|
|
18
|
+
|
|
19
|
+
So there are three outcomes, not two: **held**, **regressed**, and **not
|
|
20
|
+
comparable**. The third is not a pass. It says the question could not be
|
|
21
|
+
asked, which is what a reader needs in order to know they are not being
|
|
22
|
+
reassured by silence.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
from typing import Any
|
|
28
|
+
|
|
29
|
+
from ._scan_history import ScanRecord, segments
|
|
30
|
+
|
|
31
|
+
#: Scores are reported to one decimal, so a difference below this is the
|
|
32
|
+
#: rounding boundary rather than a movement. Named rather than inlined
|
|
33
|
+
#: because it is a judgment: too tight and every scan trips on noise, too
|
|
34
|
+
#: loose and a real slide hides under the threshold.
|
|
35
|
+
TOLERANCE = 0.05
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _series(record: ScanRecord, field: str) -> dict[str, float]:
|
|
39
|
+
values = getattr(record, field, None) or {}
|
|
40
|
+
return {
|
|
41
|
+
name: float(value) for name, value in values.items()
|
|
42
|
+
if isinstance(value, (int, float))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _regressions(
|
|
47
|
+
previous: ScanRecord, current: ScanRecord, field: str
|
|
48
|
+
) -> list[dict[str, Any]]:
|
|
49
|
+
"""Names that dropped, with the size of the drop."""
|
|
50
|
+
before, after = _series(previous, field), _series(current, field)
|
|
51
|
+
found = []
|
|
52
|
+
for name, was in sorted(before.items()):
|
|
53
|
+
# A name the current scan does not carry is not a regression: the
|
|
54
|
+
# aspect became unmeasurable, which the evidence model already
|
|
55
|
+
# reports as `Unknown` rather than as a zero. Scoring it as a drop
|
|
56
|
+
# to nothing would turn withheld evidence into a failure, which is
|
|
57
|
+
# the exact inversion P3 forbids.
|
|
58
|
+
if name not in after:
|
|
59
|
+
continue
|
|
60
|
+
now = after[name]
|
|
61
|
+
if was - now > TOLERANCE:
|
|
62
|
+
found.append({
|
|
63
|
+
"name": name, "was": round(was, 2),
|
|
64
|
+
"now": round(now, 2), "drop": round(was - now, 2),
|
|
65
|
+
})
|
|
66
|
+
return found
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def dimension_ratchet(records: list[ScanRecord]) -> dict[str, Any]:
|
|
70
|
+
"""Compare the newest scan with the previous comparable one.
|
|
71
|
+
|
|
72
|
+
`comparable` is the load-bearing field. False means the two scans sit
|
|
73
|
+
either side of an instrument change, or there is no earlier scan at
|
|
74
|
+
all — in both cases the question was not asked, and a caller must not
|
|
75
|
+
read that as "nothing regressed".
|
|
76
|
+
"""
|
|
77
|
+
if len(records) < 2:
|
|
78
|
+
return {
|
|
79
|
+
"comparable": False,
|
|
80
|
+
"reason": "no earlier scan to compare against",
|
|
81
|
+
"regressed": [],
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
latest = segments(records)[-1]
|
|
85
|
+
if len(latest.records) < 2:
|
|
86
|
+
return {
|
|
87
|
+
"comparable": False,
|
|
88
|
+
"reason": (
|
|
89
|
+
latest.break_reason
|
|
90
|
+
or "the previous scan was taken with a different instrument"
|
|
91
|
+
),
|
|
92
|
+
"regressed": [],
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
previous, current = latest.records[-2], latest.records[-1]
|
|
96
|
+
categories = _regressions(previous, current, "categories")
|
|
97
|
+
aspects = _regressions(previous, current, "aspects")
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
"comparable": True,
|
|
101
|
+
"against": getattr(previous, "commit", None),
|
|
102
|
+
"categories": categories,
|
|
103
|
+
"aspects": aspects,
|
|
104
|
+
# Categories only. An aspect can move while the category holding it
|
|
105
|
+
# holds, and failing on every aspect wobble makes the gate noise.
|
|
106
|
+
# Aspects are reported so a reader can see what moved underneath.
|
|
107
|
+
"regressed": categories,
|
|
108
|
+
"held": not categories,
|
|
109
|
+
}
|
{maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/cli.py
RENAMED
|
@@ -14,6 +14,10 @@ from ._first_run import (
|
|
|
14
14
|
maybe_prompt_first_run,
|
|
15
15
|
maybe_prompt_test_command,
|
|
16
16
|
)
|
|
17
|
+
from ._gates import (
|
|
18
|
+
_attach_post_audit_records,
|
|
19
|
+
audit_exit_code,
|
|
20
|
+
)
|
|
17
21
|
from ._mcp_audit import record_scan_and_attach
|
|
18
22
|
from ._safe_write import write_artifact
|
|
19
23
|
from ._scan_history import (
|
|
@@ -24,7 +28,6 @@ from ._user_config import mark_repo_seen
|
|
|
24
28
|
from ._work_order import SELECTABLE, combined_delta, select
|
|
25
29
|
from .baseline import (
|
|
26
30
|
finding_fingerprints,
|
|
27
|
-
findings_not_in_baseline,
|
|
28
31
|
load_baseline,
|
|
29
32
|
write_baseline,
|
|
30
33
|
)
|
|
@@ -36,7 +39,7 @@ from .config import (
|
|
|
36
39
|
load_config,
|
|
37
40
|
repository_path,
|
|
38
41
|
)
|
|
39
|
-
from .git_tools import
|
|
42
|
+
from .git_tools import changed_paths
|
|
40
43
|
from .instructions import (
|
|
41
44
|
INSTRUCTION_TARGETS,
|
|
42
45
|
UnknownTarget,
|
|
@@ -65,6 +68,14 @@ def _add_gate_arguments(parser: argparse.ArgumentParser) -> None:
|
|
|
65
68
|
"else'; without this the bound is an instruction nothing checks. "
|
|
66
69
|
"Reports only unless --fail-on-out-of-scope is passed.",
|
|
67
70
|
)
|
|
71
|
+
parser.add_argument(
|
|
72
|
+
"--fail-on-regression", action="store_true",
|
|
73
|
+
help="Exit 1 when any scoring category is lower than the previous "
|
|
74
|
+
"comparable scan. Reads the recorded history, and refuses to "
|
|
75
|
+
"compare across an instrument change rather than reporting a "
|
|
76
|
+
"false regression: two scans taken under different calibration "
|
|
77
|
+
"cannot be subtracted.",
|
|
78
|
+
)
|
|
68
79
|
parser.add_argument(
|
|
69
80
|
"--fail-on-out-of-scope", action="store_true",
|
|
70
81
|
help="With --conformance, exit 1 when the diff touched files the work "
|
|
@@ -86,6 +97,14 @@ def add_arguments(parser: argparse.ArgumentParser) -> None:
|
|
|
86
97
|
parser.add_argument("--prompt-output", help="Optional Markdown prompt for AI-assisted remediation.")
|
|
87
98
|
parser.add_argument("--comment-output", help="Optional Markdown body suitable for a PR comment.")
|
|
88
99
|
parser.add_argument("--agent-instructions-output", help="Optional reusable instructions for AI coding agents.")
|
|
100
|
+
parser.add_argument(
|
|
101
|
+
"--attestation-output",
|
|
102
|
+
help="Write the attestation: an independent, reproducible record of "
|
|
103
|
+
"what was measured, what the change was told to do, what it did, "
|
|
104
|
+
"and what moved. Populated by --conformance and "
|
|
105
|
+
"--fail-on-regression; a generator cannot produce this about its "
|
|
106
|
+
"own output.",
|
|
107
|
+
)
|
|
89
108
|
parser.add_argument("--hostile-prompt-output",
|
|
90
109
|
help="Optional adversarial audit brief seeded from this run (ADR 013). "
|
|
91
110
|
"Text only: it does not gate, score, or send anything.")
|
|
@@ -216,6 +235,10 @@ def write_outputs(args: argparse.Namespace, report: dict, rendered: str) -> None
|
|
|
216
235
|
write_artifact(root, Path(args.comment_output), render_pr_comment(report) + "\n")
|
|
217
236
|
if args.agent_instructions_output:
|
|
218
237
|
write_artifact(root, Path(args.agent_instructions_output), render_agent_instructions(report) + "\n")
|
|
238
|
+
if args.attestation_output:
|
|
239
|
+
from ._attestation import render_attestation
|
|
240
|
+
|
|
241
|
+
write_artifact(root, Path(args.attestation_output), render_attestation(report))
|
|
219
242
|
if args.hostile_prompt_output:
|
|
220
243
|
write_artifact(root, Path(args.hostile_prompt_output),
|
|
221
244
|
render_hostile_audit_prompt(report) + "\n")
|
|
@@ -226,48 +249,6 @@ def write_outputs(args: argparse.Namespace, report: dict, rendered: str) -> None
|
|
|
226
249
|
json.dumps(report_to_sarif(report), indent=2) + "\n", json_artifact=True)
|
|
227
250
|
|
|
228
251
|
|
|
229
|
-
def attach_conformance(args: argparse.Namespace, report: dict) -> None:
|
|
230
|
-
"""Record how the diff relates to the work order, when asked.
|
|
231
|
-
|
|
232
|
-
Assembly, not scoring: the record is attached to the report and reaches
|
|
233
|
-
no dimension, because whether a diff was obedient is a fact about an
|
|
234
|
-
agent's behaviour and not evidence about the code's condition.
|
|
235
|
-
"""
|
|
236
|
-
if not args.conformance:
|
|
237
|
-
return
|
|
238
|
-
from ._conformance import scope_conformance
|
|
239
|
-
|
|
240
|
-
root = Path(report["root"])
|
|
241
|
-
changed = changed_paths(root, args.conformance)
|
|
242
|
-
added = added_lines(root, args.conformance)
|
|
243
|
-
report["scope_conformance"] = scope_conformance(
|
|
244
|
-
report, changed, args.conformance, added
|
|
245
|
-
)
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
def audit_exit_code(args: argparse.Namespace, report: dict) -> int:
|
|
249
|
-
if args.fail_on_new:
|
|
250
|
-
# Structured matching, never a label-set difference: a label
|
|
251
|
-
# changes under `git mv` and same-name reorder, and failing a
|
|
252
|
-
# build over either is a false report about the change.
|
|
253
|
-
root = Path(report["root"])
|
|
254
|
-
if findings_not_in_baseline(report, args.baseline, root):
|
|
255
|
-
return 1
|
|
256
|
-
if args.fail_on_gate and report["hard_gate_failures"]:
|
|
257
|
-
return 1
|
|
258
|
-
if args.fail_on_out_of_scope:
|
|
259
|
-
record = report.get("scope_conformance")
|
|
260
|
-
if record is None:
|
|
261
|
-
print("--fail-on-out-of-scope needs --conformance REVSPEC", file=sys.stderr)
|
|
262
|
-
return 2
|
|
263
|
-
# `clean`, not `conformant`: staying in scope while silencing the
|
|
264
|
-
# finding is the evasion the pairing exists to catch, and a gate
|
|
265
|
-
# that accepted it would be satisfied by the thing it guards against.
|
|
266
|
-
if not record["clean"]:
|
|
267
|
-
return 1
|
|
268
|
-
return 0
|
|
269
|
-
|
|
270
|
-
|
|
271
252
|
def _analyzers_resolved(args: argparse.Namespace, config: dict) -> bool:
|
|
272
253
|
"""Whether the pool runs: flags beat config, config beats nothing.
|
|
273
254
|
|
|
@@ -325,7 +306,6 @@ def _install_skill_action(args: argparse.Namespace) -> int:
|
|
|
325
306
|
return 0
|
|
326
307
|
|
|
327
308
|
|
|
328
|
-
|
|
329
309
|
def _target_paths(pairs: list[str] | None) -> dict[str, str]:
|
|
330
310
|
"""`TARGET=PATH` arguments, as a mapping.
|
|
331
311
|
|
|
@@ -429,7 +409,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
429
409
|
record=bool(record), want_targets=bool(args.prompt_output))
|
|
430
410
|
# Before rendering, so the record is in the report every presentation
|
|
431
411
|
# and every consumer reads, rather than only in the exit code.
|
|
432
|
-
|
|
412
|
+
_attach_post_audit_records(args, report, history_path)
|
|
433
413
|
rendered = _render_presentation(args, report, history_path)
|
|
434
414
|
write_outputs(args, report, rendered)
|
|
435
415
|
mark_repo_seen(root) # completed audit: not a first run now, whatever the gate says (D13)
|
{maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/config.py
RENAMED
|
@@ -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.
|
|
14
|
+
VERSION = "2.3.0"
|
|
15
15
|
|
|
16
16
|
PROJECT_URL = "https://github.com/marshallguillory86/maintainability-agent"
|
|
17
17
|
|
{maintainability_agent-2.1.0 → maintainability_agent-2.3.0}/src/maintainability_audit/prompts.py
RENAMED
|
@@ -11,6 +11,7 @@ from __future__ import annotations
|
|
|
11
11
|
from typing import Any
|
|
12
12
|
|
|
13
13
|
from . import _evidence_view as view
|
|
14
|
+
from ._attestation import render_attestation # noqa: F401
|
|
14
15
|
from ._hostile_prompt import render_hostile_audit_prompt # noqa: F401
|
|
15
16
|
from ._hotspots import hotspot_measure, hotspot_name
|
|
16
17
|
from ._tdd_view import tdd_sentences
|