maintainability-agent 2.5.0__tar.gz → 2.6.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.5.0/src/maintainability_agent.egg-info → maintainability_agent-2.6.0}/PKG-INFO +11 -7
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/README.md +10 -6
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/pyproject.toml +1 -1
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0/src/maintainability_agent.egg-info}/PKG-INFO +11 -7
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_agent.egg-info/SOURCES.txt +3 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/__init__.py +1 -1
- maintainability_agent-2.6.0/src/maintainability_audit/_arguments.py +186 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_gates.py +28 -4
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_history_view.py +46 -0
- maintainability_agent-2.6.0/src/maintainability_audit/_run_comparison.py +184 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_scan_history.py +19 -1
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/cli.py +37 -165
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/config.py +1 -1
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/renderers.py +6 -1
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_history_schema2.py +18 -3
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_identity_resolution.py +4 -1
- maintainability_agent-2.6.0/tests/test_run_comparison.py +321 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/LICENSE +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/setup.cfg +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_agent.egg-info/dependency_links.txt +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_agent.egg-info/entry_points.txt +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_agent.egg-info/requires.txt +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_agent.egg-info/top_level.txt +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/__main__.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_adapters.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_analysis.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_aspects.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_assets/analyzer-catalog.json +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_assets/standard.md +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_attestation.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_backfill.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_bands.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_built_ins.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_calibration.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_catalog.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_charts.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_cognitive.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_config_defaults.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_conformance.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_corroborate.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_coverage_notes.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_derive.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_discovery.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_documents.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_economics.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_economics_view.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_environment.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_evidence_view.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_finding_match.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_first_run.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_formula.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_generic.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_grant_ledger.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_hostile_prompt.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_hotspots.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_html_report_sections.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_html_view.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_identity.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_jvm_adapters.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_markdown_sections.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_masking.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_mcp_audit.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_mcp_gate.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_mcp_grants.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_mcp_refusals.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_mcp_resources.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_mcp_setup.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_metric_adapters.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_metrics_types.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_pillars.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_practice.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_pressures.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_ranges_c.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_ranges_core.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_ranges_cpp.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_ranges_csharp.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_ranges_fortran.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_ranges_java.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_ranges_js.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_ranges_swift.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_ratchet.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_recurrence.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_runner.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_safe_write.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_scan_view.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_second_source.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_selection.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_semantic.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_semantic_policy.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_semantic_ts.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_semantic_view.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_setup_errors.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_setup_persist.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_skill_data/SKILL.md +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_skill_data/agents/anthropic.yaml +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_skill_data/agents/copilot.yaml +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_skill_data/agents/openai.yaml +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_skill_data/copilot/maintainability-agent.prompt.md +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_skill_data/references/finding-taxonomy.md +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_skill_install.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_stored_grants.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_tdd_view.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_test_commands.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_test_execution.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_test_pairing.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_tokens.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_tool_adapters.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_trends.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_user_config.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_verdict_adapters.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_verification.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_work_order.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_work_order_view.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_work_order_weights.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_xml.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/baseline.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/deadcode.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/declarations.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/duplication.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/evidence.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/git_tools.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/history.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/idioms.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/instructions.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/mcp_server.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/metrics.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/prompts.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/report.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/sarif.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/scoring.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/similarity.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/source.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_acquisition_trust.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_adapter_docs.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_adapter_recordings.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_adapters.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_added_lines.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_analysis_coverage.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_analysis_execution.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_analyzer_bridge.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_analyzer_config_isolation.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_analyzer_estimate_claims.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_analyzer_pinning.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_analyzer_primary_estimate.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_analyzer_provenance.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_analyzer_provenance_exclusions.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_analyzer_scoring_path.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_analyzer_spread_range.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_analyzer_xml_bounds.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_annotations_match_behaviour.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_anticipated_refusals.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_architecture.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_artifact_write_route_class.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_aspect_coverage.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_asset_snapshot_class.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_ast_reading_resolves_spellings.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_attestation.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_audit_components.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_authorization_freshness.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_authorship_gates.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_backfill.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_band_concealment_class.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_band_concealment_p3.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_band_pressures.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_bands.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_body_or_signature_class.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_c_declarations.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_calibration_corpus.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_calibration_drift.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_calibration_tooling.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_catalog_selection.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_catalog_tier_promise.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_chat_primary_docs.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_checkstyle_adapter.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_checkstyle_catalog.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_ci_history_recipe.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_ci_installs_the_analyzer_pool.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_claimed_languages.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_cli.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_clone_group_class.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_cognitive_complexity.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_cohort_statistics.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_concealment_properties.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_config_shape.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_consent_and_grant.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_consumer_migration.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_contract_claims.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_corroborate.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_coverage_by_language.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_cpp_declarations.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_csharp_declarations.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_d15_composition.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_d15_goal_directed.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_dead_code.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_declaration_edge_cases.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_declaration_grading.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_declaration_ranges.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_declaration_source.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_declared_authorship.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_declared_imports.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_derivation_mix.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_determinism.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_dimension_ratchet.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_direct_runner_availability_class.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_discovery.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_doc_claims.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_docs_links.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_economic_context.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_empty_output_not_clean.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_empty_output_not_clean_adapters.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_environment_work_order.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_evidence_invariants.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_evidence_normalization.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_evidence_properties.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_exclusion_dialects.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_falsifier_standard.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_file_list_cap_class.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_finding_identity.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_first_run_elicitation.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_first_run_prompt.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_first_run_reelicitation.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_fix_breadth_window.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_fixed_form_fortran.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_format_ask.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_fortran_declarations.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_fortran_metrics.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_fortran_practice.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_git_argv.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_git_read_only.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_git_readonly_class.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_grant_only_user_tier.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_history.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_history_boundary.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_history_manifest.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_history_window.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_hostile_prompt.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_html_executive.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_html_section_parity_class.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_identity_docs.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_idiom_divergence.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_installed_wheel_runtime.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_instruction_targets.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_java_range_guard.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_java_ranges.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_java_scored.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_java_wiring.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_js_complexity_operators.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_js_declarations.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_language_coverage.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_mcp_baseline_payload.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_mcp_history.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_mcp_server.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_migration_1_0.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_near_duplicates.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_network_disclosure.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_one_setup_across_surfaces_class.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_pattern_budget.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_phase6_claims.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_pillars_and_practice.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_platform_claim.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_pmd_adapter.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_pmd_audit_closures.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_pool_runs_by_config.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_population_floors.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_practice_nested_gates_class.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_presentation_refusals.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_promises.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_prompt_escalation_once.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_prompt_honesty.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_prompt_location_escalation_class.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_prove_falsifiers.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_python_declarations.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_read_boundary.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_readme_claims.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_recurrence.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_release_plan.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_release_plan_status.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_report_remedy_copy.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_roles_recorded.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_root_grants.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_rubric_ignores_project_lint_config.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_runner.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_sarif.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_scan_history.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_scan_scope.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_scanning.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_schema_fidelity.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_scope_conformance.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_scoring_calibration.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_semantic_policy.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_setup_gate_completeness.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_setup_gate_independent_contract.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_setup_precondition.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_skill_install.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_source_index.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_spotbugs_adapter.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_spotbugs_catalog.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_stage8_contract.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_suite_isolation.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_swift_declarations.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_tdd_structure.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_test_commands.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_test_execution_class.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_three_presentations.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_trends.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_type_fields_not_functions_class.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_uat_falsifiers.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_unread_code.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_user_config_tier.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_verification_audit_closures.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_verified_grade.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_version_claim.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_wheel_contents.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_work_order.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_work_order_items.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_work_order_selection.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_workflow_supply_chain.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_write_boundary.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/tests/test_write_route_class.py +0 -0
- {maintainability_agent-2.5.0 → maintainability_agent-2.6.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.6.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.6.0**.
|
|
48
48
|
|
|
49
49
|
**Languages parsed:** Python, Java, C, C++, C#, Swift, Fortran (free-form
|
|
50
50
|
*and* fixed-form), and the JS/TS family — each by a scanner written for it, and
|
|
@@ -152,9 +152,8 @@ registered verdict stands at **INCONCLUSIVE**. Method, limits and raw data:
|
|
|
152
152
|
every claim the project could not stand behind was removed. A scoring engine
|
|
153
153
|
rebuilt after it was found to grade repo *size*; a headline claim about AI
|
|
154
154
|
authorship retracted against a matched control; a score that is withheld rather
|
|
155
|
-
than guessed. That arc is the credibility, and it is recorded in full
|
|
156
|
-
figures quoted from their approved summaries
|
|
157
|
-
[the track record](docs/track-record.md).
|
|
155
|
+
than guessed. That arc is the credibility, and it is recorded in full, with the
|
|
156
|
+
figures quoted from their approved summaries, in [the track record](docs/track-record.md).
|
|
158
157
|
|
|
159
158
|
## Install
|
|
160
159
|
|
|
@@ -223,8 +222,8 @@ withheld and the scope is named as the reason.
|
|
|
223
222
|
### Checking that the agent did the work
|
|
224
223
|
|
|
225
224
|
The work order tells an agent to fix exactly these findings and refactor nothing
|
|
226
|
-
else. These flags check that the diff
|
|
227
|
-
|
|
225
|
+
else. These flags check that the diff obeyed it — they read its _shape_, not its
|
|
226
|
+
correctness:
|
|
228
227
|
|
|
229
228
|
```bash
|
|
230
229
|
maintainability-agent \
|
|
@@ -251,6 +250,11 @@ maintainability-agent \
|
|
|
251
250
|
work order, and what moved. It is reproducible and **not signed** — nothing
|
|
252
251
|
here holds a key, and the document says so in its own text. A check nobody ran
|
|
253
252
|
renders as _not asked_, never as passed.
|
|
253
|
+
- **`--transformation NAME`** names the class of work a scan followed and
|
|
254
|
+
reports how this run of it compares with earlier ones. **A report, never a
|
|
255
|
+
gate.** It says one run "moved further" than another, never that it was
|
|
256
|
+
_better_ — two runs of one codemod land on different code — and it measures
|
|
257
|
+
the interval, not the transformation.
|
|
254
258
|
|
|
255
259
|
The [roadmap](docs/roadmap.md#the-remediation-hole-closed-in-210-through-230)
|
|
256
260
|
has the history of why this was the hole under the product's central claim.
|
|
@@ -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.6.0**.
|
|
9
9
|
|
|
10
10
|
**Languages parsed:** Python, Java, C, C++, C#, Swift, Fortran (free-form
|
|
11
11
|
*and* fixed-form), and the JS/TS family — each by a scanner written for it, and
|
|
@@ -113,9 +113,8 @@ registered verdict stands at **INCONCLUSIVE**. Method, limits and raw data:
|
|
|
113
113
|
every claim the project could not stand behind was removed. A scoring engine
|
|
114
114
|
rebuilt after it was found to grade repo *size*; a headline claim about AI
|
|
115
115
|
authorship retracted against a matched control; a score that is withheld rather
|
|
116
|
-
than guessed. That arc is the credibility, and it is recorded in full
|
|
117
|
-
figures quoted from their approved summaries
|
|
118
|
-
[the track record](docs/track-record.md).
|
|
116
|
+
than guessed. That arc is the credibility, and it is recorded in full, with the
|
|
117
|
+
figures quoted from their approved summaries, in [the track record](docs/track-record.md).
|
|
119
118
|
|
|
120
119
|
## Install
|
|
121
120
|
|
|
@@ -184,8 +183,8 @@ withheld and the scope is named as the reason.
|
|
|
184
183
|
### Checking that the agent did the work
|
|
185
184
|
|
|
186
185
|
The work order tells an agent to fix exactly these findings and refactor nothing
|
|
187
|
-
else. These flags check that the diff
|
|
188
|
-
|
|
186
|
+
else. These flags check that the diff obeyed it — they read its _shape_, not its
|
|
187
|
+
correctness:
|
|
189
188
|
|
|
190
189
|
```bash
|
|
191
190
|
maintainability-agent \
|
|
@@ -212,6 +211,11 @@ maintainability-agent \
|
|
|
212
211
|
work order, and what moved. It is reproducible and **not signed** — nothing
|
|
213
212
|
here holds a key, and the document says so in its own text. A check nobody ran
|
|
214
213
|
renders as _not asked_, never as passed.
|
|
214
|
+
- **`--transformation NAME`** names the class of work a scan followed and
|
|
215
|
+
reports how this run of it compares with earlier ones. **A report, never a
|
|
216
|
+
gate.** It says one run "moved further" than another, never that it was
|
|
217
|
+
_better_ — two runs of one codemod land on different code — and it measures
|
|
218
|
+
the interval, not the transformation.
|
|
215
219
|
|
|
216
220
|
The [roadmap](docs/roadmap.md#the-remediation-hole-closed-in-210-through-230)
|
|
217
221
|
has the history of why this was the hole under the product's central claim.
|
|
@@ -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.6.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.6.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.6.0**.
|
|
48
48
|
|
|
49
49
|
**Languages parsed:** Python, Java, C, C++, C#, Swift, Fortran (free-form
|
|
50
50
|
*and* fixed-form), and the JS/TS family — each by a scanner written for it, and
|
|
@@ -152,9 +152,8 @@ registered verdict stands at **INCONCLUSIVE**. Method, limits and raw data:
|
|
|
152
152
|
every claim the project could not stand behind was removed. A scoring engine
|
|
153
153
|
rebuilt after it was found to grade repo *size*; a headline claim about AI
|
|
154
154
|
authorship retracted against a matched control; a score that is withheld rather
|
|
155
|
-
than guessed. That arc is the credibility, and it is recorded in full
|
|
156
|
-
figures quoted from their approved summaries
|
|
157
|
-
[the track record](docs/track-record.md).
|
|
155
|
+
than guessed. That arc is the credibility, and it is recorded in full, with the
|
|
156
|
+
figures quoted from their approved summaries, in [the track record](docs/track-record.md).
|
|
158
157
|
|
|
159
158
|
## Install
|
|
160
159
|
|
|
@@ -223,8 +222,8 @@ withheld and the scope is named as the reason.
|
|
|
223
222
|
### Checking that the agent did the work
|
|
224
223
|
|
|
225
224
|
The work order tells an agent to fix exactly these findings and refactor nothing
|
|
226
|
-
else. These flags check that the diff
|
|
227
|
-
|
|
225
|
+
else. These flags check that the diff obeyed it — they read its _shape_, not its
|
|
226
|
+
correctness:
|
|
228
227
|
|
|
229
228
|
```bash
|
|
230
229
|
maintainability-agent \
|
|
@@ -251,6 +250,11 @@ maintainability-agent \
|
|
|
251
250
|
work order, and what moved. It is reproducible and **not signed** — nothing
|
|
252
251
|
here holds a key, and the document says so in its own text. A check nobody ran
|
|
253
252
|
renders as _not asked_, never as passed.
|
|
253
|
+
- **`--transformation NAME`** names the class of work a scan followed and
|
|
254
|
+
reports how this run of it compares with earlier ones. **A report, never a
|
|
255
|
+
gate.** It says one run "moved further" than another, never that it was
|
|
256
|
+
_better_ — two runs of one codemod land on different code — and it measures
|
|
257
|
+
the interval, not the transformation.
|
|
254
258
|
|
|
255
259
|
The [roadmap](docs/roadmap.md#the-remediation-hole-closed-in-210-through-230)
|
|
256
260
|
has the history of why this was the hole under the product's central claim.
|
|
@@ -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/_arguments.py
|
|
14
15
|
src/maintainability_audit/_aspects.py
|
|
15
16
|
src/maintainability_audit/_attestation.py
|
|
16
17
|
src/maintainability_audit/_backfill.py
|
|
@@ -67,6 +68,7 @@ src/maintainability_audit/_ranges_js.py
|
|
|
67
68
|
src/maintainability_audit/_ranges_swift.py
|
|
68
69
|
src/maintainability_audit/_ratchet.py
|
|
69
70
|
src/maintainability_audit/_recurrence.py
|
|
71
|
+
src/maintainability_audit/_run_comparison.py
|
|
70
72
|
src/maintainability_audit/_runner.py
|
|
71
73
|
src/maintainability_audit/_safe_write.py
|
|
72
74
|
src/maintainability_audit/_scan_history.py
|
|
@@ -274,6 +276,7 @@ tests/test_report_remedy_copy.py
|
|
|
274
276
|
tests/test_roles_recorded.py
|
|
275
277
|
tests/test_root_grants.py
|
|
276
278
|
tests/test_rubric_ignores_project_lint_config.py
|
|
279
|
+
tests/test_run_comparison.py
|
|
277
280
|
tests/test_runner.py
|
|
278
281
|
tests/test_sarif.py
|
|
279
282
|
tests/test_scan_history.py
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""What the command line accepts, separate from what the command does.
|
|
2
|
+
|
|
3
|
+
Split out of `cli` when it crossed this project's own `max_file_lines`
|
|
4
|
+
gate — the second time that gate has shaped this module, after
|
|
5
|
+
`_add_gate_arguments` was split from `add_arguments` for the function
|
|
6
|
+
budget. Both splits were forced by the tool on a change to the tool, which
|
|
7
|
+
is the gate doing the job it exists for rather than an inconvenience.
|
|
8
|
+
|
|
9
|
+
The boundary is deliberate and not merely a line count: everything here
|
|
10
|
+
declares a flag and nothing here runs an audit. `cli` imports it, never
|
|
11
|
+
the reverse.
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import argparse
|
|
16
|
+
|
|
17
|
+
from .config import VERSION
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _add_gate_arguments(parser: argparse.ArgumentParser) -> None:
|
|
21
|
+
"""What can make a run fail, and the conformance record behind one.
|
|
22
|
+
|
|
23
|
+
Split from `add_arguments` when the conformance flags took it past this
|
|
24
|
+
project's own function-length gate — which is the gate doing its job on
|
|
25
|
+
the change that added a gate.
|
|
26
|
+
"""
|
|
27
|
+
parser.add_argument("--fail-on-gate", action="store_true", help="Exit 1 when hard gates fail.")
|
|
28
|
+
parser.add_argument(
|
|
29
|
+
"--conformance",
|
|
30
|
+
metavar="REVSPEC",
|
|
31
|
+
help="Report how a diff relates to the work order, e.g. `main...HEAD`. "
|
|
32
|
+
"The bounded work order says 'fix exactly these and refactor nothing "
|
|
33
|
+
"else'; without this the bound is an instruction nothing checks. "
|
|
34
|
+
"Reports only unless --fail-on-out-of-scope is passed.",
|
|
35
|
+
)
|
|
36
|
+
parser.add_argument(
|
|
37
|
+
"--fail-on-regression", action="store_true",
|
|
38
|
+
help="Exit 1 when any scoring category is lower than the previous "
|
|
39
|
+
"comparable scan. Reads the recorded history, and refuses to "
|
|
40
|
+
"compare across an instrument change rather than reporting a "
|
|
41
|
+
"false regression: two scans taken under different calibration "
|
|
42
|
+
"cannot be subtracted.",
|
|
43
|
+
)
|
|
44
|
+
parser.add_argument(
|
|
45
|
+
"--fail-on-out-of-scope", action="store_true",
|
|
46
|
+
help="With --conformance, exit 1 when the diff touched files the work "
|
|
47
|
+
"order did not name and that do not pair to one as its test, or "
|
|
48
|
+
"when it added a suppression to a file the work order flagged.",
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _add_artifact_arguments(parser: argparse.ArgumentParser) -> None:
|
|
53
|
+
"""The documents a run can write beside its report, and the run's label.
|
|
54
|
+
|
|
55
|
+
Split out for the same reason `_add_gate_arguments` was, and by the same
|
|
56
|
+
gate: `--transformation` took `add_arguments` past this project's own
|
|
57
|
+
function-length threshold, so the gate failed the change that added a
|
|
58
|
+
reporting flag. Grouped by what they produce rather than alphabetically
|
|
59
|
+
— every one of these is an artifact for somebody other than the person
|
|
60
|
+
running the command.
|
|
61
|
+
"""
|
|
62
|
+
parser.add_argument("--prompt-output", help="Optional Markdown prompt for AI-assisted remediation.")
|
|
63
|
+
parser.add_argument("--comment-output", help="Optional Markdown body suitable for a PR comment.")
|
|
64
|
+
parser.add_argument("--agent-instructions-output", help="Optional reusable instructions for AI coding agents.")
|
|
65
|
+
parser.add_argument(
|
|
66
|
+
"--attestation-output",
|
|
67
|
+
help="Write the attestation: an independent, reproducible record of "
|
|
68
|
+
"what was measured, what the change was told to do, what it did, "
|
|
69
|
+
"and what moved. Populated by --conformance and "
|
|
70
|
+
"--fail-on-regression; a generator cannot produce this about its "
|
|
71
|
+
"own output.",
|
|
72
|
+
)
|
|
73
|
+
parser.add_argument(
|
|
74
|
+
"--transformation",
|
|
75
|
+
metavar="NAME",
|
|
76
|
+
help="Name the class of work this scan followed, e.g. `react-18`. "
|
|
77
|
+
"Records the name on the scan and reports how this run of it "
|
|
78
|
+
"compares with earlier ones. Nothing in a tree says which "
|
|
79
|
+
"transformation produced it, so this is your claim and the tool "
|
|
80
|
+
"records it without verifying it. A report, never a gate.",
|
|
81
|
+
)
|
|
82
|
+
parser.add_argument("--hostile-prompt-output",
|
|
83
|
+
help="Optional adversarial audit brief seeded from this run (ADR 013). "
|
|
84
|
+
"Text only: it does not gate, score, or send anything.")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def add_arguments(parser: argparse.ArgumentParser) -> None:
|
|
88
|
+
parser.add_argument("--version", action="version", version=f"maintainability-agent {VERSION}")
|
|
89
|
+
parser.add_argument("--root", default=".", help="Repository root to scan.")
|
|
90
|
+
parser.add_argument("--config", help="Path to JSON config.")
|
|
91
|
+
# Default None, not "markdown": the TTY question (8.4) only fires
|
|
92
|
+
# when the user stated nothing, and argparse cannot distinguish a
|
|
93
|
+
# stated default from an omitted flag.
|
|
94
|
+
parser.add_argument("--format", choices=["json", "markdown", "html"], default=None)
|
|
95
|
+
parser.add_argument("--html-output", help="Write the single-file HTML report here (ADR 011).")
|
|
96
|
+
parser.add_argument("--output", help="Output file. Defaults to stdout.")
|
|
97
|
+
_add_artifact_arguments(parser)
|
|
98
|
+
parser.add_argument("--sarif-output", help="Optional SARIF output path for GitHub code scanning.")
|
|
99
|
+
parser.add_argument("--sarif-input", action="append", help="Optional external SARIF file to summarize in reports. Repeatable.")
|
|
100
|
+
parser.add_argument("--changed-only", help="Audit only files changed in a git revspec, for example main...HEAD.")
|
|
101
|
+
parser.add_argument("--baseline", help="Existing baseline JSON. With --fail-on-new, only new findings fail.")
|
|
102
|
+
parser.add_argument("--write-baseline", help="Write current findings to a baseline JSON file.")
|
|
103
|
+
parser.add_argument("--fail-on-new", action="store_true", help="Fail only when findings are not in --baseline.")
|
|
104
|
+
parser.add_argument(
|
|
105
|
+
"--analyzers", action="store_true",
|
|
106
|
+
help="Force the external analyzer pool on for this run. The pool is "
|
|
107
|
+
"the primary evidence source (ADR 006, docs/analyzer-pool.md); a "
|
|
108
|
+
"repository with a config file runs it by default, so this flag "
|
|
109
|
+
"matters only for unconfigured trees or to override "
|
|
110
|
+
"analyzers.run=false. Measurements move the point estimate where "
|
|
111
|
+
"the full concept set was measured; otherwise the built-in "
|
|
112
|
+
"fallback stands and the range widens around the two.",
|
|
113
|
+
)
|
|
114
|
+
parser.add_argument(
|
|
115
|
+
"--no-analyzers", action="store_true",
|
|
116
|
+
help="Force the analyzer pool off for this run, overriding the "
|
|
117
|
+
"config. The report will say the built-in fallback supplied the "
|
|
118
|
+
"evidence (P8); it is a faster scan, not the full audit.",
|
|
119
|
+
)
|
|
120
|
+
parser.add_argument(
|
|
121
|
+
"--work", action="append", metavar="AXIS=VALUE",
|
|
122
|
+
help="Narrow the work order, for example --work band=quick-win or "
|
|
123
|
+
"--work path=src/. Repeatable; every criterion must match. "
|
|
124
|
+
"Axes: band, finding_class, path, verification. Narrowing "
|
|
125
|
+
"changes what is shown and never what anything scored.",
|
|
126
|
+
)
|
|
127
|
+
parser.add_argument(
|
|
128
|
+
"--record-history", action="store_true",
|
|
129
|
+
help="Append this scan to the history at paths.history (default "
|
|
130
|
+
".maintainability/history.jsonl). Opt-in, like every other write "
|
|
131
|
+
"this tool performs. Once the file exists, every later successful "
|
|
132
|
+
"scan appends to it and reads the resulting history.",
|
|
133
|
+
)
|
|
134
|
+
parser.add_argument(
|
|
135
|
+
"--backfill", metavar="REVSPEC",
|
|
136
|
+
help="Scan each commit in a range into the history and exit, for "
|
|
137
|
+
"example --backfill HEAD~50..HEAD. Each commit is checked out in "
|
|
138
|
+
"a temporary worktree, so the working tree is never touched. "
|
|
139
|
+
"Expensive and therefore explicit: it never runs as part of a "
|
|
140
|
+
"normal scan. Records are marked as reconstructed.",
|
|
141
|
+
)
|
|
142
|
+
parser.add_argument(
|
|
143
|
+
"--backfill-interval", type=int, default=1, metavar="N",
|
|
144
|
+
help="With --backfill, scan every Nth commit instead of all of them. "
|
|
145
|
+
"The shape of a series is what a trend reads, and a thousand "
|
|
146
|
+
"commits is hours of work nobody asked for.",
|
|
147
|
+
)
|
|
148
|
+
_add_gate_arguments(parser)
|
|
149
|
+
_add_setup_actions(parser)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _add_setup_actions(parser: argparse.ArgumentParser) -> None:
|
|
153
|
+
"""Flags that perform a setup action and exit instead of auditing."""
|
|
154
|
+
parser.add_argument("--init-agent-standards", action="store_true", help="Write model/tool-specific instruction files and exit.")
|
|
155
|
+
parser.add_argument(
|
|
156
|
+
"--install-skill", action="store_true",
|
|
157
|
+
help="Copy the packaged agent skill into the skills directory and "
|
|
158
|
+
"exit. Re-run after every upgrade: an installed skill that "
|
|
159
|
+
"drifts from the shipped one teaches agents a dead workflow.",
|
|
160
|
+
)
|
|
161
|
+
parser.add_argument(
|
|
162
|
+
"--skills-dir", default=None, metavar="DIR",
|
|
163
|
+
help="Where --install-skill writes (default: ~/.claude/skills).",
|
|
164
|
+
)
|
|
165
|
+
parser.add_argument(
|
|
166
|
+
"--force-skill", action="store_true",
|
|
167
|
+
help="With --install-skill: overwrite a differing installed copy "
|
|
168
|
+
"and remove files the package no longer ships. Without it, a "
|
|
169
|
+
"differing copy is refused with the list of differences.",
|
|
170
|
+
)
|
|
171
|
+
parser.add_argument(
|
|
172
|
+
"--target-path",
|
|
173
|
+
action="append",
|
|
174
|
+
metavar="TARGET=PATH",
|
|
175
|
+
help="Where a target's standing instructions go, e.g. "
|
|
176
|
+
"`--target-path bob=.bob/instructions.md`. Required for any agent "
|
|
177
|
+
"this tool has no built-in convention for; overrides the built-in "
|
|
178
|
+
"path for one it does. The target list will always trail the "
|
|
179
|
+
"market, and guessing a path writes a file the agent never opens.",
|
|
180
|
+
)
|
|
181
|
+
parser.add_argument(
|
|
182
|
+
"--target",
|
|
183
|
+
action="append",
|
|
184
|
+
help="Instruction target. Repeatable. Used with --init-agent-standards.",
|
|
185
|
+
)
|
|
186
|
+
parser.add_argument("--instructions-output-dir", default=".", help="Directory for generated instruction files.")
|
{maintainability_agent-2.5.0 → maintainability_agent-2.6.0}/src/maintainability_audit/_gates.py
RENAMED
|
@@ -51,18 +51,42 @@ def attach_ratchet(report: dict, history_path: Path) -> None:
|
|
|
51
51
|
report["dimension_ratchet"] = dimension_ratchet(read_history(history_path))
|
|
52
52
|
|
|
53
53
|
|
|
54
|
+
def attach_run_comparison(
|
|
55
|
+
args: argparse.Namespace, report: dict, history_path: Path
|
|
56
|
+
) -> None:
|
|
57
|
+
"""Compare this run of a named transformation with earlier ones.
|
|
58
|
+
|
|
59
|
+
Only when the operator named one: an unnamed run belongs to no series,
|
|
60
|
+
and inventing a series for it would be the tool answering a question
|
|
61
|
+
nobody asked.
|
|
62
|
+
"""
|
|
63
|
+
label = getattr(args, "transformation", None)
|
|
64
|
+
if not label or not history_path.exists():
|
|
65
|
+
return
|
|
66
|
+
from ._run_comparison import compare_runs
|
|
67
|
+
from ._scan_history import read_history
|
|
68
|
+
|
|
69
|
+
report["run_comparison"] = compare_runs(read_history(history_path), label)
|
|
70
|
+
|
|
71
|
+
|
|
54
72
|
def _attach_post_audit_records(
|
|
55
73
|
args: argparse.Namespace, report: dict, history_path: Path
|
|
56
74
|
) -> None:
|
|
57
75
|
"""Records composed from the finished report, before it is rendered.
|
|
58
76
|
|
|
59
|
-
|
|
60
|
-
whether a diff obeyed its work order,
|
|
61
|
-
since the last comparable scan,
|
|
62
|
-
|
|
77
|
+
All three read the report or the history rather than the tree, and none
|
|
78
|
+
reaches scoring: whether a diff obeyed its work order, whether a
|
|
79
|
+
dimension slipped since the last comparable scan, and how this run of a
|
|
80
|
+
transformation compares with earlier ones are facts about a change and
|
|
81
|
+
its history — not evidence about the code's condition.
|
|
82
|
+
|
|
83
|
+
The third is the only one that cannot fail a build. It is a report, by
|
|
84
|
+
design: "run seven moved further than run six" is worth knowing and is
|
|
85
|
+
not a thing to block a merge on.
|
|
63
86
|
"""
|
|
64
87
|
attach_conformance(args, report)
|
|
65
88
|
attach_ratchet(report, history_path)
|
|
89
|
+
attach_run_comparison(args, report, history_path)
|
|
66
90
|
|
|
67
91
|
|
|
68
92
|
def _conformance_exit(args: argparse.Namespace, report: dict) -> int:
|
|
@@ -120,3 +120,49 @@ def escalations_markdown(escalated: list[dict[str, Any]] | None) -> list[str]:
|
|
|
120
120
|
])
|
|
121
121
|
lines.append("")
|
|
122
122
|
return lines
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def run_comparison_markdown(comparison: dict[str, Any] | None) -> list[str]:
|
|
126
|
+
"""The markdown section. Presentation only — it computes no score.
|
|
127
|
+
|
|
128
|
+
Empty for a run nobody named, which is most of them.
|
|
129
|
+
"""
|
|
130
|
+
if not comparison:
|
|
131
|
+
return []
|
|
132
|
+
label = comparison["label"]
|
|
133
|
+
lines = [f"## Runs of `{label}`", ""]
|
|
134
|
+
runs = comparison["runs"]
|
|
135
|
+
if not runs:
|
|
136
|
+
return [*lines, comparison["trend"].get("reason", "No runs recorded."), ""]
|
|
137
|
+
|
|
138
|
+
lines += ["| Recorded | Commit | Estimate | Moved | Files | Declarations |",
|
|
139
|
+
"|---|---|---|---|---|---|"]
|
|
140
|
+
for run in runs:
|
|
141
|
+
estimate = "withheld" if run["estimate"] is None else f"{run['estimate']:.2f}"
|
|
142
|
+
moved = "—" if run["moved"] is None else f"{run['moved']:+.2f}"
|
|
143
|
+
populations = run["populations"]
|
|
144
|
+
lines.append(
|
|
145
|
+
f"| {run['recorded_at']} | `{(run['commit'] or '')[:8]}` | {estimate} | "
|
|
146
|
+
f"{moved} | {populations.get('files_scanned', '—')} | "
|
|
147
|
+
f"{populations.get('declarations_scanned', '—')} |"
|
|
148
|
+
)
|
|
149
|
+
lines.append("")
|
|
150
|
+
|
|
151
|
+
trend = comparison["trend"]
|
|
152
|
+
lines.append(
|
|
153
|
+
f"**{trend['summary']}.**" if trend.get("comparable")
|
|
154
|
+
else f"**Not established:** {trend['reason']}."
|
|
155
|
+
)
|
|
156
|
+
if comparison.get("excluded_earlier_runs"):
|
|
157
|
+
lines += ["", (
|
|
158
|
+
f"{comparison['excluded_earlier_runs']} earlier run(s) under this "
|
|
159
|
+
f"name are excluded: {comparison['exclusion_reason']}."
|
|
160
|
+
)]
|
|
161
|
+
lines += ["", (
|
|
162
|
+
"*`Moved` is the change in estimate between the previous recorded "
|
|
163
|
+
"scan and this one — the movement across that interval, not the "
|
|
164
|
+
"effect of the transformation. Anything else that happened in the "
|
|
165
|
+
"same interval is inside the number, and two runs of one "
|
|
166
|
+
"transformation land on different code.*"
|
|
167
|
+
), ""]
|
|
168
|
+
return lines
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""Did run seven of this transformation land better than run six?
|
|
2
|
+
|
|
3
|
+
The last of the remediation-integrity items, and the only one that is a
|
|
4
|
+
**report rather than a gate**. Where an agent performs one class of work
|
|
5
|
+
repeatedly — a framework upgrade, a migration, a codemod applied across
|
|
6
|
+
services — nobody measures whether the seventh run produced better code
|
|
7
|
+
than the sixth. The generator cannot answer it: its output is not
|
|
8
|
+
reproducible and it keeps no memory across runs. This tool has pinned
|
|
9
|
+
references, structured finding identity and scan history, so the
|
|
10
|
+
comparison is a join, not new machinery.
|
|
11
|
+
|
|
12
|
+
**What names a run.** Nothing in a tree says "this scan followed the React
|
|
13
|
+
18 upgrade", so the operator says it: `--transformation react-18`. The
|
|
14
|
+
label rides on the scan record and groups runs into a series.
|
|
15
|
+
|
|
16
|
+
**What it measures, stated precisely because the temptation is to
|
|
17
|
+
overstate it.** For each labeled run, the movement between the *previous
|
|
18
|
+
scan in history* and that run. That is the movement across an interval,
|
|
19
|
+
and the transformation is what the operator says happened during that
|
|
20
|
+
interval. This module does not and cannot verify that claim: anything else
|
|
21
|
+
that happened in the same interval is inside the number. So the report
|
|
22
|
+
speaks of a run's *movement*, never of its *effect*, and never says one
|
|
23
|
+
run was "better" — it says one moved further than another, which is what
|
|
24
|
+
was actually measured.
|
|
25
|
+
|
|
26
|
+
**It refuses across an instrument change**, reusing `segments` exactly as
|
|
27
|
+
`_ratchet` does. Two scans taken under different calibration cannot be
|
|
28
|
+
subtracted, and a series that quietly spanned the 2.0 corpus extension
|
|
29
|
+
would have reported every transformation on earth as catastrophic. When
|
|
30
|
+
the newest labeled run sits in a different segment from its predecessors,
|
|
31
|
+
the earlier runs are named as excluded rather than dropped in silence:
|
|
32
|
+
a reader who cannot see the refusal will assume there was no history.
|
|
33
|
+
|
|
34
|
+
Nothing here reaches scoring, and nothing here fails a build.
|
|
35
|
+
"""
|
|
36
|
+
from __future__ import annotations
|
|
37
|
+
|
|
38
|
+
from typing import Any
|
|
39
|
+
|
|
40
|
+
from ._scan_history import ScanRecord, segments
|
|
41
|
+
|
|
42
|
+
#: The same rounding boundary `_ratchet` uses, and for the same reason:
|
|
43
|
+
#: estimates are published to one decimal, so a smaller difference is the
|
|
44
|
+
#: rounding and not a movement.
|
|
45
|
+
TOLERANCE = 0.05
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _movement(previous: ScanRecord | None, current: ScanRecord) -> float | None:
|
|
49
|
+
"""How far the estimate moved across the interval ending at `current`.
|
|
50
|
+
|
|
51
|
+
`None` where either end is missing an estimate. A withheld estimate is
|
|
52
|
+
not a zero — P7 withholds it precisely because the evidence did not
|
|
53
|
+
support a number, and subtracting from it would manufacture one.
|
|
54
|
+
"""
|
|
55
|
+
if previous is None or previous.estimate is None or current.estimate is None:
|
|
56
|
+
return None
|
|
57
|
+
return round(current.estimate - previous.estimate, 3)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _run_entry(
|
|
61
|
+
record: ScanRecord, previous: ScanRecord | None
|
|
62
|
+
) -> dict[str, Any]:
|
|
63
|
+
return {
|
|
64
|
+
"recorded_at": record.recorded_at,
|
|
65
|
+
"commit": record.commit,
|
|
66
|
+
"scope": record.scope,
|
|
67
|
+
"estimate": record.estimate,
|
|
68
|
+
"evidence_status": record.evidence_status,
|
|
69
|
+
# Named so a reader can tell a movement in the code from a movement
|
|
70
|
+
# in what was measured: a run that doubled the tree is a different
|
|
71
|
+
# event from one that changed the same tree's condition.
|
|
72
|
+
"populations": dict(record.populations),
|
|
73
|
+
"moved": _movement(previous, record),
|
|
74
|
+
# A reconstructed scan carries today's tooling against an old
|
|
75
|
+
# commit, which is not the scan that would have happened then.
|
|
76
|
+
"backfilled": record.backfilled,
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _direction(difference: float) -> str:
|
|
81
|
+
"""How to say it. Never "better" — see `_trend`."""
|
|
82
|
+
if abs(difference) < TOLERANCE:
|
|
83
|
+
return "within tolerance of"
|
|
84
|
+
return "further than" if difference > 0 else "less than"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _trend(runs: list[dict[str, Any]]) -> dict[str, Any]:
|
|
88
|
+
"""How the newest run's movement compares with the one before it.
|
|
89
|
+
|
|
90
|
+
Deliberately not called "better". Two runs of one codemod land on
|
|
91
|
+
different code, so a larger movement can come from the tree it was
|
|
92
|
+
applied to as easily as from the run. "Moved further" is what the
|
|
93
|
+
numbers support; "better" is a judgment about work this tool never saw.
|
|
94
|
+
"""
|
|
95
|
+
movements = [run for run in runs if run["moved"] is not None]
|
|
96
|
+
if len(movements) < 2:
|
|
97
|
+
return {
|
|
98
|
+
"comparable": False,
|
|
99
|
+
"reason": (
|
|
100
|
+
"fewer than two runs in this series carry a movement, so "
|
|
101
|
+
"there is nothing to compare them on"
|
|
102
|
+
),
|
|
103
|
+
}
|
|
104
|
+
latest, prior = movements[-1]["moved"], movements[-2]["moved"]
|
|
105
|
+
difference = round(latest - prior, 3)
|
|
106
|
+
direction = _direction(difference)
|
|
107
|
+
return {
|
|
108
|
+
"comparable": True,
|
|
109
|
+
"latest_moved": latest,
|
|
110
|
+
"previous_moved": prior,
|
|
111
|
+
"difference": difference,
|
|
112
|
+
"summary": (
|
|
113
|
+
f"the newest run moved {latest:+.2f}, {direction} the "
|
|
114
|
+
f"{prior:+.2f} of the run before it"
|
|
115
|
+
),
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _segment_holding(records: list[ScanRecord], wanted: ScanRecord) -> Any:
|
|
120
|
+
"""The instrument-segment containing one record.
|
|
121
|
+
|
|
122
|
+
Membership is by **identity**, not equality: two scans of an unchanged
|
|
123
|
+
tree differ only in their timestamp, and an equality test would find
|
|
124
|
+
the wrong one — silently attributing a run to the wrong instrument.
|
|
125
|
+
"""
|
|
126
|
+
return next(
|
|
127
|
+
segment for segment in segments(records)
|
|
128
|
+
if any(item is wanted for item in segment.records)
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _entries_within(segment: Any, labelled: list[ScanRecord]) -> list[dict[str, Any]]:
|
|
133
|
+
"""The labelled runs inside this segment, each with its predecessor.
|
|
134
|
+
|
|
135
|
+
A run's predecessor is the scan immediately before it *in the
|
|
136
|
+
segment*, whatever that scan was labelled — the movement being
|
|
137
|
+
measured is across the interval, not between two runs of the same
|
|
138
|
+
name.
|
|
139
|
+
"""
|
|
140
|
+
order = {id(record): index for index, record in enumerate(segment.records)}
|
|
141
|
+
return [
|
|
142
|
+
_run_entry(
|
|
143
|
+
record,
|
|
144
|
+
segment.records[order[id(record)] - 1] if order[id(record)] else None,
|
|
145
|
+
)
|
|
146
|
+
for record in labelled if id(record) in order
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def compare_runs(records: list[ScanRecord], label: str) -> dict[str, Any]:
|
|
151
|
+
"""Every recorded run of one transformation, and how the last two differ.
|
|
152
|
+
|
|
153
|
+
The current scan is already in `records` when this is called — history
|
|
154
|
+
is appended before the post-audit records are attached — so the newest
|
|
155
|
+
labeled run is this one.
|
|
156
|
+
"""
|
|
157
|
+
labelled = [record for record in records if record.transformation == label]
|
|
158
|
+
if not labelled:
|
|
159
|
+
return {
|
|
160
|
+
"label": label,
|
|
161
|
+
"runs": [],
|
|
162
|
+
"excluded_earlier_runs": 0,
|
|
163
|
+
"trend": {
|
|
164
|
+
"comparable": False,
|
|
165
|
+
"reason": f"no scan has been recorded under the name {label!r}",
|
|
166
|
+
},
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
current = _segment_holding(records, labelled[-1])
|
|
170
|
+
runs = _entries_within(current, labelled)
|
|
171
|
+
excluded = len(labelled) - len(runs)
|
|
172
|
+
|
|
173
|
+
comparison = {
|
|
174
|
+
"label": label,
|
|
175
|
+
"runs": runs,
|
|
176
|
+
"excluded_earlier_runs": excluded,
|
|
177
|
+
"trend": _trend(runs),
|
|
178
|
+
}
|
|
179
|
+
if excluded:
|
|
180
|
+
comparison["exclusion_reason"] = (
|
|
181
|
+
current.break_reason
|
|
182
|
+
or "those runs were produced by a different instrument"
|
|
183
|
+
)
|
|
184
|
+
return comparison
|