maintainability-agent 3.0.2__tar.gz → 3.1.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-3.0.2/src/maintainability_agent.egg-info → maintainability_agent-3.1.0}/PKG-INFO +2 -2
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/README.md +1 -1
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/pyproject.toml +1 -1
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0/src/maintainability_agent.egg-info}/PKG-INFO +2 -2
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_agent.egg-info/SOURCES.txt +5 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/__init__.py +1 -1
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_anchor.py +27 -6
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_assets/standard.md +39 -18
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_banner.py +23 -3
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_discovery.py +24 -2
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_environment.py +54 -2
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_in_loop.py +47 -1
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_practice.py +25 -1
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_semantic_ts.py +14 -4
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_setup_persist.py +47 -3
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_test_commands.py +23 -2
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_test_execution.py +23 -2
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/config.py +53 -2
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/metrics.py +21 -1
- maintainability_agent-3.1.0/tests/test_check_refuses_plus_only_fragments.py +38 -0
- maintainability_agent-3.1.0/tests/test_tree_cannot_opt_the_host_in.py +111 -0
- maintainability_agent-3.1.0/tests/test_tree_reads_use_the_primitive.py +167 -0
- maintainability_agent-3.1.0/tests/test_unanchored_set_matches_corpus.py +124 -0
- maintainability_agent-3.1.0/tests/test_work_order_binds_to_this_interpreter.py +72 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/LICENSE +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/setup.cfg +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_agent.egg-info/dependency_links.txt +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_agent.egg-info/entry_points.txt +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_agent.egg-info/requires.txt +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_agent.egg-info/top_level.txt +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/__main__.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_adapters.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_analysis.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_analyzer_sections.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_arguments.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_aspects.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_assets/analyzer-catalog.json +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_attestation.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_backfill.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_bands.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_built_ins.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_calibration.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_catalog.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_charts.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_cognitive.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_config_defaults.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_conformance.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_corroborate.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_coverage_notes.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_criteria_scope.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_derive.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_documents.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_economics.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_economics_view.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_evidence_view.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_finding_match.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_first_run.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_formula.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_gates.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_generic.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_grant_ledger.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_history_view.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_hostile_prompt.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_hotspots.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_html_report_sections.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_html_view.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_identity.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_in_loop_view.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_jvm_adapters.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_markdown_sections.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_masking.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_mcp_audit.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_mcp_gate.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_mcp_grants.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_mcp_refusals.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_mcp_resources.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_mcp_setup.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_metric_adapters.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_metrics_types.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_operator_reads.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_pillars.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_precommit.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_precommit_install.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_precommit_view.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_pressures.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_ranges_c.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_ranges_cobol.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_ranges_core.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_ranges_cpp.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_ranges_csharp.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_ranges_fortran.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_ranges_go.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_ranges_java.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_ranges_js.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_ranges_php.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_ranges_ruby.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_ranges_rust.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_ranges_swift.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_ratchet.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_recurrence.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_run_comparison.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_runner.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_safe_write.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_scan_history.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_scan_view.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_second_source.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_selection.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_semantic.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_semantic_policy.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_semantic_view.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_setup_errors.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_skill_data/SKILL.md +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_skill_data/agents/anthropic.yaml +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_skill_data/agents/copilot.yaml +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_skill_data/agents/openai.yaml +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_skill_data/copilot/maintainability-agent.prompt.md +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_skill_data/references/finding-taxonomy.md +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_skill_install.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_stored_grants.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_tdd_view.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_test_pairing.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_tokens.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_tool_adapters.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_trends.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_user_config.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_verdict_adapters.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_verification.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_work_order.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_work_order_view.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_work_order_weights.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_xml.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/baseline.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/cli.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/deadcode.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/declarations.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/duplication.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/evidence.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/git_tools.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/history.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/idioms.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/instructions.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/mcp_server.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/prompts.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/renderers.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/report.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/sarif.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/scoring.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/similarity.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/source.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_acquisition_trust.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_adapter_docs.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_adapter_recordings.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_adapters.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_added_lines.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_analysis_coverage.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_analysis_execution.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_analyzer_bridge.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_analyzer_config_isolation.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_analyzer_estimate_claims.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_analyzer_pinning.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_analyzer_primary_estimate.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_analyzer_provenance.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_analyzer_provenance_exclusions.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_analyzer_scoring_path.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_analyzer_spread_range.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_analyzer_xml_bounds.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_anchor_and_mode.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_anchor_disclosure.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_annotations_match_behaviour.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_anticipated_refusals.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_architecture.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_artifact_write_route_class.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_aspect_coverage.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_asset_snapshot_class.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_ast_reading_resolves_spellings.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_attestation.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_audit_components.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_authorization_freshness.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_authorship_gates.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_backfill.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_band_concealment_class.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_band_concealment_p3.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_band_pressures.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_bands.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_body_or_signature_class.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_c_declarations.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_calibration_corpus.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_calibration_drift.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_calibration_tooling.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_catalog_selection.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_catalog_tier_promise.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_chat_primary_docs.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_checkstyle_adapter.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_checkstyle_catalog.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_ci_history_recipe.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_ci_installs_the_analyzer_pool.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_claimed_languages.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_cli.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_clone_group_class.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_cobol_declarations.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_cognitive_complexity.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_cohort_statistics.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_concealment_properties.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_config_shape.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_consent_and_grant.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_consumer_migration.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_contract_claims.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_corroborate.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_coverage_by_language.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_cpp_declarations.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_criteria_completion.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_csharp_declarations.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_d15_composition.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_d15_goal_directed.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_dead_code.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_declaration_criteria_scope.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_declaration_edge_cases.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_declaration_grading.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_declaration_ranges.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_declaration_source.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_declared_authorship.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_declared_imports.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_derivation_mix.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_determinism.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_dimension_ratchet.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_direct_runner_availability_class.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_discovery.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_doc_claims.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_docs_links.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_economic_context.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_empty_output_not_clean.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_empty_output_not_clean_adapters.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_environment_work_order.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_evidence_invariants.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_evidence_normalization.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_evidence_properties.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_exclusion_dialects.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_falsifier_standard.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_file_list_cap_class.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_finding_identity.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_first_run_elicitation.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_first_run_prompt.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_first_run_reelicitation.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_fix_breadth_window.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_fixed_form_fortran.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_format_ask.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_fortran_declarations.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_fortran_metrics.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_fortran_practice.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_git_argv.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_git_read_only.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_git_readonly_class.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_go_declarations.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_grammar_constructs.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_grammar_token_coverage.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_grant_only_user_tier.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_history.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_history_boundary.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_history_manifest.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_history_schema2.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_history_window.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_hostile_prompt.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_html_executive.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_html_section_parity_class.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_identity_docs.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_identity_resolution.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_idiom_divergence.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_in_loop_budgets.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_in_loop_check.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_installed_wheel_runtime.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_instruction_targets.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_java_range_guard.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_java_ranges.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_java_scored.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_java_wiring.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_js_complexity_operators.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_js_declarations.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_language_coverage.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_language_support_honesty.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_mcp_baseline_payload.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_mcp_history.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_mcp_server.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_migration_1_0.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_near_duplicates.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_network_disclosure.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_one_setup_across_surfaces_class.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_operator_named_paths.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_operator_named_reads.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_pattern_budget.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_phase6_claims.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_php_declarations.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_pillars_and_practice.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_platform_claim.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_pmd_adapter.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_pmd_audit_closures.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_policy_claims.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_pool_runs_by_config.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_population_floors.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_practice_nested_gates_class.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_precommit_staged.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_presentation_refusals.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_promises.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_prompt_escalation_once.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_prompt_honesty.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_prompt_location_escalation_class.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_prove_falsifiers.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_python_complexity.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_python_declarations.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_read_boundary.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_readme_claims.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_recurrence.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_release_plan.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_release_plan_status.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_report_remedy_copy.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_roles_recorded.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_root_grants.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_rubric_ignores_project_lint_config.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_ruby_declarations.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_run_comparison.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_runner.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_rust_declarations.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_sarif.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_scan_history.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_scan_scope.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_scanner_unsafe_direction.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_scanning.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_schema_fidelity.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_scope_conformance.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_scoring_calibration.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_semantic_policy.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_setup_gate_completeness.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_setup_gate_independent_contract.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_setup_precondition.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_skill_install.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_sonar_resolve.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_source_index.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_spotbugs_adapter.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_spotbugs_catalog.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_stage8_contract.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_suite_isolation.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_swift_declarations.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_tdd_structure.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_test_commands.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_test_execution_class.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_three_presentations.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_tool_resolution.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_tree_chosen_spawn.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_trends.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_type_fields_not_functions_class.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_uat_falsifiers.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_unread_code.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_user_config_tier.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_verification_audit_closures.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_verified_grade.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_version_claim.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_wheel_contents.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_work_order.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_work_order_items.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_work_order_selection.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_workflow_supply_chain.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_write_boundary.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_write_route_class.py +0 -0
- {maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/tests/test_written_record.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: maintainability-agent
|
|
3
|
-
Version: 3.0
|
|
3
|
+
Version: 3.1.0
|
|
4
4
|
Summary: Maintainability gate and AI remediation prompt generator for CI.
|
|
5
5
|
Author: Marshall Guillory
|
|
6
6
|
License: MIT
|
|
@@ -45,7 +45,7 @@ Dynamic: license-file
|
|
|
45
45
|
**A deterministic, offline maintainability audit whose output is a _bounded
|
|
46
46
|
work order_ for an AI coding agent** — a copy-paste prompt, per finding, that
|
|
47
47
|
says *fix exactly these and refactor nothing else*. Chat-primary; CLI for CI.
|
|
48
|
-
Version **3.0
|
|
48
|
+
Version **3.1.0**.
|
|
49
49
|
|
|
50
50
|
**Planned, not shipped:** the audit tool will become `maintainability-audit`;
|
|
51
51
|
a new `maintainability-agent` companion will run it and automate human-selected
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
**A deterministic, offline maintainability audit whose output is a _bounded
|
|
6
6
|
work order_ for an AI coding agent** — a copy-paste prompt, per finding, that
|
|
7
7
|
says *fix exactly these and refactor nothing else*. Chat-primary; CLI for CI.
|
|
8
|
-
Version **3.0
|
|
8
|
+
Version **3.1.0**.
|
|
9
9
|
|
|
10
10
|
**Planned, not shipped:** the audit tool will become `maintainability-audit`;
|
|
11
11
|
a new `maintainability-agent` companion will run it and automate human-selected
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "maintainability-agent"
|
|
7
|
-
version = "3.0
|
|
7
|
+
version = "3.1.0"
|
|
8
8
|
description = "Maintainability gate and AI remediation prompt generator for CI."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
# 3.11, not 3.10. `_discovery`, `_pillars` and `_runner` all import
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: maintainability-agent
|
|
3
|
-
Version: 3.0
|
|
3
|
+
Version: 3.1.0
|
|
4
4
|
Summary: Maintainability gate and AI remediation prompt generator for CI.
|
|
5
5
|
Author: Marshall Guillory
|
|
6
6
|
License: MIT
|
|
@@ -45,7 +45,7 @@ Dynamic: license-file
|
|
|
45
45
|
**A deterministic, offline maintainability audit whose output is a _bounded
|
|
46
46
|
work order_ for an AI coding agent** — a copy-paste prompt, per finding, that
|
|
47
47
|
says *fix exactly these and refactor nothing else*. Chat-primary; CLI for CI.
|
|
48
|
-
Version **3.0
|
|
48
|
+
Version **3.1.0**.
|
|
49
49
|
|
|
50
50
|
**Planned, not shipped:** the audit tool will become `maintainability-audit`;
|
|
51
51
|
a new `maintainability-agent` companion will run it and automate human-selected
|
|
@@ -183,6 +183,7 @@ tests/test_calibration_tooling.py
|
|
|
183
183
|
tests/test_catalog_selection.py
|
|
184
184
|
tests/test_catalog_tier_promise.py
|
|
185
185
|
tests/test_chat_primary_docs.py
|
|
186
|
+
tests/test_check_refuses_plus_only_fragments.py
|
|
186
187
|
tests/test_checkstyle_adapter.py
|
|
187
188
|
tests/test_checkstyle_catalog.py
|
|
188
189
|
tests/test_ci_history_recipe.py
|
|
@@ -337,10 +338,13 @@ tests/test_test_commands.py
|
|
|
337
338
|
tests/test_test_execution_class.py
|
|
338
339
|
tests/test_three_presentations.py
|
|
339
340
|
tests/test_tool_resolution.py
|
|
341
|
+
tests/test_tree_cannot_opt_the_host_in.py
|
|
340
342
|
tests/test_tree_chosen_spawn.py
|
|
343
|
+
tests/test_tree_reads_use_the_primitive.py
|
|
341
344
|
tests/test_trends.py
|
|
342
345
|
tests/test_type_fields_not_functions_class.py
|
|
343
346
|
tests/test_uat_falsifiers.py
|
|
347
|
+
tests/test_unanchored_set_matches_corpus.py
|
|
344
348
|
tests/test_unread_code.py
|
|
345
349
|
tests/test_user_config_tier.py
|
|
346
350
|
tests/test_verification_audit_closures.py
|
|
@@ -348,6 +352,7 @@ tests/test_verified_grade.py
|
|
|
348
352
|
tests/test_version_claim.py
|
|
349
353
|
tests/test_wheel_contents.py
|
|
350
354
|
tests/test_work_order.py
|
|
355
|
+
tests/test_work_order_binds_to_this_interpreter.py
|
|
351
356
|
tests/test_work_order_items.py
|
|
352
357
|
tests/test_work_order_selection.py
|
|
353
358
|
tests/test_workflow_supply_chain.py
|
{maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_anchor.py
RENAMED
|
@@ -25,12 +25,33 @@ from __future__ import annotations
|
|
|
25
25
|
|
|
26
26
|
#: Parsed by a scanner, absent from the reference corpus.
|
|
27
27
|
#:
|
|
28
|
-
#: Stated rather than derived
|
|
29
|
-
#: repository file
|
|
30
|
-
#: be unavailable exactly where a
|
|
31
|
-
#:
|
|
32
|
-
#:
|
|
33
|
-
|
|
28
|
+
#: Stated rather than derived, and the reason still holds:
|
|
29
|
+
#: `tools/calibration/corpus.json` is a repository file that does not ship
|
|
30
|
+
#: in the wheel, so a runtime read would be unavailable exactly where a
|
|
31
|
+
#: user reads a grade. What a stated list buys in availability it owes in
|
|
32
|
+
#: upkeep, and it did not pay.
|
|
33
|
+
#:
|
|
34
|
+
#: **D143.** This read `("Swift", "COBOL", "Go", "Rust", "PHP", "Ruby")`
|
|
35
|
+
#: after 3.0.0 anchored five of those six. The corpus grew from 112
|
|
36
|
+
#: repositories to 180 and from eight measured languages to thirteen, and
|
|
37
|
+
#: this line did not move — so every skin told a Swift, Go, Rust, PHP or
|
|
38
|
+
#: Ruby reader their grade was provisional against a corpus that had
|
|
39
|
+
#: measured their language for two releases. A caveat that names an
|
|
40
|
+
#: anchored language is not a caution, it is a false statement about the
|
|
41
|
+
#: evidence, and it undersells the thing the recalibration was for.
|
|
42
|
+
#:
|
|
43
|
+
#: COBOL stays, and stays alone. It is parsed and deliberately never
|
|
44
|
+
#: anchored: GitHub holds one COBOL repository above 500 stars and what is
|
|
45
|
+
#: there is curriculum, Java parsers and JavaScript wrappers rather than
|
|
46
|
+
#: COBOL codebases, so the corpus policy excludes it permanently rather
|
|
47
|
+
#: than temporarily. Emptying this tuple would delete a disclosure that is
|
|
48
|
+
#: still true.
|
|
49
|
+
#:
|
|
50
|
+
#: `tests/test_unanchored_set_matches_corpus.py` recomputes
|
|
51
|
+
#: `parsed - corpus` from `DECLARATION_SUFFIXES` and `corpus.json` and
|
|
52
|
+
#: fails if this stops matching — including when a language is *added* to
|
|
53
|
+
#: the corpus, which is the direction that went unnoticed.
|
|
54
|
+
UNANCHORED_LANGUAGES: tuple[str, ...] = ("COBOL",)
|
|
34
55
|
|
|
35
56
|
|
|
36
57
|
def unanchored_names() -> str:
|
|
@@ -118,10 +118,20 @@ The corpus median lands at **4.0 (B)**: a well-run real codebase earns a B, and
|
|
|
118
118
|
|
|
119
119
|
### The reference corpus
|
|
120
120
|
|
|
121
|
-
Calibration is reproducible, not a snapshot someone took once. The corpus is defined in [`tools/calibration/corpus.json`](../tools/calibration/corpus.json) —
|
|
121
|
+
Calibration is reproducible, not a snapshot someone took once. The corpus is defined in [`tools/calibration/corpus.json`](../tools/calibration/corpus.json) — **180 mature open-source repositories pinned to exact commits**, spanning 24 to 18,789 source files and 2,749,575 declarations across thirteen languages: Python, TypeScript, JavaScript, Java, C, C++, C#, Fortran, Swift, Go, Rust, PHP and Ruby.
|
|
122
|
+
|
|
123
|
+
The forty repositories 1.x calibrated on are all still in it, at the commits they were measured at then:
|
|
122
124
|
|
|
123
125
|
> angular · ansible · ant-design · anime · axios · Chart.js · code-server · django · echarts · excalidraw · express · fastapi · flask · freeCodeCamp · github-readme-stats · hackingtool · hoppscotch · jquery · keras · localstack · lodash · manim · material-ui · mermaid · models · n8n · nest · playwright · reveal.js · scrapy · strapi · svelte · tabby · tailwindcss · transformers · uBlock · vite · webpack · youtube-dl · yt-dlp
|
|
124
126
|
|
|
127
|
+
**This paragraph opened "40 mature open-source repositories … across
|
|
128
|
+
Python, TypeScript and JavaScript" for two releases after 3.0.0 measured
|
|
129
|
+
180 across thirteen (D143).** The corrected numbers were four paragraphs
|
|
130
|
+
below it the whole time, so a reader met both and had nothing to tell
|
|
131
|
+
them which was current. Same defect as the unanchored caveat below and
|
|
132
|
+
found in the same pass: a count written into prose, with nothing that
|
|
133
|
+
fails when the thing it counts moves.
|
|
134
|
+
|
|
125
135
|
**What the anchor does not cover.** For five releases this section named a
|
|
126
136
|
real defect: the corpus was 40 repositories of Python, TypeScript and
|
|
127
137
|
JavaScript while the scanner parsed eight languages, so Java, C, C++, C#
|
|
@@ -131,23 +141,34 @@ median and fortran-lang/stdlib at **1.10x** — the first a true statement
|
|
|
131
141
|
about LAPACK relative to mature OSS *web* code, and not a statement about
|
|
132
142
|
typical Fortran, because no typical Fortran was in the comparison set.
|
|
133
143
|
|
|
134
|
-
**
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
pinned to the commits they were measured at, so
|
|
138
|
-
is the languages added and nothing else.
|
|
139
|
-
|
|
140
|
-
**
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
144
|
+
**3.0.0 extended the corpus.** It is now **180 repositories spanning
|
|
145
|
+
thirteen languages**: Python, TypeScript, JavaScript, Java, C, C++, C#,
|
|
146
|
+
Fortran, Swift, Go, Rust, PHP and Ruby. The 112 rows measured for 2.0.0
|
|
147
|
+
are unchanged and still pinned to the commits they were measured at, so
|
|
148
|
+
what moved the constants is the languages added and nothing else.
|
|
149
|
+
|
|
150
|
+
**One parsed language is not in it: COBOL.** It is **unanchored** — a
|
|
151
|
+
scanner reads it, and the medians its rates are compared against were
|
|
152
|
+
measured on other languages. The cost is disclosed rather than absorbed:
|
|
153
|
+
findings in COBOL are as good as their parser, a COBOL grade is
|
|
154
|
+
provisional, and every surface that prints a grade says so.
|
|
155
|
+
|
|
156
|
+
COBOL is unanchored **permanently**, not pending a re-measure. Against
|
|
157
|
+
the corpus criteria GitHub holds one COBOL repository above 500 stars,
|
|
158
|
+
and what is public is curriculum, Java parsers and JavaScript wrappers
|
|
159
|
+
rather than COBOL codebases; the language's real corpus is on private
|
|
160
|
+
mainframes. Anchoring it on what exists would build a reference out of
|
|
161
|
+
teaching material — an anchor that *looks* like one, which is worse than
|
|
162
|
+
the disclosed absence.
|
|
163
|
+
|
|
164
|
+
Two corrections are recorded here rather than quietly applied, because
|
|
165
|
+
both were live for multiple releases. This sentence read "every language
|
|
166
|
+
this scanner parses" for three releases after that stopped being true.
|
|
167
|
+
It then named **six** unanchored languages for two releases after 3.0.0
|
|
168
|
+
anchored five of them (D143) — Swift, Go, Rust, PHP and Ruby readers were
|
|
169
|
+
told their grade was provisional against a corpus that had measured their
|
|
170
|
+
language. A caveat naming an anchored language is not a caution; it is a
|
|
171
|
+
false statement about the evidence.
|
|
151
172
|
|
|
152
173
|
Three limits remain, and they are narrower than the one they replace:
|
|
153
174
|
|
{maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_banner.py
RENAMED
|
@@ -17,9 +17,12 @@ one-way.
|
|
|
17
17
|
"""
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
+
import os
|
|
20
21
|
import re
|
|
21
22
|
from pathlib import Path
|
|
22
23
|
|
|
24
|
+
from ._operator_reads import open_regular_file
|
|
25
|
+
|
|
23
26
|
# Whatever a language uses to open a comment, plus decoration a generator
|
|
24
27
|
# may pad the line with: `#`, `//`, `*`, `/*`, `!`, `--`, `<!--`, `;`, and
|
|
25
28
|
# the emoji rules some tools draw. `@` is spared, or `# @generated` would
|
|
@@ -40,12 +43,29 @@ BANNER_LINES = 20
|
|
|
40
43
|
def banner_says_generated(
|
|
41
44
|
path: Path, banners: tuple[str, ...]
|
|
42
45
|
) -> str | None:
|
|
43
|
-
"""The banner text, if the head of this file carries one.
|
|
46
|
+
"""The banner text, if the head of this file carries one.
|
|
47
|
+
|
|
48
|
+
The head only, so this stays cheap on a large tree — but read
|
|
49
|
+
*through* a checked handle rather than by name, so a FIFO refuses
|
|
50
|
+
instead of stopping the audit dead (D145). `path.open()` on one waits
|
|
51
|
+
for a writer that never comes, and the previous `except OSError`
|
|
52
|
+
could not have caught that: there is no error to catch, only a
|
|
53
|
+
process that never returns.
|
|
54
|
+
"""
|
|
44
55
|
try:
|
|
45
|
-
|
|
46
|
-
|
|
56
|
+
handle = open_regular_file(
|
|
57
|
+
path,
|
|
58
|
+
"Banner detection reads the head of a discovered file; a "
|
|
59
|
+
"device, socket or FIFO would block the audit rather than "
|
|
60
|
+
"declare itself generated.",
|
|
61
|
+
)
|
|
47
62
|
except OSError:
|
|
48
63
|
return None
|
|
64
|
+
try:
|
|
65
|
+
with os.fdopen(handle, "r", encoding="utf-8", errors="replace", closefd=False) as opened:
|
|
66
|
+
head = "".join(next(opened, "") for _ in range(BANNER_LINES)).lower()
|
|
67
|
+
finally:
|
|
68
|
+
os.close(handle)
|
|
49
69
|
previous: str | None = None
|
|
50
70
|
for raw in head.splitlines():
|
|
51
71
|
stripped = COMMENT_LEAD.sub("", raw).strip()
|
{maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_discovery.py
RENAMED
|
@@ -42,6 +42,7 @@ from typing import Any
|
|
|
42
42
|
|
|
43
43
|
from ._banner import banner_says_generated
|
|
44
44
|
from ._metrics_types import KNOWN_SOURCE_SUFFIXES, is_test_path
|
|
45
|
+
from ._operator_reads import PathNotAllowed, read_source_file
|
|
45
46
|
from .metrics import is_excluded
|
|
46
47
|
|
|
47
48
|
# The inventory names languages the way a person does; the analyzer
|
|
@@ -207,6 +208,19 @@ class Inventory:
|
|
|
207
208
|
return tally
|
|
208
209
|
|
|
209
210
|
|
|
211
|
+
def _text_of(path: Path) -> str:
|
|
212
|
+
"""A repository file's text, read through the regular-file primitive.
|
|
213
|
+
|
|
214
|
+
Discovery reads whatever the tree names — `package.json` anywhere
|
|
215
|
+
beneath the root, `.gitmodules` at it — so every path here is
|
|
216
|
+
repository-controlled and none of it was going through the door
|
|
217
|
+
D131 and D141 built (D145). `read_text` on a FIFO waits for a writer
|
|
218
|
+
that never comes, and no `except OSError` catches a process that
|
|
219
|
+
simply does not return.
|
|
220
|
+
"""
|
|
221
|
+
return "\n".join(read_source_file(path))
|
|
222
|
+
|
|
223
|
+
|
|
210
224
|
def _generated_directories(root: Path, excludes: tuple[str, ...]) -> dict[str, str]:
|
|
211
225
|
"""Directories a `package.json` script deletes and rebuilds."""
|
|
212
226
|
found: dict[str, str] = {}
|
|
@@ -215,7 +229,15 @@ def _generated_directories(root: Path, excludes: tuple[str, ...]) -> dict[str, s
|
|
|
215
229
|
if is_excluded(relative, list(excludes)):
|
|
216
230
|
continue
|
|
217
231
|
try:
|
|
218
|
-
scripts = json.loads(manifest
|
|
232
|
+
scripts = json.loads(_text_of(manifest)).get("scripts", {})
|
|
233
|
+
except PathNotAllowed:
|
|
234
|
+
# `PathNotAllowed` is a `ValueError`, so it would otherwise be
|
|
235
|
+
# swallowed by the clause below and the manifest would read as
|
|
236
|
+
# a malformed one rather than a refused one (D145). A FIFO
|
|
237
|
+
# named `package.json` is not bad JSON; it is a path this tool
|
|
238
|
+
# will not read, and saying so is the whole point of routing
|
|
239
|
+
# the read through the primitive.
|
|
240
|
+
raise
|
|
219
241
|
except (OSError, ValueError, AttributeError):
|
|
220
242
|
continue
|
|
221
243
|
if not isinstance(scripts, dict):
|
|
@@ -241,7 +263,7 @@ def _vendored_directories(root: Path) -> dict[str, str]:
|
|
|
241
263
|
modules = root / ".gitmodules"
|
|
242
264
|
if modules.exists():
|
|
243
265
|
try:
|
|
244
|
-
text = modules
|
|
266
|
+
text = _text_of(modules)
|
|
245
267
|
except OSError:
|
|
246
268
|
text = ""
|
|
247
269
|
for entry in re.finditer(r"^\s*path\s*=\s*(.+?)\s*$", text, re.MULTILINE):
|
|
@@ -13,6 +13,8 @@ holds that structurally.
|
|
|
13
13
|
"""
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
16
|
+
import shlex
|
|
17
|
+
import sys
|
|
16
18
|
from typing import Any
|
|
17
19
|
|
|
18
20
|
# Outcomes a user can act on. `timed-out` and `failed` are excluded on
|
|
@@ -53,6 +55,57 @@ _VERIFY: dict[str, str] = {
|
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
|
|
58
|
+
def _remedy_for(slug: str) -> dict[str, str]:
|
|
59
|
+
"""The install and verify commands, bound to the interpreter that audits.
|
|
60
|
+
|
|
61
|
+
A remedy is only a remedy if it addresses the same environment the
|
|
62
|
+
next audit reads. `pip install lizard` and `lizard --version` name
|
|
63
|
+
neither: they address whatever `pip` and `lizard` `PATH` happens to
|
|
64
|
+
resolve, which on any machine with more than one Python is a
|
|
65
|
+
different interpreter than the one that just reported the tool
|
|
66
|
+
missing (D144).
|
|
67
|
+
|
|
68
|
+
That is not hypothetical here. D142 was the same gap seen from the
|
|
69
|
+
other side — the agent could not find tools the operator had already
|
|
70
|
+
installed, because `pip` had put them where `PATH` did not look. A
|
|
71
|
+
work order that hands back a bare `pip` sends the operator around
|
|
72
|
+
that loop again, and the second time it looks like the remedy simply
|
|
73
|
+
does not work.
|
|
74
|
+
|
|
75
|
+
So pip-backed adapters emit `{sys.executable} -m pip ...`. The dict
|
|
76
|
+
entries stay spelled as distribution names — `pip install
|
|
77
|
+
fortitude-lint` — because two tests derive the CI install list from
|
|
78
|
+
them, and rewriting the dict would move a fact that belongs at the
|
|
79
|
+
emit site into the data.
|
|
80
|
+
|
|
81
|
+
Verification moves from `--version` to `pip show` for these, and the
|
|
82
|
+
reason is worth stating because the old comment argued the opposite:
|
|
83
|
+
`--version` was chosen to run the same probe the runner runs. Since
|
|
84
|
+
D142 the runner does not probe `PATH` at all — it searches this
|
|
85
|
+
interpreter's script directories — so a bare `{slug} --version`
|
|
86
|
+
verifies a *different* thing than the audit does. `pip show` asks the
|
|
87
|
+
question that actually decides the next run: is this distribution
|
|
88
|
+
installed for this interpreter.
|
|
89
|
+
|
|
90
|
+
Node and JVM overrides are untouched. `npx` and `brew` are not
|
|
91
|
+
interpreter-scoped and rewriting them through `sys.executable` would
|
|
92
|
+
name a Python package that does not exist.
|
|
93
|
+
"""
|
|
94
|
+
install = _INSTALL.get(slug, f"pip install {slug}")
|
|
95
|
+
if not install.startswith("pip install"):
|
|
96
|
+
return {"install": install, "verify": _VERIFY.get(slug, f"{slug} --version")}
|
|
97
|
+
distribution = install.split()[-1]
|
|
98
|
+
return {
|
|
99
|
+
"install": _pip_command("install", distribution),
|
|
100
|
+
"verify": _pip_command("show", distribution),
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _pip_command(action: str, distribution: str) -> str:
|
|
105
|
+
"""A pip invocation that cannot resolve to another interpreter."""
|
|
106
|
+
return f"{shlex.quote(sys.executable)} -m pip {action} {distribution}"
|
|
107
|
+
|
|
108
|
+
|
|
56
109
|
def environment_work_order(coverage: list[Any]) -> list[dict[str, str]]:
|
|
57
110
|
"""One item per selected analyzer that could not contribute.
|
|
58
111
|
|
|
@@ -82,8 +135,7 @@ def environment_work_order(coverage: list[Any]) -> list[dict[str, str]]:
|
|
|
82
135
|
items.append({
|
|
83
136
|
"tool": entry.slug,
|
|
84
137
|
"reason": entry.detail or f"outcome: {entry.outcome}",
|
|
85
|
-
|
|
86
|
-
"verify": _VERIFY.get(entry.slug, f"{entry.slug} --version"),
|
|
138
|
+
**_remedy_for(entry.slug),
|
|
87
139
|
# What installing the tool restores, so a host can explain
|
|
88
140
|
# the trade instead of relaying a bare command (D9).
|
|
89
141
|
"concepts": ", ".join(entry.concepts),
|
{maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_in_loop.py
RENAMED
|
@@ -299,6 +299,52 @@ def _is_unified_diff(text: str) -> bool:
|
|
|
299
299
|
return has_hunk or (has_old and has_new)
|
|
300
300
|
|
|
301
301
|
|
|
302
|
+
#: The two marks a unified diff puts in column one on a changed line.
|
|
303
|
+
#: Context lines carry a space, which is why a fragment of *context*
|
|
304
|
+
#: still parses: it is the file's own text with one column of padding,
|
|
305
|
+
#: and refusing it would refuse the ordinary case of a pasted excerpt.
|
|
306
|
+
_CHANGE_MARKS = ("+", "-")
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def _is_diff_fragment(text: str) -> bool:
|
|
310
|
+
"""Whether this is the changed lines of a diff with the frame cut off.
|
|
311
|
+
|
|
312
|
+
D138 loosened `_is_unified_diff` so a hunk-only paste is refused,
|
|
313
|
+
because a chat window shows the hunk and not the file headers. This
|
|
314
|
+
is the paste one step smaller again, and the commonest of all: an
|
|
315
|
+
agent copies the *green* lines out of a review pane and pipes them
|
|
316
|
+
to `--check`. There is no `@@` and there are no `---`/`+++`
|
|
317
|
+
headers, so every shape check above passes it through, the brace
|
|
318
|
+
scanners find nothing in `+function hello() {`, and
|
|
319
|
+
`declarations_read` reports true (D146).
|
|
320
|
+
|
|
321
|
+
That is the same absence-read-as-a-pass D133 and D138 each closed
|
|
322
|
+
one fragment shape to the left, and the third time it arrived the
|
|
323
|
+
detector was still being widened by example rather than by rule. So
|
|
324
|
+
the rule is stated here: a body whose every non-blank line begins
|
|
325
|
+
with a change mark is a diff fragment, whatever suffix it was
|
|
326
|
+
named. Nothing else in this project's fourteen languages is written
|
|
327
|
+
that way, and a file that genuinely is would still be measured as
|
|
328
|
+
zero declarations rather than silently reported as read.
|
|
329
|
+
|
|
330
|
+
**The mention-versus-assertion guard survives, and by construction
|
|
331
|
+
rather than by luck.** Somebody writing *about* a diff writes prose
|
|
332
|
+
or code around the quoted line — `text = '+function hello()'` — and
|
|
333
|
+
that surrounding line does not begin with a mark, so the body is not
|
|
334
|
+
all-marked and parses normally. The guard is the same one
|
|
335
|
+
`_HUNK_HEADER`'s anchored `match` gives the hunk check: a mark has
|
|
336
|
+
to hold column one of *every* line to count, and a mention never
|
|
337
|
+
does.
|
|
338
|
+
|
|
339
|
+
Blank lines are ignored rather than counted against the fragment,
|
|
340
|
+
because a copied hunk keeps its blank lines unmarked.
|
|
341
|
+
"""
|
|
342
|
+
marked = [line for line in text.splitlines() if line.strip()]
|
|
343
|
+
if not marked:
|
|
344
|
+
return False
|
|
345
|
+
return all(line[0] in _CHANGE_MARKS for line in marked)
|
|
346
|
+
|
|
347
|
+
|
|
302
348
|
def _parses(path: str, text: str) -> bool:
|
|
303
349
|
"""Whether the content is what its extension claims to be.
|
|
304
350
|
|
|
@@ -309,7 +355,7 @@ def _parses(path: str, text: str) -> bool:
|
|
|
309
355
|
is absence read as a pass, arriving through the exact feature whose
|
|
310
356
|
docstring promises to refuse it (Gemini's field check).
|
|
311
357
|
"""
|
|
312
|
-
if _is_unified_diff(text):
|
|
358
|
+
if _is_unified_diff(text) or _is_diff_fragment(text):
|
|
313
359
|
return False
|
|
314
360
|
if Path(path).suffix != ".py":
|
|
315
361
|
# No parser for the brace languages, and zero declarations is not
|
{maintainability_agent-3.0.2 → maintainability_agent-3.1.0}/src/maintainability_audit/_practice.py
RENAMED
|
@@ -31,11 +31,13 @@ engineering practice made from the outside.
|
|
|
31
31
|
|
|
32
32
|
from __future__ import annotations
|
|
33
33
|
|
|
34
|
+
import os
|
|
34
35
|
import re
|
|
35
36
|
from dataclasses import dataclass, field
|
|
36
37
|
from pathlib import Path
|
|
37
38
|
from typing import Any
|
|
38
39
|
|
|
40
|
+
from ._operator_reads import open_regular_file
|
|
39
41
|
from .metrics import is_excluded
|
|
40
42
|
|
|
41
43
|
# A repository holding config but running none of it. See the module
|
|
@@ -139,10 +141,32 @@ class Practice:
|
|
|
139
141
|
|
|
140
142
|
|
|
141
143
|
def _read(path: Path, limit: int = 200_000) -> str:
|
|
144
|
+
"""A bounded read of a repository file, through a checked handle.
|
|
145
|
+
|
|
146
|
+
The head only, so this stays cheap on a large tree — but read
|
|
147
|
+
*through* a checked handle rather than by name, so a FIFO refuses
|
|
148
|
+
instead of stopping the audit dead (D145). `path.open()` on one waits
|
|
149
|
+
for a writer that never comes, and the previous `except OSError`
|
|
150
|
+
could not have caught that: there is no error to catch, only a
|
|
151
|
+
process that never returns.
|
|
152
|
+
|
|
153
|
+
Practice detection reads whatever the tree names as CI and toolchain
|
|
154
|
+
configuration, so every path here is repository-controlled.
|
|
155
|
+
"""
|
|
142
156
|
try:
|
|
143
|
-
|
|
157
|
+
handle = open_regular_file(
|
|
158
|
+
path,
|
|
159
|
+
"Practice detection reads CI and toolchain files from the "
|
|
160
|
+
"audited tree; a device, socket or FIFO would block the audit "
|
|
161
|
+
"rather than describe a practice.",
|
|
162
|
+
)
|
|
144
163
|
except OSError:
|
|
145
164
|
return ""
|
|
165
|
+
try:
|
|
166
|
+
with os.fdopen(handle, "r", encoding="utf-8", errors="replace", closefd=False) as opened:
|
|
167
|
+
return opened.read(limit)
|
|
168
|
+
finally:
|
|
169
|
+
os.close(handle)
|
|
146
170
|
|
|
147
171
|
|
|
148
172
|
def _ci_files(root: Path) -> list[Path]:
|
|
@@ -20,6 +20,7 @@ import re
|
|
|
20
20
|
from pathlib import Path
|
|
21
21
|
from typing import Any
|
|
22
22
|
|
|
23
|
+
from ._operator_reads import read_source_file
|
|
23
24
|
from ._runner import Invocation, locate, run
|
|
24
25
|
from .metrics import within
|
|
25
26
|
|
|
@@ -49,12 +50,21 @@ _QUOTED = re.compile(r"\"([A-Za-z_][\w-]*)\"")
|
|
|
49
50
|
|
|
50
51
|
|
|
51
52
|
def recorded_type_analysis(root: Path) -> dict[str, Any] | None:
|
|
52
|
-
"""The checked-in recording, when the repository keeps one.
|
|
53
|
+
"""The checked-in recording, when the repository keeps one.
|
|
54
|
+
|
|
55
|
+
`is_file()` used to stand in for both questions — does it exist, and
|
|
56
|
+
is it readable — and answered a FIFO with "absent" (D145). A
|
|
57
|
+
repository could then name `RECORDED_ANALYSIS` as a pipe and have
|
|
58
|
+
the tool report no recording rather than refuse one, which is the
|
|
59
|
+
same skip-and-pretend-absent shape D141 closed in the scan walk.
|
|
60
|
+
Existence is asked here; kind is asked by the primitive, which
|
|
61
|
+
refuses rather than waiting for a writer.
|
|
62
|
+
"""
|
|
53
63
|
path = root / RECORDED_ANALYSIS
|
|
54
|
-
if not path.
|
|
64
|
+
if not path.exists():
|
|
55
65
|
return None
|
|
56
66
|
try:
|
|
57
|
-
payload = json.loads(
|
|
67
|
+
payload = json.loads("\n".join(read_source_file(path)))
|
|
58
68
|
except ValueError:
|
|
59
69
|
return None
|
|
60
70
|
return payload if isinstance(payload, dict) and payload.get("tool") else None
|
|
@@ -226,7 +236,7 @@ def operation_sets(root: Path) -> list[dict[str, Any]]:
|
|
|
226
236
|
"""
|
|
227
237
|
found: list[dict[str, Any]] = []
|
|
228
238
|
for path in _typescript_sources(root):
|
|
229
|
-
text =
|
|
239
|
+
text = "\n".join(read_source_file(path))
|
|
230
240
|
dispatch = frozenset(
|
|
231
241
|
name for pair in _DISPATCH.findall(text) for name in pair if name
|
|
232
242
|
)
|
|
@@ -12,7 +12,7 @@ from typing import Any
|
|
|
12
12
|
|
|
13
13
|
from ._safe_write import write_bounded
|
|
14
14
|
from ._setup_errors import SetupRequired
|
|
15
|
-
from ._user_config import write_user_answers
|
|
15
|
+
from ._user_config import user_config_answers, write_user_answers
|
|
16
16
|
from .config import CONFIG_FILENAME, discovered_config, load_config
|
|
17
17
|
from .config import _configured as _read_config
|
|
18
18
|
|
|
@@ -75,6 +75,33 @@ def _is_bounds_only(answers: dict[str, Any]) -> bool:
|
|
|
75
75
|
return bool(given) and given <= names
|
|
76
76
|
|
|
77
77
|
|
|
78
|
+
def _user_tier_with(**changes: Any) -> dict[str, Any]:
|
|
79
|
+
"""The user's stored answers plus these, and nothing from the tree.
|
|
80
|
+
|
|
81
|
+
Both staged writers used to hand `write_user_answers` the
|
|
82
|
+
**repository's** document, because that was the dict they had built
|
|
83
|
+
for the repository write and it looked like "the config". So a
|
|
84
|
+
two-stage setup — answer the rates, or the test command — copied
|
|
85
|
+
whatever the audited tree had written under `analyzers.acquire_tools`,
|
|
86
|
+
`test_execution.requested` and `expected_commands.test` straight into
|
|
87
|
+
the user tier, which is the one tier `acquisition_permitted` and
|
|
88
|
+
`opted_in_command` trust (D147).
|
|
89
|
+
|
|
90
|
+
Four lines in a pull request, then any staged reply, and the tree had
|
|
91
|
+
granted itself `npx --yes` and a command to run. Neither check was
|
|
92
|
+
wrong: the tier they read had been told the tree's answers were the
|
|
93
|
+
user's.
|
|
94
|
+
|
|
95
|
+
So the user tier is now built **from the user tier**, the way
|
|
96
|
+
`_persist_answers` builds it from the setup payload. Merged rather
|
|
97
|
+
than replaced, for the same reason `_persist_answers` merges: a
|
|
98
|
+
stage-two reply must not reset the answers stage one wrote (D13).
|
|
99
|
+
"""
|
|
100
|
+
stored = dict(user_config_answers() or {})
|
|
101
|
+
stored.update(changes)
|
|
102
|
+
return stored
|
|
103
|
+
|
|
104
|
+
|
|
78
105
|
def _apply_bounds(root: Path, answers: dict[str, Any]) -> dict[str, Any]:
|
|
79
106
|
"""Fill the rates into a configuration that already has its answers.
|
|
80
107
|
|
|
@@ -91,7 +118,7 @@ def _apply_bounds(root: Path, answers: dict[str, Any]) -> dict[str, Any]:
|
|
|
91
118
|
Path(root), config_path,
|
|
92
119
|
json.dumps(stored, indent=2, sort_keys=True) + "\n",
|
|
93
120
|
)
|
|
94
|
-
write_user_answers(
|
|
121
|
+
write_user_answers(_user_tier_with(economic_context=economics))
|
|
95
122
|
return load_config(str(config_path))
|
|
96
123
|
|
|
97
124
|
|
|
@@ -114,14 +141,31 @@ def _apply_command(root: Path, answers: dict[str, Any]) -> dict[str, Any]:
|
|
|
114
141
|
commands = dict(stored.get("expected_commands") or {})
|
|
115
142
|
commands["test"] = shlex.split(command)
|
|
116
143
|
stored["expected_commands"] = commands
|
|
144
|
+
# The user tier carries the command as well as the request,
|
|
145
|
+
# because `opted_in_command` reads both from there: a person
|
|
146
|
+
# consents to the command they were shown, and leaving the
|
|
147
|
+
# command to the repository would keep the consent and hand back
|
|
148
|
+
# the choice of what it means (D147).
|
|
149
|
+
user_tier = _user_tier_with(
|
|
150
|
+
expected_commands={
|
|
151
|
+
**(user_config_answers() or {}).get("expected_commands", {}),
|
|
152
|
+
"test": shlex.split(command),
|
|
153
|
+
},
|
|
154
|
+
)
|
|
117
155
|
else:
|
|
118
156
|
stored["test_execution"] = {"requested": False}
|
|
157
|
+
user_tier = _user_tier_with(
|
|
158
|
+
test_execution={
|
|
159
|
+
**(user_config_answers() or {}).get("test_execution", {}),
|
|
160
|
+
"requested": False,
|
|
161
|
+
},
|
|
162
|
+
)
|
|
119
163
|
config_path = Path(root) / CONFIG_FILENAME
|
|
120
164
|
write_bounded(
|
|
121
165
|
Path(root), config_path,
|
|
122
166
|
json.dumps(stored, indent=2, sort_keys=True) + "\n",
|
|
123
167
|
)
|
|
124
|
-
write_user_answers(
|
|
168
|
+
write_user_answers(user_tier)
|
|
125
169
|
return load_config(str(config_path))
|
|
126
170
|
|
|
127
171
|
|