maintainability-agent 2.2.0__tar.gz → 2.4.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.2.0/src/maintainability_agent.egg-info → maintainability_agent-2.4.0}/PKG-INFO +8 -7
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/README.md +7 -6
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/pyproject.toml +1 -1
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0/src/maintainability_agent.egg-info}/PKG-INFO +8 -7
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_agent.egg-info/SOURCES.txt +4 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/__init__.py +1 -1
- maintainability_agent-2.4.0/src/maintainability_audit/_attestation.py +166 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_cognitive.py +21 -4
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_metrics_types.py +14 -0
- maintainability_agent-2.4.0/src/maintainability_audit/_ranges_swift.py +215 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/cli.py +12 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/config.py +1 -1
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/declarations.py +15 -2
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/prompts.py +1 -0
- maintainability_agent-2.4.0/tests/test_attestation.py +151 -0
- maintainability_agent-2.4.0/tests/test_swift_declarations.py +264 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/LICENSE +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/setup.cfg +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_agent.egg-info/dependency_links.txt +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_agent.egg-info/entry_points.txt +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_agent.egg-info/requires.txt +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_agent.egg-info/top_level.txt +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/__main__.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_adapters.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_analysis.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_aspects.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_assets/analyzer-catalog.json +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_assets/standard.md +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_backfill.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_bands.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_built_ins.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_calibration.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_catalog.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_charts.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_config_defaults.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_conformance.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_corroborate.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_coverage_notes.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_derive.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_discovery.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_documents.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_economics.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_economics_view.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_environment.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_evidence_view.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_finding_match.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_first_run.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_formula.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_gates.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_generic.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_grant_ledger.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_history_view.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_hostile_prompt.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_hotspots.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_html_report_sections.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_html_view.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_identity.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_jvm_adapters.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_markdown_sections.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_masking.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_mcp_audit.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_mcp_gate.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_mcp_grants.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_mcp_refusals.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_mcp_resources.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_mcp_setup.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_metric_adapters.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_pillars.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_practice.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_pressures.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_ranges_c.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_ranges_core.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_ranges_cpp.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_ranges_csharp.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_ranges_fortran.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_ranges_java.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_ranges_js.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_ratchet.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_recurrence.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_runner.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_safe_write.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_scan_history.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_scan_view.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_second_source.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_selection.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_semantic.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_semantic_policy.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_semantic_ts.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_semantic_view.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_setup_errors.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_setup_persist.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_skill_data/SKILL.md +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_skill_data/agents/anthropic.yaml +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_skill_data/agents/copilot.yaml +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_skill_data/agents/openai.yaml +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_skill_data/copilot/maintainability-agent.prompt.md +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_skill_data/references/finding-taxonomy.md +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_skill_install.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_stored_grants.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_tdd_view.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_test_execution.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_test_pairing.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_tokens.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_tool_adapters.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_trends.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_user_config.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_verdict_adapters.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_verification.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_work_order.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_work_order_view.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_work_order_weights.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_xml.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/baseline.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/deadcode.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/duplication.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/evidence.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/git_tools.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/history.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/idioms.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/instructions.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/mcp_server.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/metrics.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/renderers.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/report.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/sarif.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/scoring.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/similarity.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/source.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_acquisition_trust.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_adapter_docs.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_adapter_recordings.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_adapters.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_added_lines.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_analysis_coverage.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_analysis_execution.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_analyzer_bridge.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_analyzer_config_isolation.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_analyzer_estimate_claims.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_analyzer_pinning.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_analyzer_primary_estimate.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_analyzer_provenance.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_analyzer_provenance_exclusions.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_analyzer_scoring_path.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_analyzer_spread_range.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_analyzer_xml_bounds.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_annotations_match_behaviour.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_anticipated_refusals.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_architecture.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_artifact_write_route_class.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_aspect_coverage.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_asset_snapshot_class.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_ast_reading_resolves_spellings.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_audit_components.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_authorization_freshness.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_authorship_gates.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_backfill.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_band_concealment_class.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_band_concealment_p3.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_band_pressures.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_bands.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_body_or_signature_class.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_c_declarations.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_calibration_corpus.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_calibration_drift.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_calibration_tooling.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_catalog_selection.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_catalog_tier_promise.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_chat_primary_docs.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_checkstyle_adapter.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_checkstyle_catalog.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_ci_history_recipe.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_ci_installs_the_analyzer_pool.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_claimed_languages.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_cli.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_clone_group_class.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_cognitive_complexity.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_cohort_statistics.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_concealment_properties.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_config_shape.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_consent_and_grant.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_consumer_migration.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_contract_claims.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_corroborate.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_coverage_by_language.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_cpp_declarations.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_csharp_declarations.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_d15_composition.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_d15_goal_directed.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_dead_code.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_declaration_edge_cases.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_declaration_grading.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_declaration_ranges.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_declaration_source.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_declared_authorship.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_declared_imports.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_derivation_mix.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_determinism.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_dimension_ratchet.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_direct_runner_availability_class.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_discovery.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_doc_claims.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_docs_links.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_economic_context.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_empty_output_not_clean.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_empty_output_not_clean_adapters.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_environment_work_order.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_evidence_invariants.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_evidence_normalization.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_evidence_properties.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_exclusion_dialects.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_falsifier_standard.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_file_list_cap_class.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_finding_identity.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_first_run_elicitation.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_first_run_prompt.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_first_run_reelicitation.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_fix_breadth_window.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_fixed_form_fortran.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_format_ask.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_fortran_declarations.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_fortran_metrics.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_fortran_practice.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_git_argv.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_git_read_only.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_git_readonly_class.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_grant_only_user_tier.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_history.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_history_boundary.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_history_manifest.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_history_schema2.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_history_window.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_hostile_prompt.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_html_executive.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_html_section_parity_class.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_identity_docs.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_identity_resolution.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_idiom_divergence.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_installed_wheel_runtime.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_instruction_targets.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_java_range_guard.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_java_ranges.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_java_scored.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_java_wiring.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_js_complexity_operators.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_js_declarations.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_language_coverage.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_mcp_baseline_payload.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_mcp_history.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_mcp_server.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_migration_1_0.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_near_duplicates.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_network_disclosure.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_one_setup_across_surfaces_class.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_pattern_budget.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_phase6_claims.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_pillars_and_practice.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_platform_claim.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_pmd_adapter.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_pmd_audit_closures.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_pool_runs_by_config.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_population_floors.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_practice_nested_gates_class.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_presentation_refusals.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_promises.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_prompt_escalation_once.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_prompt_honesty.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_prompt_location_escalation_class.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_prove_falsifiers.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_python_declarations.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_read_boundary.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_readme_claims.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_recurrence.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_release_plan.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_release_plan_status.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_report_remedy_copy.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_roles_recorded.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_root_grants.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_rubric_ignores_project_lint_config.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_runner.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_sarif.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_scan_history.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_scan_scope.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_scanning.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_schema_fidelity.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_scope_conformance.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_scoring_calibration.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_semantic_policy.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_setup_gate_completeness.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_setup_gate_independent_contract.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_setup_precondition.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_skill_install.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_source_index.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_spotbugs_adapter.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_spotbugs_catalog.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_stage8_contract.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_tdd_structure.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_test_execution_class.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_three_presentations.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_trends.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_type_fields_not_functions_class.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_uat_falsifiers.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_unread_code.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_user_config_tier.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_verification_audit_closures.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_verified_grade.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_version_claim.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_wheel_contents.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_work_order.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_work_order_items.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_work_order_selection.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_workflow_supply_chain.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_write_boundary.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/tests/test_write_route_class.py +0 -0
- {maintainability_agent-2.2.0 → maintainability_agent-2.4.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.4.0
|
|
4
4
|
Summary: Maintainability gate and AI remediation prompt generator for CI.
|
|
5
5
|
Author: Marshall Guillory
|
|
6
6
|
License: MIT
|
|
@@ -44,10 +44,10 @@ 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.4.0**.
|
|
48
48
|
|
|
49
|
-
**Languages parsed:** Python, Java, C, C++, C#, Fortran (free-form
|
|
50
|
-
fixed-form), and the JS/TS family — each by a scanner written for it, and
|
|
49
|
+
**Languages parsed:** Python, Java, C, C++, C#, Swift, Fortran (free-form
|
|
50
|
+
*and* fixed-form), and the JS/TS family — each by a scanner written for it, and
|
|
51
51
|
measured with that language's own reading of what a branch is.
|
|
52
52
|
[What that means per language](#language-support).
|
|
53
53
|
|
|
@@ -252,9 +252,9 @@ SonarQube, Qlty) rather than replacing them — their SARIF folds in via
|
|
|
252
252
|
and on an unrecognized language it will quietly under-report rather than fail.**
|
|
253
253
|
Coverage comes from two layers.
|
|
254
254
|
|
|
255
|
-
|
|
256
|
-
C++ (1.2), C# (1.3), Fortran (free-form 1.4, fixed-form 1.6),
|
|
257
|
-
and HTML. Each has a scanner written for it and a documented list of what it
|
|
255
|
+
Nine languages are parsed as of **2.4.0**: Python (1.0), Java (1.0), C (1.1),
|
|
256
|
+
C++ (1.2), C# (1.3), Fortran (free-form 1.4, fixed-form 1.6), Swift (2.4), the
|
|
257
|
+
JS/TS family, and HTML. Each has a scanner written for it and a documented list of what it
|
|
258
258
|
misses — a language is claimed here only when both exist.
|
|
259
259
|
|
|
260
260
|
**Built-in scanner (always on, no dependencies)** — reads function/class
|
|
@@ -268,6 +268,7 @@ these:
|
|
|
268
268
|
| C (`.c`, `.h`) | dedicated brace-bounded scanner — functions, `struct`/`enum`/`union` | Bounded; prototypes and macros are not declarations |
|
|
269
269
|
| C++ (`.cpp`, `.hpp`, `.cc`, `.cxx`, `.hh`) | dedicated brace-bounded scanner — functions, class members, namespaces, templates | Bounded; bodyless declarations are not definitions |
|
|
270
270
|
| C# (`.cs`) | dedicated brace-bounded scanner — methods, constructors, `class`/`interface`/`struct`/`record`/`enum` | Bounded; properties are not declarations |
|
|
271
|
+
| Swift (`.swift`) | dedicated brace-bounded scanner — functions, initialisers, subscripts, `class`/`struct`/`enum`/`protocol`/`actor` | Bounded; extension members carry their type, protocol requirements and computed properties are not declarations |
|
|
271
272
|
| Fortran, free-form (`.f90`, `.f95`, `.f03`, `.f08`, `.F90`, `.F95`, `.F03`, `.F08`, `.pf`) | dedicated **keyword**-bounded scanner — modules, subroutines, functions, derived types | Bounded by `end`; measured with Fortran's own branch and nesting reading |
|
|
272
273
|
| Fortran, fixed-form (`.f`, `.for`, `.ftn`, `.F`, `.FOR`, `.FTN`) | the same scanner over card-column source; continuations joined, labelled `DO` loops understood | Bounded by `end` or by the loop's label |
|
|
273
274
|
| JS / TS / JSX / TSX (`.js`, `.jsx`, `.mjs`, `.cjs`, `.ts`, `.tsx`) | brace/paren depth over a masked copy | Bounded by the declaration's own braces |
|
|
@@ -5,10 +5,10 @@
|
|
|
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.4.0**.
|
|
9
9
|
|
|
10
|
-
**Languages parsed:** Python, Java, C, C++, C#, Fortran (free-form
|
|
11
|
-
fixed-form), and the JS/TS family — each by a scanner written for it, and
|
|
10
|
+
**Languages parsed:** Python, Java, C, C++, C#, Swift, Fortran (free-form
|
|
11
|
+
*and* fixed-form), and the JS/TS family — each by a scanner written for it, and
|
|
12
12
|
measured with that language's own reading of what a branch is.
|
|
13
13
|
[What that means per language](#language-support).
|
|
14
14
|
|
|
@@ -213,9 +213,9 @@ SonarQube, Qlty) rather than replacing them — their SARIF folds in via
|
|
|
213
213
|
and on an unrecognized language it will quietly under-report rather than fail.**
|
|
214
214
|
Coverage comes from two layers.
|
|
215
215
|
|
|
216
|
-
|
|
217
|
-
C++ (1.2), C# (1.3), Fortran (free-form 1.4, fixed-form 1.6),
|
|
218
|
-
and HTML. Each has a scanner written for it and a documented list of what it
|
|
216
|
+
Nine languages are parsed as of **2.4.0**: Python (1.0), Java (1.0), C (1.1),
|
|
217
|
+
C++ (1.2), C# (1.3), Fortran (free-form 1.4, fixed-form 1.6), Swift (2.4), the
|
|
218
|
+
JS/TS family, and HTML. Each has a scanner written for it and a documented list of what it
|
|
219
219
|
misses — a language is claimed here only when both exist.
|
|
220
220
|
|
|
221
221
|
**Built-in scanner (always on, no dependencies)** — reads function/class
|
|
@@ -229,6 +229,7 @@ these:
|
|
|
229
229
|
| C (`.c`, `.h`) | dedicated brace-bounded scanner — functions, `struct`/`enum`/`union` | Bounded; prototypes and macros are not declarations |
|
|
230
230
|
| C++ (`.cpp`, `.hpp`, `.cc`, `.cxx`, `.hh`) | dedicated brace-bounded scanner — functions, class members, namespaces, templates | Bounded; bodyless declarations are not definitions |
|
|
231
231
|
| C# (`.cs`) | dedicated brace-bounded scanner — methods, constructors, `class`/`interface`/`struct`/`record`/`enum` | Bounded; properties are not declarations |
|
|
232
|
+
| Swift (`.swift`) | dedicated brace-bounded scanner — functions, initialisers, subscripts, `class`/`struct`/`enum`/`protocol`/`actor` | Bounded; extension members carry their type, protocol requirements and computed properties are not declarations |
|
|
232
233
|
| Fortran, free-form (`.f90`, `.f95`, `.f03`, `.f08`, `.F90`, `.F95`, `.F03`, `.F08`, `.pf`) | dedicated **keyword**-bounded scanner — modules, subroutines, functions, derived types | Bounded by `end`; measured with Fortran's own branch and nesting reading |
|
|
233
234
|
| Fortran, fixed-form (`.f`, `.for`, `.ftn`, `.F`, `.FOR`, `.FTN`) | the same scanner over card-column source; continuations joined, labelled `DO` loops understood | Bounded by `end` or by the loop's label |
|
|
234
235
|
| JS / TS / JSX / TSX (`.js`, `.jsx`, `.mjs`, `.cjs`, `.ts`, `.tsx`) | brace/paren depth over a masked copy | Bounded by the declaration's own braces |
|
|
@@ -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.4.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.4.0
|
|
4
4
|
Summary: Maintainability gate and AI remediation prompt generator for CI.
|
|
5
5
|
Author: Marshall Guillory
|
|
6
6
|
License: MIT
|
|
@@ -44,10 +44,10 @@ 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.4.0**.
|
|
48
48
|
|
|
49
|
-
**Languages parsed:** Python, Java, C, C++, C#, Fortran (free-form
|
|
50
|
-
fixed-form), and the JS/TS family — each by a scanner written for it, and
|
|
49
|
+
**Languages parsed:** Python, Java, C, C++, C#, Swift, Fortran (free-form
|
|
50
|
+
*and* fixed-form), and the JS/TS family — each by a scanner written for it, and
|
|
51
51
|
measured with that language's own reading of what a branch is.
|
|
52
52
|
[What that means per language](#language-support).
|
|
53
53
|
|
|
@@ -252,9 +252,9 @@ SonarQube, Qlty) rather than replacing them — their SARIF folds in via
|
|
|
252
252
|
and on an unrecognized language it will quietly under-report rather than fail.**
|
|
253
253
|
Coverage comes from two layers.
|
|
254
254
|
|
|
255
|
-
|
|
256
|
-
C++ (1.2), C# (1.3), Fortran (free-form 1.4, fixed-form 1.6),
|
|
257
|
-
and HTML. Each has a scanner written for it and a documented list of what it
|
|
255
|
+
Nine languages are parsed as of **2.4.0**: Python (1.0), Java (1.0), C (1.1),
|
|
256
|
+
C++ (1.2), C# (1.3), Fortran (free-form 1.4, fixed-form 1.6), Swift (2.4), the
|
|
257
|
+
JS/TS family, and HTML. Each has a scanner written for it and a documented list of what it
|
|
258
258
|
misses — a language is claimed here only when both exist.
|
|
259
259
|
|
|
260
260
|
**Built-in scanner (always on, no dependencies)** — reads function/class
|
|
@@ -268,6 +268,7 @@ these:
|
|
|
268
268
|
| C (`.c`, `.h`) | dedicated brace-bounded scanner — functions, `struct`/`enum`/`union` | Bounded; prototypes and macros are not declarations |
|
|
269
269
|
| C++ (`.cpp`, `.hpp`, `.cc`, `.cxx`, `.hh`) | dedicated brace-bounded scanner — functions, class members, namespaces, templates | Bounded; bodyless declarations are not definitions |
|
|
270
270
|
| C# (`.cs`) | dedicated brace-bounded scanner — methods, constructors, `class`/`interface`/`struct`/`record`/`enum` | Bounded; properties are not declarations |
|
|
271
|
+
| Swift (`.swift`) | dedicated brace-bounded scanner — functions, initialisers, subscripts, `class`/`struct`/`enum`/`protocol`/`actor` | Bounded; extension members carry their type, protocol requirements and computed properties are not declarations |
|
|
271
272
|
| Fortran, free-form (`.f90`, `.f95`, `.f03`, `.f08`, `.F90`, `.F95`, `.F03`, `.F08`, `.pf`) | dedicated **keyword**-bounded scanner — modules, subroutines, functions, derived types | Bounded by `end`; measured with Fortran's own branch and nesting reading |
|
|
272
273
|
| Fortran, fixed-form (`.f`, `.for`, `.ftn`, `.F`, `.FOR`, `.FTN`) | the same scanner over card-column source; continuations joined, labelled `DO` loops understood | Bounded by `end` or by the loop's label |
|
|
273
274
|
| JS / TS / JSX / TSX (`.js`, `.jsx`, `.mjs`, `.cjs`, `.ts`, `.tsx`) | brace/paren depth over a masked copy | Bounded by the declaration's own braces |
|
|
@@ -12,6 +12,7 @@ src/maintainability_audit/__main__.py
|
|
|
12
12
|
src/maintainability_audit/_adapters.py
|
|
13
13
|
src/maintainability_audit/_analysis.py
|
|
14
14
|
src/maintainability_audit/_aspects.py
|
|
15
|
+
src/maintainability_audit/_attestation.py
|
|
15
16
|
src/maintainability_audit/_backfill.py
|
|
16
17
|
src/maintainability_audit/_bands.py
|
|
17
18
|
src/maintainability_audit/_built_ins.py
|
|
@@ -63,6 +64,7 @@ src/maintainability_audit/_ranges_csharp.py
|
|
|
63
64
|
src/maintainability_audit/_ranges_fortran.py
|
|
64
65
|
src/maintainability_audit/_ranges_java.py
|
|
65
66
|
src/maintainability_audit/_ranges_js.py
|
|
67
|
+
src/maintainability_audit/_ranges_swift.py
|
|
66
68
|
src/maintainability_audit/_ratchet.py
|
|
67
69
|
src/maintainability_audit/_recurrence.py
|
|
68
70
|
src/maintainability_audit/_runner.py
|
|
@@ -144,6 +146,7 @@ tests/test_artifact_write_route_class.py
|
|
|
144
146
|
tests/test_aspect_coverage.py
|
|
145
147
|
tests/test_asset_snapshot_class.py
|
|
146
148
|
tests/test_ast_reading_resolves_spellings.py
|
|
149
|
+
tests/test_attestation.py
|
|
147
150
|
tests/test_audit_components.py
|
|
148
151
|
tests/test_authorization_freshness.py
|
|
149
152
|
tests/test_authorship_gates.py
|
|
@@ -287,6 +290,7 @@ tests/test_source_index.py
|
|
|
287
290
|
tests/test_spotbugs_adapter.py
|
|
288
291
|
tests/test_spotbugs_catalog.py
|
|
289
292
|
tests/test_stage8_contract.py
|
|
293
|
+
tests/test_swift_declarations.py
|
|
290
294
|
tests/test_tdd_structure.py
|
|
291
295
|
tests/test_test_execution_class.py
|
|
292
296
|
tests/test_three_presentations.py
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""The record a generator cannot produce about its own output.
|
|
2
|
+
|
|
3
|
+
The three mechanisms — scope conformance, suppression detection, the
|
|
4
|
+
dimension ratchet — each answer one question about a change. This composes
|
|
5
|
+
them into the artifact they were built for: a per-change record of what was
|
|
6
|
+
measured, what the agent was told to change, what it actually changed, and
|
|
7
|
+
what moved.
|
|
8
|
+
|
|
9
|
+
**Why an independent process has to emit it.** A platform that generates
|
|
10
|
+
code and then reports on its quality is producing a self-assessment. That
|
|
11
|
+
is a property of the arrangement, not a criticism of any particular one,
|
|
12
|
+
and no amount of engineering removes it. The question a reviewer asks is
|
|
13
|
+
*who checked this, and can they re-derive the check months from now* — and
|
|
14
|
+
an author cannot be the answer. This tool writes nothing, runs no model,
|
|
15
|
+
and returns the same verdict from the same inputs, which is the only reason
|
|
16
|
+
its answer can be evidence rather than an opinion.
|
|
17
|
+
|
|
18
|
+
**It is reproducible, not signed.** Every field is derived from the report
|
|
19
|
+
and the pinned inputs, and the digest at the end is over the record's own
|
|
20
|
+
content, so two runs on the same commit with the same analyzer versions
|
|
21
|
+
produce the same digest. That makes it *verifiable* — anyone can re-run and
|
|
22
|
+
compare. It is deliberately not called a signature: nothing here holds a
|
|
23
|
+
key, and claiming a cryptographic guarantee this does not provide would be
|
|
24
|
+
exactly the kind of overclaim the rest of the project spends its time
|
|
25
|
+
removing. Signing the artifact, if a caller wants that, is a step they take
|
|
26
|
+
with their own key on a file whose bytes are stable.
|
|
27
|
+
|
|
28
|
+
**What it does not establish.** The record says which files the work order
|
|
29
|
+
named and whether a checker was switched off — not whether the edits were
|
|
30
|
+
correct. It never opens the diff to judge it, and it never touches a score.
|
|
31
|
+
Review is still review.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
import hashlib
|
|
37
|
+
import json
|
|
38
|
+
from typing import Any
|
|
39
|
+
|
|
40
|
+
ARTIFACT_VERSION = 1
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _verdicts(report: dict[str, Any]) -> list[str]:
|
|
44
|
+
"""One line per question that was actually asked."""
|
|
45
|
+
lines: list[str] = []
|
|
46
|
+
conformance = report.get("scope_conformance")
|
|
47
|
+
if conformance:
|
|
48
|
+
out = len(conformance["out_of_scope"])
|
|
49
|
+
silenced = conformance["suppressions_on_named_paths"]
|
|
50
|
+
lines.append(
|
|
51
|
+
f"- **Stayed inside the work order:** "
|
|
52
|
+
f"{'yes' if conformance['conformant'] else f'no — {out} file(s) outside it'}"
|
|
53
|
+
)
|
|
54
|
+
lines.append(
|
|
55
|
+
f"- **Without silencing a finding:** "
|
|
56
|
+
f"{'yes' if not silenced else f'no — {silenced} suppression(s) on flagged files'}"
|
|
57
|
+
)
|
|
58
|
+
ratchet = report.get("dimension_ratchet")
|
|
59
|
+
if ratchet:
|
|
60
|
+
if not ratchet["comparable"]:
|
|
61
|
+
lines.append(
|
|
62
|
+
f"- **No dimension regressed:** not established — {ratchet['reason']}"
|
|
63
|
+
)
|
|
64
|
+
elif ratchet["regressed"]:
|
|
65
|
+
names = ", ".join(item["name"] for item in ratchet["regressed"])
|
|
66
|
+
lines.append(f"- **No dimension regressed:** no — {names}")
|
|
67
|
+
else:
|
|
68
|
+
lines.append("- **No dimension regressed:** yes")
|
|
69
|
+
return lines
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _measured(report: dict[str, Any]) -> list[str]:
|
|
73
|
+
score = report.get("score") or {}
|
|
74
|
+
coverage = report.get("analyzer_coverage") or {}
|
|
75
|
+
status = score.get("evidence_status")
|
|
76
|
+
if isinstance(status, dict):
|
|
77
|
+
status = status.get("status")
|
|
78
|
+
ran = (coverage.get("by_outcome") or {}).get("ran") or []
|
|
79
|
+
return [
|
|
80
|
+
f"- Estimate: {score.get('maintainability_estimate', 'withheld')}",
|
|
81
|
+
f"- Verified grade: {score.get('verified_grade') or 'withheld'}",
|
|
82
|
+
f"- Evidence: {status or 'unstated'}",
|
|
83
|
+
f"- Analyzers that ran: {len(ran)}",
|
|
84
|
+
f"- Hard gate failures: {len(report.get('hard_gate_failures') or [])}",
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def attestation_record(report: dict[str, Any]) -> dict[str, Any]:
|
|
89
|
+
"""The structured record, before rendering."""
|
|
90
|
+
conformance = report.get("scope_conformance") or {}
|
|
91
|
+
ratchet = report.get("dimension_ratchet") or {}
|
|
92
|
+
score = report.get("score") or {}
|
|
93
|
+
record = {
|
|
94
|
+
"artifact_version": ARTIFACT_VERSION,
|
|
95
|
+
"tool_version": report.get("tool_version"),
|
|
96
|
+
"commit": report.get("git_commit"),
|
|
97
|
+
"branch": report.get("git_branch"),
|
|
98
|
+
# A finding reported against "the commit" that actually depends on
|
|
99
|
+
# uncommitted work is unreproducible, so the state of the tree is
|
|
100
|
+
# part of what is attested rather than a footnote.
|
|
101
|
+
"tree_dirty": bool(report.get("git_status_short")),
|
|
102
|
+
"revspec": conformance.get("revspec"),
|
|
103
|
+
"estimate": score.get("maintainability_estimate"),
|
|
104
|
+
"verified_grade": score.get("verified_grade"),
|
|
105
|
+
"stayed_in_scope": conformance.get("conformant"),
|
|
106
|
+
"no_finding_silenced": (
|
|
107
|
+
None if not conformance else conformance["suppressions_on_named_paths"] == 0
|
|
108
|
+
),
|
|
109
|
+
"no_dimension_regressed": (
|
|
110
|
+
None if not ratchet or not ratchet.get("comparable")
|
|
111
|
+
else not ratchet["regressed"]
|
|
112
|
+
),
|
|
113
|
+
"scope_conformance": conformance or None,
|
|
114
|
+
"dimension_ratchet": ratchet or None,
|
|
115
|
+
}
|
|
116
|
+
# The digest is over the record without it, so it is reproducible: the
|
|
117
|
+
# same commit measured with the same analyzer versions yields the same
|
|
118
|
+
# value, and anyone can recompute it rather than trust it.
|
|
119
|
+
record["digest"] = hashlib.sha256(
|
|
120
|
+
json.dumps(record, sort_keys=True, default=str).encode("utf-8")
|
|
121
|
+
).hexdigest()[:16]
|
|
122
|
+
return record
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def render_attestation(report: dict[str, Any]) -> str:
|
|
126
|
+
"""The record as text, for a reviewer or an audit trail."""
|
|
127
|
+
record = attestation_record(report)
|
|
128
|
+
dirty = " (uncommitted changes present)" if record["tree_dirty"] else ""
|
|
129
|
+
lines = [
|
|
130
|
+
"# Maintainability attestation",
|
|
131
|
+
"",
|
|
132
|
+
"An independent, deterministic check on a change. Produced by a "
|
|
133
|
+
"process that generated none of the code it examined.",
|
|
134
|
+
"",
|
|
135
|
+
f"- Commit: `{record['commit'] or 'unknown'}`{dirty}",
|
|
136
|
+
f"- Branch: `{record['branch'] or 'unknown'}`",
|
|
137
|
+
f"- Tool version: {record['tool_version'] or 'unknown'}",
|
|
138
|
+
f"- Compared: `{record['revspec'] or 'not asked'}`",
|
|
139
|
+
"",
|
|
140
|
+
"## What was measured",
|
|
141
|
+
"",
|
|
142
|
+
*_measured(report),
|
|
143
|
+
"",
|
|
144
|
+
"## What the change did",
|
|
145
|
+
"",
|
|
146
|
+
]
|
|
147
|
+
verdicts = _verdicts(report)
|
|
148
|
+
lines += verdicts or ["- Nothing was asked: run with `--conformance` and "
|
|
149
|
+
"`--fail-on-regression` to populate this section."]
|
|
150
|
+
lines += [
|
|
151
|
+
"",
|
|
152
|
+
"## What this does not establish",
|
|
153
|
+
"",
|
|
154
|
+
"- It compares paths and reads added lines. A file in scope means the "
|
|
155
|
+
"work order named it, **not** that the change to it was correct — this "
|
|
156
|
+
"never opens the diff to judge it.",
|
|
157
|
+
"- It touches no score. Whether a change obeyed its instructions is a "
|
|
158
|
+
"fact about behaviour, not evidence about the code's condition.",
|
|
159
|
+
"- It is reproducible, **not signed**. The digest is over this "
|
|
160
|
+
"record's own content, so the same inputs reproduce it and anyone can "
|
|
161
|
+
"recompute it. Nothing here holds a key.",
|
|
162
|
+
"",
|
|
163
|
+
f"Digest: `{record['digest']}`",
|
|
164
|
+
"",
|
|
165
|
+
]
|
|
166
|
+
return "\n".join(lines)
|
{maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/_cognitive.py
RENAMED
|
@@ -45,6 +45,14 @@ _NESTING_NODES = (ast.If, ast.For, ast.AsyncFor, ast.While, ast.ExceptHandler, a
|
|
|
45
45
|
_NESTING_ONLY = (ast.FunctionDef, ast.AsyncFunctionDef, ast.Lambda)
|
|
46
46
|
|
|
47
47
|
_CONTROL_RE = re.compile(r"\b(if|for|while|catch|switch|case|elif|except)\b")
|
|
48
|
+
#: Swift adds `guard` — an early exit that is its primary branching idiom —
|
|
49
|
+
#: and `repeat`, its do-while. Without them a function built from guards
|
|
50
|
+
#: reads as having no branches at all, which is the Fortran `do` defect in
|
|
51
|
+
#: another language: six nested loops once scored complexity 1 because the
|
|
52
|
+
#: pattern did not know the keyword.
|
|
53
|
+
_SWIFT_CONTROL_RE = re.compile(
|
|
54
|
+
r"\b(if|for|while|catch|switch|case|guard|repeat)\b"
|
|
55
|
+
)
|
|
48
56
|
_ELSE_RE = re.compile(r"\belse\b")
|
|
49
57
|
_BOOLEAN_RE = re.compile(r"&&|\|\|")
|
|
50
58
|
|
|
@@ -167,7 +175,7 @@ def fortran_cognitive(lines: list[str]) -> int:
|
|
|
167
175
|
return score
|
|
168
176
|
|
|
169
177
|
|
|
170
|
-
def brace_cognitive(lines: list[str]) -> int:
|
|
178
|
+
def brace_cognitive(lines: list[str], control: re.Pattern[str] | None = None) -> int:
|
|
171
179
|
"""Approximate cognitive complexity for a C-family declaration body.
|
|
172
180
|
|
|
173
181
|
Nesting is read from brace depth relative to the declaration's own
|
|
@@ -191,16 +199,25 @@ def brace_cognitive(lines: list[str]) -> int:
|
|
|
191
199
|
if base is None and opens:
|
|
192
200
|
base = depth
|
|
193
201
|
nesting = max(0, depth - (base if base is not None else 0))
|
|
194
|
-
score += _score_brace_line(line, nesting)
|
|
202
|
+
score += _score_brace_line(line, nesting, control or _CONTROL_RE)
|
|
195
203
|
depth = max(0, depth + opens - closes)
|
|
196
204
|
return score
|
|
197
205
|
|
|
198
206
|
|
|
199
|
-
def _score_brace_line(line: str, nesting: int) -> int:
|
|
207
|
+
def _score_brace_line(line: str, nesting: int, control: re.Pattern[str]) -> int:
|
|
200
208
|
score = 0
|
|
201
|
-
for _ in
|
|
209
|
+
for _ in control.finditer(line):
|
|
202
210
|
score += 1 + nesting
|
|
203
211
|
if _ELSE_RE.search(line):
|
|
204
212
|
score += 1
|
|
205
213
|
score += len(_BOOLEAN_RE.findall(line))
|
|
206
214
|
return score
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def swift_cognitive(lines: list[str]) -> int:
|
|
218
|
+
"""`brace_cognitive` reading Swift's control keywords.
|
|
219
|
+
|
|
220
|
+
Swift is braced, so nesting is read the same way; only the vocabulary
|
|
221
|
+
differs, and `guard` is the difference that matters.
|
|
222
|
+
"""
|
|
223
|
+
return brace_cognitive(lines, _SWIFT_CONTROL_RE)
|
|
@@ -53,6 +53,20 @@ COMPLEXITY_RE = re.compile(
|
|
|
53
53
|
)
|
|
54
54
|
|
|
55
55
|
|
|
56
|
+
#: Swift's `guard` is an early exit and its primary branching idiom, and
|
|
57
|
+
#: `repeat` is its do-while. Neither is in the C-family pattern, so a
|
|
58
|
+
#: guard-heavy function would read as branchless — the same defect Fortran
|
|
59
|
+
#: had with `do`.
|
|
60
|
+
SWIFT_COMPLEXITY_RE = re.compile(
|
|
61
|
+
r"\b(if|for|while|case|catch|guard|repeat)\b|&&|\|\||\?\?|\?(?![.?])"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def swift_branch_points(line: str) -> int:
|
|
66
|
+
"""Decision points on one line of Swift."""
|
|
67
|
+
return len(SWIFT_COMPLEXITY_RE.findall(line))
|
|
68
|
+
|
|
69
|
+
|
|
56
70
|
def branch_points(line: str) -> int:
|
|
57
71
|
"""Decision points on one line, for the C family and Python."""
|
|
58
72
|
return len(COMPLEXITY_RE.findall(line))
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""Declaration ranges for Swift.
|
|
2
|
+
|
|
3
|
+
Braced, so it reuses `scan_bounded` with C, C++, C# and Java. What is
|
|
4
|
+
different is worth stating, because two of the differences are the whole
|
|
5
|
+
job and one of them is a trap this project has already fallen into once.
|
|
6
|
+
|
|
7
|
+
**Every declaration is keyword-led.** `func`, `init`, `subscript`, `class`,
|
|
8
|
+
`struct`, `enum`, `protocol`, `actor`, `extension`. That makes recognition
|
|
9
|
+
*easier* than C++, where a bare `name(` is equally a call, a macro and a
|
|
10
|
+
constructor, and where a wrong guess let a macro swallow the next
|
|
11
|
+
declaration's braces. Nothing here has to guess.
|
|
12
|
+
|
|
13
|
+
**An `extension` adds members to a type declared elsewhere**, often in
|
|
14
|
+
another file. `extension Widget { func draw() }` must report `Widget.draw`
|
|
15
|
+
rather than a second bare `draw` indistinguishable from every other type's,
|
|
16
|
+
which is the same rule C++ applies to an out-of-line `void geo::Widget::draw()`.
|
|
17
|
+
The extension itself is walked into and not graded: it is a container, and
|
|
18
|
+
grading it would count its members twice.
|
|
19
|
+
|
|
20
|
+
**Computed properties are not declarations**, and this is the C# properties
|
|
21
|
+
problem in Swift spelling. `var area: Double { w * h }` has braces and would
|
|
22
|
+
bound perfectly cleanly; an ordinary Swift type has many, each a line or
|
|
23
|
+
two, none anybody's maintenance burden. Counting them would dilute the
|
|
24
|
+
population every declaration rate divides by. They are excluded by
|
|
25
|
+
construction rather than by a rule: every pattern here requires either a
|
|
26
|
+
parameter list or a type keyword, and a property has neither.
|
|
27
|
+
|
|
28
|
+
**A protocol requirement has no body** — `func draw()` with nothing after
|
|
29
|
+
it — so it mints nothing, exactly as a C prototype and a pure virtual do.
|
|
30
|
+
A protocol of forty requirements is not forty declarations.
|
|
31
|
+
|
|
32
|
+
One claim was corrected while writing this. The roadmap said Swift's string
|
|
33
|
+
interpolation "puts braces inside literals, so masking has to run before
|
|
34
|
+
depth counting". It does not: Swift interpolates with `\\(expr)` —
|
|
35
|
+
parentheses, not braces — and string contents are masked before any
|
|
36
|
+
counting regardless. The real reason masking matters here is the ordinary
|
|
37
|
+
one every language shares: a brace inside a string literal, `"{}"`, would
|
|
38
|
+
desync depth.
|
|
39
|
+
"""
|
|
40
|
+
from __future__ import annotations
|
|
41
|
+
|
|
42
|
+
import re
|
|
43
|
+
|
|
44
|
+
from ._metrics_types import DeclRange
|
|
45
|
+
from ._ranges_core import _mask_generics, scan_bounded
|
|
46
|
+
|
|
47
|
+
# Stripped once per line so the patterns stay small: these lead a
|
|
48
|
+
# declaration and say nothing about what it is.
|
|
49
|
+
_SW_MODIFIER_RE = re.compile(
|
|
50
|
+
r"^\s*(?:(?:public|private|internal|fileprivate|open|static|final"
|
|
51
|
+
r"|override|mutating|nonmutating|convenience|required|lazy|weak|unowned"
|
|
52
|
+
r"|indirect|dynamic|optional|async|throws|rethrows|infix|prefix|postfix"
|
|
53
|
+
r"|package)\s+)*"
|
|
54
|
+
)
|
|
55
|
+
# `class` is both a declaration keyword and a modifier: `class Widget` is a
|
|
56
|
+
# type, `class func make()` is a type-level method. Stripping it with the
|
|
57
|
+
# other modifiers cost every `final class Store` its keyword — the type
|
|
58
|
+
# vanished and only its members were reported. So it is removed only where
|
|
59
|
+
# a declaration keyword follows it, and left alone otherwise.
|
|
60
|
+
_SW_CLASS_MODIFIER_RE = re.compile(r"^class\s+(?=(?:func|var|let|subscript)\b)")
|
|
61
|
+
# `@objc`, `@available(iOS 13, *)`, `@MainActor` — an attribute leads a
|
|
62
|
+
# declaration and is not one. Stripped, so what follows is still read.
|
|
63
|
+
_SW_ATTRIBUTE_RE = re.compile(r"^\s*(?:@[A-Za-z_]\w*(?:\([^)]*\))?\s*)+")
|
|
64
|
+
# `extension Widget`, `extension Widget: Drawable`, `extension Array where …`
|
|
65
|
+
_SW_EXTENSION_RE = re.compile(r"^extension\s+([A-Za-z_][\w.]*)")
|
|
66
|
+
# The container keywords. `protocol` is here so its body is walked into —
|
|
67
|
+
# its requirements are then skipped for having no body, which is what makes
|
|
68
|
+
# a forty-requirement protocol mint nothing.
|
|
69
|
+
_SW_TYPE_RE = re.compile(
|
|
70
|
+
r"^(?:class|struct|enum|protocol|actor)\s+([A-Za-z_]\w*)"
|
|
71
|
+
)
|
|
72
|
+
# `func name(`, and the three declarations that carry no `func` keyword.
|
|
73
|
+
_SW_FUNC_RE = re.compile(r"^func\s+([A-Za-z_]\w*|[^\s(]+)\s*(?:<[^>]*>)?\s*\(")
|
|
74
|
+
_SW_INIT_RE = re.compile(r"^(init\??|deinit|subscript)\b")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _swift_declaration(text: str) -> tuple[str, str | None] | None:
|
|
78
|
+
"""``(name, kind)`` for a Swift declaration on one masked line.
|
|
79
|
+
|
|
80
|
+
``kind`` is ``None`` for an `extension`: walk in, grade nothing.
|
|
81
|
+
"""
|
|
82
|
+
line = _SW_ATTRIBUTE_RE.sub("", _mask_generics(text), count=1)
|
|
83
|
+
body = line[_SW_MODIFIER_RE.match(line).end():]
|
|
84
|
+
body = _SW_CLASS_MODIFIER_RE.sub("", body, count=1)
|
|
85
|
+
body = body[_SW_MODIFIER_RE.match(body).end():]
|
|
86
|
+
|
|
87
|
+
extension = _SW_EXTENSION_RE.match(body)
|
|
88
|
+
if extension is not None:
|
|
89
|
+
# The extended type's name, carried so its members can be reported
|
|
90
|
+
# as `Widget.draw` rather than as a second bare `draw`.
|
|
91
|
+
return extension.group(1), None
|
|
92
|
+
|
|
93
|
+
type_match = _SW_TYPE_RE.match(body)
|
|
94
|
+
if type_match is not None:
|
|
95
|
+
return type_match.group(1), "class"
|
|
96
|
+
|
|
97
|
+
func = _SW_FUNC_RE.match(body)
|
|
98
|
+
if func is not None:
|
|
99
|
+
return (func.group(1), "function") if _opens_a_body(body) else None
|
|
100
|
+
|
|
101
|
+
special = _SW_INIT_RE.match(body)
|
|
102
|
+
if special is not None:
|
|
103
|
+
return (special.group(1).rstrip("?"), "function") if _opens_a_body(body) else None
|
|
104
|
+
|
|
105
|
+
# Anything else — `var`, `let`, a computed property, a `case` — is not
|
|
106
|
+
# a declaration this tool measures. Falling through rather than listing
|
|
107
|
+
# exclusions keeps the rule "a declaration is keyword-led and named".
|
|
108
|
+
return None
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _opens_a_body(body: str) -> bool:
|
|
112
|
+
"""Whether this line starts a body, or is a requirement with none.
|
|
113
|
+
|
|
114
|
+
Swift has no statement terminator, so the shared `skip_bare` check
|
|
115
|
+
cannot work here: it looks for a signature that closes without opening
|
|
116
|
+
a brace, and in C or C# that is a `;`. A Swift protocol requirement
|
|
117
|
+
simply ends at the newline, so the end-finder walks on and adopts the
|
|
118
|
+
*next* declaration's brace — which is how `func describe() -> String`
|
|
119
|
+
inside a protocol came back measured as two lines of body.
|
|
120
|
+
|
|
121
|
+
The rule that replaces it: a signature whose parentheses balance on
|
|
122
|
+
this line and which then shows no `{` has no body. A wrapped signature
|
|
123
|
+
leaves its parentheses unbalanced, so it is still read as a
|
|
124
|
+
declaration and its body is found normally:
|
|
125
|
+
|
|
126
|
+
func draw( <- unbalanced here, kept
|
|
127
|
+
into: Canvas
|
|
128
|
+
) throws {
|
|
129
|
+
|
|
130
|
+
A body written Allman — `func f()` with `{` alone on the next line —
|
|
131
|
+
is missed and mints nothing. That is legal Swift and vanishingly rare
|
|
132
|
+
in it, and missing one declaration is the cheaper error than counting
|
|
133
|
+
every protocol requirement in the tree.
|
|
134
|
+
"""
|
|
135
|
+
if "{" in body:
|
|
136
|
+
return True
|
|
137
|
+
return body.count("(") != body.count(")")
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def swift_declaration_ranges(lines: list[str]) -> tuple[list[DeclRange], list[str]]:
|
|
141
|
+
"""Functions, initialisers and types, each bounded by its own body.
|
|
142
|
+
|
|
143
|
+
Types and extensions are descended into, because that is where members
|
|
144
|
+
live; a function body is stepped over, so nothing inside one is read as
|
|
145
|
+
a declaration.
|
|
146
|
+
|
|
147
|
+
Bodyless declarations mint nothing, as in C, C++ and C#: a protocol
|
|
148
|
+
requirement is a signature with nothing to measure, and grading it
|
|
149
|
+
would put one-line members into the population every rate divides by.
|
|
150
|
+
|
|
151
|
+
Everything it misses, it misses in the safe direction:
|
|
152
|
+
|
|
153
|
+
- **Computed properties** (`var area: Double { w * h }`) are not
|
|
154
|
+
declarations, the same call C# makes for the same reason.
|
|
155
|
+
- **A closure assigned to a property** is a body without a declaration
|
|
156
|
+
keyword and is not counted.
|
|
157
|
+
- **Declarations produced by a macro** (`@freestanding`, `#`-macros)
|
|
158
|
+
are not in the source and are not seen, as in C and C++.
|
|
159
|
+
- **Conditional compilation is not evaluated**, so a declaration in a
|
|
160
|
+
disabled `#if` arm still counts.
|
|
161
|
+
"""
|
|
162
|
+
ranges, masked = scan_bounded(
|
|
163
|
+
lines, _swift_declaration, descend=("class",), skip_preprocessor=True
|
|
164
|
+
)
|
|
165
|
+
return _qualify_extension_members(ranges, masked), masked
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _extension_spans(masked: list[str]) -> list[tuple[int, int, str]]:
|
|
169
|
+
"""Every `extension X { … }` as (start, end, extended type)."""
|
|
170
|
+
def only_extensions(text: str) -> tuple[str, str | None] | None:
|
|
171
|
+
found = _swift_declaration(text)
|
|
172
|
+
if found is None:
|
|
173
|
+
return None
|
|
174
|
+
name, kind = found
|
|
175
|
+
return (name, "class") if kind is None else None
|
|
176
|
+
|
|
177
|
+
spans, _ = scan_bounded(
|
|
178
|
+
masked, only_extensions, descend=(), skip_preprocessor=True
|
|
179
|
+
)
|
|
180
|
+
return [(item.start, item.end, item.name) for item in spans]
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _qualify_extension_members(
|
|
184
|
+
ranges: list[DeclRange], masked: list[str]
|
|
185
|
+
) -> list[DeclRange]:
|
|
186
|
+
"""Report an extension's members under the type they extend.
|
|
187
|
+
|
|
188
|
+
`extension Widget { func draw() }` becomes `Widget.draw`. Without it
|
|
189
|
+
the report carries a bare `draw` indistinguishable from every other
|
|
190
|
+
type's `draw`, and a reader given "shorten `draw`" cannot find which
|
|
191
|
+
one — the same reason C++ keeps the qualification on an out-of-line
|
|
192
|
+
`void geo::Widget::draw()`, except that in Swift the source does not
|
|
193
|
+
write it and the scanner has to carry it.
|
|
194
|
+
|
|
195
|
+
A member already qualified is left alone, and a nested type inside an
|
|
196
|
+
extension keeps its own name: the prefix answers "which type does this
|
|
197
|
+
belong to", which a named type already answers for itself.
|
|
198
|
+
"""
|
|
199
|
+
spans = _extension_spans(masked)
|
|
200
|
+
if not spans:
|
|
201
|
+
return ranges
|
|
202
|
+
qualified = []
|
|
203
|
+
for item in ranges:
|
|
204
|
+
owner = next(
|
|
205
|
+
(name for start, end, name in spans
|
|
206
|
+
if start < item.start <= end and item.kind == "function"),
|
|
207
|
+
None,
|
|
208
|
+
)
|
|
209
|
+
if owner is None or "." in item.name:
|
|
210
|
+
qualified.append(item)
|
|
211
|
+
continue
|
|
212
|
+
qualified.append(
|
|
213
|
+
DeclRange(item.start, item.end, f"{owner}.{item.name}", item.kind)
|
|
214
|
+
)
|
|
215
|
+
return qualified
|
{maintainability_agent-2.2.0 → maintainability_agent-2.4.0}/src/maintainability_audit/cli.py
RENAMED
|
@@ -97,6 +97,14 @@ def add_arguments(parser: argparse.ArgumentParser) -> None:
|
|
|
97
97
|
parser.add_argument("--prompt-output", help="Optional Markdown prompt for AI-assisted remediation.")
|
|
98
98
|
parser.add_argument("--comment-output", help="Optional Markdown body suitable for a PR comment.")
|
|
99
99
|
parser.add_argument("--agent-instructions-output", help="Optional reusable instructions for AI coding agents.")
|
|
100
|
+
parser.add_argument(
|
|
101
|
+
"--attestation-output",
|
|
102
|
+
help="Write the attestation: an independent, reproducible record of "
|
|
103
|
+
"what was measured, what the change was told to do, what it did, "
|
|
104
|
+
"and what moved. Populated by --conformance and "
|
|
105
|
+
"--fail-on-regression; a generator cannot produce this about its "
|
|
106
|
+
"own output.",
|
|
107
|
+
)
|
|
100
108
|
parser.add_argument("--hostile-prompt-output",
|
|
101
109
|
help="Optional adversarial audit brief seeded from this run (ADR 013). "
|
|
102
110
|
"Text only: it does not gate, score, or send anything.")
|
|
@@ -227,6 +235,10 @@ def write_outputs(args: argparse.Namespace, report: dict, rendered: str) -> None
|
|
|
227
235
|
write_artifact(root, Path(args.comment_output), render_pr_comment(report) + "\n")
|
|
228
236
|
if args.agent_instructions_output:
|
|
229
237
|
write_artifact(root, Path(args.agent_instructions_output), render_agent_instructions(report) + "\n")
|
|
238
|
+
if args.attestation_output:
|
|
239
|
+
from ._attestation import render_attestation
|
|
240
|
+
|
|
241
|
+
write_artifact(root, Path(args.attestation_output), render_attestation(report))
|
|
230
242
|
if args.hostile_prompt_output:
|
|
231
243
|
write_artifact(root, Path(args.hostile_prompt_output),
|
|
232
244
|
render_hostile_audit_prompt(report) + "\n")
|