code-mower 0.5.0b47__tar.gz → 0.5.0b49__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.
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/PKG-INFO +6 -6
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/README.md +5 -5
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/pyproject.toml +1 -1
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/__init__.py +1 -1
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/audit_labeler_lib.py +190 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/calibration/__init__.py +2 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/calibration/effect_report.py +10 -2
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/calibration/evidence_report.py +5 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/calibration/policy.py +2 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/calibration/run_status.py +4 -1
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/calibration/value_report.py +7 -5
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/claude_audit_pr.py +23 -2
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/cli.py +1 -1
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/code_mower_calibration.py +5 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/codex_audit_pr.py +25 -2
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/gate_health.py +2 -1
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/reviewer_metrics.py +3 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/reviewer-value-report.example.md +3 -3
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/workflows/builder-provenance.yml.j2 +1 -1
- {code_mower-0.5.0b47 → code_mower-0.5.0b49/src/code_mower}/templates/workflows/code-mower-gate.yml.j2 +310 -35
- {code_mower-0.5.0b47 → code_mower-0.5.0b49/src/code_mower}/templates/workflows/self-hosted-local-audit.yml.j2 +43 -56
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/trailer_comment_labeler.py +105 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower.egg-info/PKG-INFO +6 -6
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/reviewer-value-report.example.md +3 -3
- {code_mower-0.5.0b47/src/code_mower → code_mower-0.5.0b49}/templates/workflows/code-mower-gate.yml.j2 +310 -35
- {code_mower-0.5.0b47/src/code_mower → code_mower-0.5.0b49}/templates/workflows/self-hosted-local-audit.yml.j2 +43 -56
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_calibration_effect_report.py +33 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_calibration_truth.py +41 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_claude_audit_pr.py +18 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_codex_audit_pr.py +18 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_gate_health.py +16 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_release_hygiene.py +315 -38
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_reviewer_metrics_core.py +4 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_trailer_comment_labeler.py +119 -1
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/CODE_OF_CONDUCT.md +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/LICENSE +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/MANIFEST.in +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/NOTICE +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/SUPPORT.md +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/requirements/requirements.txt +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/setup.cfg +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/adapters/__init__.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/adapters/_base.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/adapters/cursor_bugbot.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/adapters/gitar.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/adapters/greptile.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/adapters/qodo.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/antigravity_cli_audit_pr.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/audit_handoff_log.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/audit_progress.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/blind_review_artifacts.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/blind_review_coordinator.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/bootstrap.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/builder_experiment.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/builder_runs.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/calibration/arms.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/calibration/auto_discovery.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/calibration/commands.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/calibration/context_inputs.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/calibration/corpus.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/calibration/evidence.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/calibration/identity.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/calibration/metrics.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/calibration/overlap.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/calibration/planning.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/calibration/results.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/calibration/run_results.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/calibration/runner.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/calibration/truth.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/checks.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/claude_cli_bounce.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/claude_cli_environment.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/clear_stale.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/cloud.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/cloud_client/__init__.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/cloud_client/bundle.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/cloud_client/doctor.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/cloud_client/dogfood.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/cloud_client/endpoints.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/cloud_client/errors.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/cloud_client/events.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/cloud_client/export.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/cloud_client/git_metadata.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/cloud_client/manifest.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/cloud_client/operations.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/cloud_client/reports.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/cloud_client/setup.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/cloud_client/upload.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/code_mower_context_packs.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/code_mower_merge.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/code_mower_telemetry.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/coderabbit_cli_audit_pr.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/codex_audit_env_preflight.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/codex_audit_schema_smoke.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/codex_audit_verdict.schema.json +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/config.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/__init__.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/cloud.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/common.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/github.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/github_actions.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/github_actions_cost.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/github_actions_cost_summary.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/github_actions_failure_annotations.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/github_actions_failure_models.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/github_actions_failure_scan.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/github_actions_failure_selection.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/github_actions_failures.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/github_actions_permissions.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/github_api.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/github_branch.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/github_config.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/github_provider.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/github_repo.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/groups.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/models.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/output.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/presets.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/privacy.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/provider_api_model.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/provider_api_model_openai.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/provider_api_model_profiles.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/provider_env.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/provider_env_required.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/provider_env_tokens.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/provider_local_cli.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/provider_local_cli_commands.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/provider_local_cli_probe_config.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/provider_probe.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/provider_probe_auth.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/provider_probe_evaluation.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/provider_probe_json.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/provider_probe_remediation.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/provider_review_hygiene.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/providers.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/registry.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/runner.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/runtime.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/doctor_checks/runtime_github_auth.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/gemini_cli_audit_pr.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/grok_build_audit_pr.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/hermes_cli_audit_pr.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/init.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/lane_configs/__init__.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/lane_configs/aider.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/lane_configs/antigravity_cli.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/lane_configs/claude.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/lane_configs/codex.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/lane_configs/devin.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/lane_configs/gemini_cli.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/lane_configs/grok_build.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/lane_configs/hermes_cli.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/lane_configs/local_llm.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/local_llm_audit_pr.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/local_llm_bakeoff.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/local_llm_calibration.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/local_llm_profiles.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/migration.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/migration_install.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/migration_mirror.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/migration_readiness.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/migration_rehearsal.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/next_steps.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/package.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/package_content.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/package_manifest.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/package_paths.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/package_rendering.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/package_static.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/plan_context.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/prompts.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/provider_registry.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/provider_runners/__init__.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/provider_runners/comments.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/provider_runners/git.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/provider_runners/github_auth.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/provider_runners/github_pr.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/provider_runners/pr_worktree.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/provider_runners/process.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/provider_runners/repo_paths.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/provider_runners/text_schema.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/provider_runners/verdict_artifacts.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/provider_runners/workspace.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/providers/__init__.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/providers/local_cli.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/providers/provenance.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/release_readiness.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/reviewer_spend.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/saas_reviewer_labeler.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/secrets.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/builder-experiment.example.json +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/calibration-corpus.example.json +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/calibration-corpus.json +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/code-mower.example.yml +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/context-packs.example.json +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/lane_prompts/base-audit.md +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/lane_prompts/calibration-policy.md +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/lane_prompts/context-driven-quality.md +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/lane_prompts/docs-design.md +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/lane_prompts/generic-programming.md +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/lane_prompts/operability.md +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/lane_prompts/package-runtime.md +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/lane_prompts/plan-conformance.md +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/lane_prompts/security-threat-model.md +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/product-support/code_mower +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/product-support/code_mower_standalone_pin.env +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/product-support/code_mower_standalone_shadow.sh +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/product-support/run_claude_audit_pr.sh +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/product-support/run_codex_audit_pr.sh +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/product-support/safe_gh_comment.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/product-support/status_report.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/providers.yml +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/reviewer-spend.example.json +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/workflows/audit-label-cleanup.yml.j2 +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/workflows/code-mower-gate-health.yml.j2 +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/workflows/hosted-bridge.yml.j2 +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/workflows/local-cli-audit.yml.j2 +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/workflows/needs-owner-notify.yml.j2 +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/workflows/private-standalone-shadow.yml.j2 +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/workflows/review-clear-stale.yml.j2 +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/workflows/saas-reviewer-labeler.yml.j2 +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/workflows/trailer-comment-labeler.yml.j2 +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/templates/workflows/weekly-status.yml.j2 +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/versioning.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower/work_orders.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower.egg-info/SOURCES.txt +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower.egg-info/dependency_links.txt +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower.egg-info/entry_points.txt +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower.egg-info/requires.txt +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/src/code_mower.egg-info/top_level.txt +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/builder-experiment.example.json +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/calibration-corpus.example.json +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/calibration-corpus.json +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/code-mower.yml.j2 +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/context-packs.example.json +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/cursor/BUGBOT.md +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/providers/acp_bridge.yml +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/providers/aider.yml +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/providers/antigravity_cli.yml +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/providers/claude_audit.yml +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/providers/claude_review.yml +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/providers/coderabbit_cli.yml +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/providers/codex.yml +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/providers/cursor_bugbot.yml +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/providers/devin.yml +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/providers/gemini_cli.yml +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/providers/gitar.yml +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/providers/greptile.yml +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/providers/hermes_cli.yml +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/providers/local_llm.yml +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/providers/qodo.yml +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/providers.yml +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/reviewer-spend.example.json +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/workflows/audit-label-cleanup.yml.j2 +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/workflows/blind-review-artifacts-dry-run.yml.j2 +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/workflows/builder-provenance.yml.j2 +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/workflows/code-mower-gate-health.yml.j2 +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/workflows/hosted-bridge.yml.j2 +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/workflows/local-cli-audit.yml.j2 +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/workflows/private-standalone-shadow.yml.j2 +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/workflows/review-clear-stale.yml.j2 +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/workflows/saas-reviewer-labeler.yml.j2 +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/templates/workflows/trailer-comment-labeler.yml.j2 +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_audit_comment_posture.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_builder_identity.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_builder_runs.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_checks.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_claude_cli_bounce.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_clear_stale.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_cloud_client.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_cloud_git_metadata.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_cloud_manifest.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_cloud_reports.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_coderabbit_cli_audit_pr.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_codex_audit_env_preflight.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_doctor_actions_cost_summary.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_doctor_actions_failure_selection.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_doctor_boundaries.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_doctor_github_checks.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_doctor_output.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_doctor_privacy.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_doctor_provider_env_required.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_doctor_provider_env_tokens.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_doctor_provider_probe.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_doctor_provider_review_hygiene.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_doctor_registry.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_doctor_runtime.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_local_llm_github_helpers.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_plan_context.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_prompts.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_provider_local_cli.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_provider_runners_comments.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_provider_runners_git.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_provider_runners_github_pr.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_provider_runners_pr_worktree.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_provider_runners_process.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_provider_runners_repo_paths.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_provider_runners_text_schema.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_provider_runners_workspace.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_reviewer_spend.py +0 -0
- {code_mower-0.5.0b47 → code_mower-0.5.0b49}/tests/test_work_orders.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code-mower
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.0b49
|
|
4
4
|
Summary: Multi-reviewer AI code audit orchestration
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -103,11 +103,11 @@ Code Mower currently targets Python 3.11+; Python 3.12 is recommended.
|
|
|
103
103
|
|
|
104
104
|
```bash
|
|
105
105
|
python3.12 --version
|
|
106
|
-
pipx install --python python3.12 code-mower==0.5.
|
|
106
|
+
pipx install --python python3.12 code-mower==0.5.0b49
|
|
107
107
|
code-mower --version
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
`0.5.
|
|
110
|
+
`0.5.0b49` is a beta release. Until Code Mower publishes a stable `1.0`
|
|
111
111
|
line, use the explicit beta version above or allow prereleases with:
|
|
112
112
|
|
|
113
113
|
```bash
|
|
@@ -155,7 +155,7 @@ First-time command map: [docs/launch-command-surface.md](docs/launch-command-sur
|
|
|
155
155
|
|
|
156
156
|
The current PyPI beta has been rehearsed end-to-end from a clean install:
|
|
157
157
|
[First-User Install Rehearsal](docs/first-user-install-rehearsal.md) records
|
|
158
|
-
the latest 10/10 public-package readiness proof for `code-mower==0.5.
|
|
158
|
+
the latest 10/10 public-package readiness proof for `code-mower==0.5.0b49`.
|
|
159
159
|
|
|
160
160
|
## Optional: Plan Before Coding
|
|
161
161
|
|
|
@@ -310,9 +310,9 @@ builder provenance, start with
|
|
|
310
310
|
|
|
311
311
|
## Installation Status
|
|
312
312
|
|
|
313
|
-
The current public beta is `v0.5.0-beta.
|
|
313
|
+
The current public beta is `v0.5.0-beta.49` from
|
|
314
314
|
[codemower-ai/code-mower](https://github.com/codemower-ai/code-mower), published
|
|
315
|
-
as `code-mower==0.5.
|
|
315
|
+
as `code-mower==0.5.0b49` on [PyPI](https://pypi.org/project/code-mower/).
|
|
316
316
|
GitHub releases remain the auditable source for tags, build artifacts, and
|
|
317
317
|
release notes.
|
|
318
318
|
|
|
@@ -88,11 +88,11 @@ Code Mower currently targets Python 3.11+; Python 3.12 is recommended.
|
|
|
88
88
|
|
|
89
89
|
```bash
|
|
90
90
|
python3.12 --version
|
|
91
|
-
pipx install --python python3.12 code-mower==0.5.
|
|
91
|
+
pipx install --python python3.12 code-mower==0.5.0b49
|
|
92
92
|
code-mower --version
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
`0.5.
|
|
95
|
+
`0.5.0b49` is a beta release. Until Code Mower publishes a stable `1.0`
|
|
96
96
|
line, use the explicit beta version above or allow prereleases with:
|
|
97
97
|
|
|
98
98
|
```bash
|
|
@@ -140,7 +140,7 @@ First-time command map: [docs/launch-command-surface.md](docs/launch-command-sur
|
|
|
140
140
|
|
|
141
141
|
The current PyPI beta has been rehearsed end-to-end from a clean install:
|
|
142
142
|
[First-User Install Rehearsal](docs/first-user-install-rehearsal.md) records
|
|
143
|
-
the latest 10/10 public-package readiness proof for `code-mower==0.5.
|
|
143
|
+
the latest 10/10 public-package readiness proof for `code-mower==0.5.0b49`.
|
|
144
144
|
|
|
145
145
|
## Optional: Plan Before Coding
|
|
146
146
|
|
|
@@ -295,9 +295,9 @@ builder provenance, start with
|
|
|
295
295
|
|
|
296
296
|
## Installation Status
|
|
297
297
|
|
|
298
|
-
The current public beta is `v0.5.0-beta.
|
|
298
|
+
The current public beta is `v0.5.0-beta.49` from
|
|
299
299
|
[codemower-ai/code-mower](https://github.com/codemower-ai/code-mower), published
|
|
300
|
-
as `code-mower==0.5.
|
|
300
|
+
as `code-mower==0.5.0b49` on [PyPI](https://pypi.org/project/code-mower/).
|
|
301
301
|
GitHub releases remain the auditable source for tags, build artifacts, and
|
|
302
302
|
release notes.
|
|
303
303
|
|
|
@@ -30,6 +30,10 @@ ACTIONS_RUN_MARKER_RE = re.compile(
|
|
|
30
30
|
r"(?:\s+body_sha256=([0-9a-f]{64}))?\s*-->"
|
|
31
31
|
)
|
|
32
32
|
TRUSTED_ACTIONS_AUDIT_EVENTS = frozenset({"pull_request_target"})
|
|
33
|
+
AUDIT_RUN_NON_TERMINAL_STATUSES = frozenset(
|
|
34
|
+
{"queued", "requested", "waiting", "pending", "in_progress"}
|
|
35
|
+
)
|
|
36
|
+
HEAD_SHA_LINE_RE = re.compile(r"Head SHA:\s*`?([0-9a-fA-F]{7,40})`?", re.IGNORECASE)
|
|
33
37
|
|
|
34
38
|
|
|
35
39
|
@dataclass(frozen=True)
|
|
@@ -344,6 +348,192 @@ def workflow_path_matches(path: str, trusted_workflows: Sequence[str]) -> bool:
|
|
|
344
348
|
return any(path == item or path.endswith("/" + item) for item in trusted_workflows)
|
|
345
349
|
|
|
346
350
|
|
|
351
|
+
def flatten_paginated_items(payload: Any) -> list[dict[str, Any]]:
|
|
352
|
+
items: list[dict[str, Any]] = []
|
|
353
|
+
if isinstance(payload, list):
|
|
354
|
+
for page in payload:
|
|
355
|
+
if isinstance(page, list):
|
|
356
|
+
items.extend(item for item in page if isinstance(item, dict))
|
|
357
|
+
elif isinstance(page, dict):
|
|
358
|
+
items.append(page)
|
|
359
|
+
return items
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def audit_comment_head_sha(body: str) -> str:
|
|
363
|
+
match = HEAD_SHA_LINE_RE.search(body)
|
|
364
|
+
return match.group(1) if match else ""
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def terminal_audit_trailer_verdict(lane: Mapping[str, Any], body: str) -> str:
|
|
368
|
+
candidates: list[tuple[int, str]] = []
|
|
369
|
+
for label_key, verdict in (("done", "done"), ("blocked", "blocked")):
|
|
370
|
+
label = str(lane.get(label_key) or "")
|
|
371
|
+
if not label:
|
|
372
|
+
continue
|
|
373
|
+
index = body.rfind(": " + label + " -->")
|
|
374
|
+
if index >= 0:
|
|
375
|
+
candidates.append((index, verdict))
|
|
376
|
+
if not candidates:
|
|
377
|
+
return ""
|
|
378
|
+
return max(candidates, key=lambda item: item[0])[1]
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def current_audit_trailer_verdict(
|
|
382
|
+
lane: Mapping[str, Any],
|
|
383
|
+
body: str,
|
|
384
|
+
*,
|
|
385
|
+
head_sha: str,
|
|
386
|
+
) -> str:
|
|
387
|
+
if "Head SHA: `" + head_sha + "`" not in body:
|
|
388
|
+
return ""
|
|
389
|
+
return terminal_audit_trailer_verdict(lane, body)
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def latest_current_audit_verdict(
|
|
393
|
+
lane: Mapping[str, Any],
|
|
394
|
+
comments: Sequence[Mapping[str, Any]],
|
|
395
|
+
*,
|
|
396
|
+
head_sha: str,
|
|
397
|
+
trusted_comment_author: Callable[
|
|
398
|
+
[Mapping[str, Any], str, str, object, str | None],
|
|
399
|
+
bool,
|
|
400
|
+
],
|
|
401
|
+
) -> str:
|
|
402
|
+
verdicts: list[tuple[tuple[str, str, int, int], str]] = []
|
|
403
|
+
for index, comment in enumerate(comments):
|
|
404
|
+
author = str(((comment.get("user") or {}).get("login")) or "")
|
|
405
|
+
body = str(comment.get("body") or "")
|
|
406
|
+
if not trusted_comment_author(lane, author, body, comment.get("id"), None):
|
|
407
|
+
continue
|
|
408
|
+
verdict = current_audit_trailer_verdict(lane, body, head_sha=head_sha)
|
|
409
|
+
if verdict:
|
|
410
|
+
try:
|
|
411
|
+
comment_id = int(comment.get("id") or 0)
|
|
412
|
+
except (TypeError, ValueError):
|
|
413
|
+
comment_id = 0
|
|
414
|
+
updated = str(
|
|
415
|
+
comment.get("updated_at")
|
|
416
|
+
or comment.get("updatedAt")
|
|
417
|
+
or comment.get("created_at")
|
|
418
|
+
or comment.get("createdAt")
|
|
419
|
+
or ""
|
|
420
|
+
)
|
|
421
|
+
created = str(comment.get("created_at") or comment.get("createdAt") or "")
|
|
422
|
+
verdicts.append(((updated, created, comment_id, index), verdict))
|
|
423
|
+
if verdicts:
|
|
424
|
+
return max(verdicts, key=lambda item: item[0])[1]
|
|
425
|
+
return ""
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def attested_non_current_audit_heads(
|
|
429
|
+
lanes: Sequence[Mapping[str, Any]],
|
|
430
|
+
comments: Sequence[Mapping[str, Any]],
|
|
431
|
+
*,
|
|
432
|
+
head_sha: str,
|
|
433
|
+
trusted_comment_author: Callable[
|
|
434
|
+
[Mapping[str, Any], str, str, object, str | None],
|
|
435
|
+
bool,
|
|
436
|
+
],
|
|
437
|
+
) -> list[str]:
|
|
438
|
+
heads: list[str] = []
|
|
439
|
+
for comment in comments:
|
|
440
|
+
author = str(((comment.get("user") or {}).get("login")) or "")
|
|
441
|
+
body = str(comment.get("body") or "")
|
|
442
|
+
reviewed = audit_comment_head_sha(body)
|
|
443
|
+
if not reviewed or sha_matches(reviewed, head_sha):
|
|
444
|
+
continue
|
|
445
|
+
for lane in lanes:
|
|
446
|
+
if not terminal_audit_trailer_verdict(lane, body):
|
|
447
|
+
continue
|
|
448
|
+
if not trusted_comment_author(lane, author, body, comment.get("id"), reviewed):
|
|
449
|
+
continue
|
|
450
|
+
heads.append(reviewed)
|
|
451
|
+
break
|
|
452
|
+
return list(dict.fromkeys(heads))
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
def audit_run_workflow_path(run: Mapping[str, Any]) -> str:
|
|
456
|
+
return str(run.get("path") or run.get("workflow_path") or run.get("workflowPath") or "")
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
def audit_run_matches_pr_head(
|
|
460
|
+
run: Mapping[str, Any],
|
|
461
|
+
*,
|
|
462
|
+
head_sha: str,
|
|
463
|
+
pr_number: int | str,
|
|
464
|
+
) -> bool:
|
|
465
|
+
run_head_sha = str(run.get("head_sha") or "")
|
|
466
|
+
pull_requests = [
|
|
467
|
+
item for item in run.get("pull_requests") or [] if isinstance(item, Mapping)
|
|
468
|
+
]
|
|
469
|
+
for item in pull_requests:
|
|
470
|
+
if not isinstance(item, Mapping):
|
|
471
|
+
continue
|
|
472
|
+
if str(item.get("number") or "") != str(pr_number):
|
|
473
|
+
continue
|
|
474
|
+
pr_head = str(((item.get("head") or {}).get("sha")) or "")
|
|
475
|
+
if pr_head and sha_matches(pr_head, head_sha):
|
|
476
|
+
return True
|
|
477
|
+
if run_head_sha and sha_matches(run_head_sha, head_sha):
|
|
478
|
+
return True
|
|
479
|
+
if pull_requests:
|
|
480
|
+
return False
|
|
481
|
+
if run_head_sha and sha_matches(run_head_sha, head_sha):
|
|
482
|
+
return True
|
|
483
|
+
return False
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def _audit_lane_match_tokens(lane: Mapping[str, Any]) -> set[str]:
|
|
487
|
+
tokens = [
|
|
488
|
+
str(lane.get("id") or ""),
|
|
489
|
+
str(lane.get("author_lane") or ""),
|
|
490
|
+
str(lane.get("display_name") or ""),
|
|
491
|
+
]
|
|
492
|
+
for label_key in ("done", "blocked"):
|
|
493
|
+
label = str(lane.get(label_key) or "")
|
|
494
|
+
for suffix in ("-audit-done", "-audit-blocked", "-done", "-blocked"):
|
|
495
|
+
if label.endswith(suffix):
|
|
496
|
+
tokens.append(label[: -len(suffix)])
|
|
497
|
+
return {token.strip().lower().replace("_", "-") for token in tokens if token.strip()}
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
def audit_job_matches_lane(job: Mapping[str, Any], lane: Mapping[str, Any]) -> bool:
|
|
501
|
+
name = str(job.get("name") or "").lower().replace("_", "-")
|
|
502
|
+
return any(token in name for token in _audit_lane_match_tokens(lane))
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
def audit_run_in_flight_for_lane(
|
|
506
|
+
entry: Mapping[str, Any],
|
|
507
|
+
lane: Mapping[str, Any],
|
|
508
|
+
*,
|
|
509
|
+
head_sha: str,
|
|
510
|
+
pr_number: int | str,
|
|
511
|
+
) -> bool:
|
|
512
|
+
run = entry.get("run") if isinstance(entry.get("run"), Mapping) else entry
|
|
513
|
+
if not isinstance(run, Mapping):
|
|
514
|
+
return False
|
|
515
|
+
if str(run.get("status") or "") not in AUDIT_RUN_NON_TERMINAL_STATUSES:
|
|
516
|
+
return False
|
|
517
|
+
workflows = parse_csv_set(str(lane.get("github_actions_workflows") or ""))
|
|
518
|
+
if not workflow_path_matches(audit_run_workflow_path(run), workflows):
|
|
519
|
+
return False
|
|
520
|
+
if not audit_run_matches_pr_head(run, head_sha=head_sha, pr_number=pr_number):
|
|
521
|
+
return False
|
|
522
|
+
jobs = entry.get("jobs") if isinstance(entry.get("jobs"), list) else []
|
|
523
|
+
non_terminal_jobs = [
|
|
524
|
+
job
|
|
525
|
+
for job in jobs
|
|
526
|
+
if isinstance(job, Mapping)
|
|
527
|
+
and (
|
|
528
|
+
str(job.get("status") or "") in AUDIT_RUN_NON_TERMINAL_STATUSES
|
|
529
|
+
or (str(job.get("status") or "") != "completed" and job.get("conclusion") is None)
|
|
530
|
+
)
|
|
531
|
+
]
|
|
532
|
+
if non_terminal_jobs:
|
|
533
|
+
return any(audit_job_matches_lane(job, lane) for job in non_terminal_jobs)
|
|
534
|
+
return bool(entry.get("jobs_fetch_failed") or not jobs)
|
|
535
|
+
|
|
536
|
+
|
|
347
537
|
def _pr_item_matches_head(
|
|
348
538
|
item: Mapping[str, Any],
|
|
349
539
|
*,
|
|
@@ -85,6 +85,7 @@ from .run_status import (
|
|
|
85
85
|
RUN_STATUS_CATEGORY_ALIASES,
|
|
86
86
|
RUN_STATUS_INFRA_ERROR,
|
|
87
87
|
RUN_STATUS_PASS,
|
|
88
|
+
RUN_STATUS_STALE,
|
|
88
89
|
RUN_STATUS_UNKNOWN,
|
|
89
90
|
count_normalized_findings,
|
|
90
91
|
normalize_run_status_category,
|
|
@@ -119,6 +120,7 @@ __all__ = [
|
|
|
119
120
|
"RUN_STATUS_CATEGORY_ALIASES",
|
|
120
121
|
"RUN_STATUS_INFRA_ERROR",
|
|
121
122
|
"RUN_STATUS_PASS",
|
|
123
|
+
"RUN_STATUS_STALE",
|
|
122
124
|
"RUN_STATUS_UNKNOWN",
|
|
123
125
|
"SELECTIVE_USEFUL_RATE",
|
|
124
126
|
"TRUTH_EXPECTATION_ALIASES",
|
|
@@ -12,6 +12,7 @@ from .run_status import (
|
|
|
12
12
|
RUN_STATUS_BLOCKED,
|
|
13
13
|
RUN_STATUS_INFRA_ERROR,
|
|
14
14
|
RUN_STATUS_PASS,
|
|
15
|
+
RUN_STATUS_STALE,
|
|
15
16
|
normalize_run_status_category,
|
|
16
17
|
)
|
|
17
18
|
|
|
@@ -39,6 +40,7 @@ class CellStats:
|
|
|
39
40
|
known_blocked_missed_runs: int = 0
|
|
40
41
|
audit_input_insufficient_runs: int = 0
|
|
41
42
|
infra_error_runs: int = 0
|
|
43
|
+
stale_runs: int = 0
|
|
42
44
|
unknown_blocked_runs: int = 0
|
|
43
45
|
known_clean_runs: int = 0
|
|
44
46
|
known_clean_pass_runs: int = 0
|
|
@@ -94,6 +96,7 @@ class CellStats:
|
|
|
94
96
|
"clean_nonblocking_finding_runs": self.clean_nonblocking_finding_runs,
|
|
95
97
|
"audit_input_insufficient_runs": self.audit_input_insufficient_runs,
|
|
96
98
|
"infra_error_runs": self.infra_error_runs,
|
|
99
|
+
"stale_runs": self.stale_runs,
|
|
97
100
|
"unknown_blocked_runs": self.unknown_blocked_runs,
|
|
98
101
|
"duration_seconds_total": round(self.duration_seconds_total, 3),
|
|
99
102
|
"seconds_per_run": _round_or_none(self.seconds_per_run),
|
|
@@ -209,8 +212,12 @@ def build_effect_report(
|
|
|
209
212
|
cell.audit_input_insufficient_runs += 1
|
|
210
213
|
elif status_category == RUN_STATUS_INFRA_ERROR:
|
|
211
214
|
cell.infra_error_runs += 1
|
|
215
|
+
elif status_category == RUN_STATUS_STALE:
|
|
216
|
+
cell.stale_runs += 1
|
|
212
217
|
else:
|
|
213
218
|
cell.unknown_blocked_runs += 1
|
|
219
|
+
elif status_category == RUN_STATUS_STALE:
|
|
220
|
+
cell.stale_runs += 1
|
|
214
221
|
if known_clean:
|
|
215
222
|
cell.known_clean_runs += 1
|
|
216
223
|
if status_category == RUN_STATUS_PASS and finding_count == 0:
|
|
@@ -489,8 +496,8 @@ def render_effect_report_text(report: Mapping[str, Any]) -> str:
|
|
|
489
496
|
"",
|
|
490
497
|
"## Provider/Lens Cells",
|
|
491
498
|
"",
|
|
492
|
-
"| Provider | Lens | Runs | Blocked caught/missed | Input gaps | Clean pass/false block | Effective catch | Evaluable catch | False-blocker rate | Sec/run |",
|
|
493
|
-
"| --- | --- | ---: | --- | ---: | --- | ---: | ---: | ---: | ---: |",
|
|
499
|
+
"| Provider | Lens | Runs | Blocked caught/missed | Input gaps | Stale | Clean pass/false block | Effective catch | Evaluable catch | False-blocker rate | Sec/run |",
|
|
500
|
+
"| --- | --- | ---: | --- | ---: | ---: | --- | ---: | ---: | ---: | ---: |",
|
|
494
501
|
]
|
|
495
502
|
for cell in report.get("cells", []) or []:
|
|
496
503
|
if not isinstance(cell, Mapping):
|
|
@@ -504,6 +511,7 @@ def render_effect_report_text(report: Mapping[str, Any]) -> str:
|
|
|
504
511
|
str(cell.get("runs", 0)),
|
|
505
512
|
f"{cell.get('known_blocked_caught_runs', 0)}/{cell.get('known_blocked_missed_runs', 0)}",
|
|
506
513
|
str(cell.get("audit_input_insufficient_runs", 0)),
|
|
514
|
+
str(cell.get("stale_runs", 0)),
|
|
507
515
|
f"{cell.get('known_clean_pass_runs', 0)}/{cell.get('blocking_false_positive_runs', 0)}",
|
|
508
516
|
_format_rate(cell.get("effective_catch_rate")),
|
|
509
517
|
_format_rate(cell.get("catch_rate")),
|
|
@@ -12,6 +12,7 @@ from .run_status import (
|
|
|
12
12
|
RUN_STATUS_BLOCKED,
|
|
13
13
|
RUN_STATUS_INFRA_ERROR,
|
|
14
14
|
RUN_STATUS_PASS,
|
|
15
|
+
RUN_STATUS_STALE,
|
|
15
16
|
RUN_STATUS_UNKNOWN,
|
|
16
17
|
normalize_run_status_category,
|
|
17
18
|
)
|
|
@@ -77,6 +78,7 @@ def build_reviewer_evidence_report(corpus: Mapping[str, Any]) -> dict[str, Any]:
|
|
|
77
78
|
profile_known_blocked_missed_run_keys: dict[str, set[tuple[Any, ...]]] = {}
|
|
78
79
|
profile_audit_input_insufficient_run_keys: dict[str, set[tuple[Any, ...]]] = {}
|
|
79
80
|
profile_infra_error_run_keys: dict[str, set[tuple[Any, ...]]] = {}
|
|
81
|
+
profile_stale_run_keys: dict[str, set[tuple[Any, ...]]] = {}
|
|
80
82
|
profile_run_statuses: dict[str, dict[str, int]] = {}
|
|
81
83
|
profile_automated_vs_manual: dict[str, dict[str, int]] = {}
|
|
82
84
|
profile_review_classes: dict[str, set[str]] = {}
|
|
@@ -303,6 +305,8 @@ def build_reviewer_evidence_report(corpus: Mapping[str, Any]) -> dict[str, Any]:
|
|
|
303
305
|
profile_infra_error_run_keys.setdefault(reviewer, set()).add(
|
|
304
306
|
reviewer_run_key
|
|
305
307
|
)
|
|
308
|
+
if status_category == RUN_STATUS_STALE:
|
|
309
|
+
profile_stale_run_keys.setdefault(reviewer, set()).add(reviewer_run_key)
|
|
306
310
|
if status_category == RUN_STATUS_AUDIT_INPUT_INSUFFICIENT:
|
|
307
311
|
profile_audit_input_insufficient_run_keys.setdefault(
|
|
308
312
|
reviewer, set()
|
|
@@ -358,6 +362,7 @@ def build_reviewer_evidence_report(corpus: Mapping[str, Any]) -> dict[str, Any]:
|
|
|
358
362
|
profile_known_blocked_missed_run_keys.get(reviewer, set())
|
|
359
363
|
),
|
|
360
364
|
"infra_error_runs": len(profile_infra_error_run_keys.get(reviewer, set())),
|
|
365
|
+
"stale_runs": len(profile_stale_run_keys.get(reviewer, set())),
|
|
361
366
|
"audit_input_insufficient_runs": len(
|
|
362
367
|
profile_audit_input_insufficient_run_keys.get(reviewer, set())
|
|
363
368
|
),
|
|
@@ -32,6 +32,7 @@ def build_lane_policy_report(metrics: Mapping[str, Any]) -> dict[str, Any]:
|
|
|
32
32
|
false_positive_runs = int(stats.get("blocking_false_positive_runs") or 0)
|
|
33
33
|
known_blocked_missed_runs = int(stats.get("known_blocked_missed_runs") or 0)
|
|
34
34
|
infra_error_runs = int(stats.get("infra_error_runs") or 0)
|
|
35
|
+
stale_runs = int(stats.get("stale_runs") or 0)
|
|
35
36
|
audit_input_insufficient_runs = int(
|
|
36
37
|
stats.get("audit_input_insufficient_runs") or 0
|
|
37
38
|
)
|
|
@@ -158,6 +159,7 @@ def build_lane_policy_report(metrics: Mapping[str, Any]) -> dict[str, Any]:
|
|
|
158
159
|
),
|
|
159
160
|
"known_blocked_missed_runs": known_blocked_missed_runs,
|
|
160
161
|
"infra_error_runs": infra_error_runs,
|
|
162
|
+
"stale_runs": stale_runs,
|
|
161
163
|
"audit_input_insufficient_runs": audit_input_insufficient_runs,
|
|
162
164
|
"observed_pr_count": observed_pr_count,
|
|
163
165
|
"reasons": reasons
|
|
@@ -9,6 +9,7 @@ RUN_STATUS_PASS = "pass"
|
|
|
9
9
|
RUN_STATUS_BLOCKED = "blocked"
|
|
10
10
|
RUN_STATUS_AUDIT_INPUT_INSUFFICIENT = "audit_input_insufficient"
|
|
11
11
|
RUN_STATUS_INFRA_ERROR = "infra_error"
|
|
12
|
+
RUN_STATUS_STALE = "stale"
|
|
12
13
|
RUN_STATUS_UNKNOWN = "unknown"
|
|
13
14
|
|
|
14
15
|
RUN_STATUS_CATEGORY_ALIASES = {
|
|
@@ -36,7 +37,9 @@ RUN_STATUS_CATEGORY_ALIASES = {
|
|
|
36
37
|
"rate_limit": RUN_STATUS_INFRA_ERROR,
|
|
37
38
|
"rate_limited": RUN_STATUS_INFRA_ERROR,
|
|
38
39
|
"setup_error": RUN_STATUS_INFRA_ERROR,
|
|
39
|
-
"stale":
|
|
40
|
+
"stale": RUN_STATUS_STALE,
|
|
41
|
+
"superseded": RUN_STATUS_STALE,
|
|
42
|
+
"superseded_head": RUN_STATUS_STALE,
|
|
40
43
|
"timeout": RUN_STATUS_INFRA_ERROR,
|
|
41
44
|
"timed_out": RUN_STATUS_INFRA_ERROR,
|
|
42
45
|
}
|
|
@@ -56,8 +56,8 @@ def render_value_report_text(report: Mapping[str, Any]) -> str:
|
|
|
56
56
|
f"Run dispositions: {report.get('run_disposition_count', 0)}",
|
|
57
57
|
f"Reviewer runs: {report.get('reviewer_run_count', 0)}",
|
|
58
58
|
"",
|
|
59
|
-
"| Reviewer | Runs | Useful | Negative | Useful rate | Known-clean pass | Known-blocked caught/missed | Infra errors | Input gaps | Auto/manual M/MB/FB | Cost | Sec/run | Cost/useful | Policy | Recommended role |",
|
|
60
|
-
"| --- | ---: | ---: | ---: | ---: | ---: | --- | ---: | ---: | --- | ---: | ---: | ---: | --- | --- |",
|
|
59
|
+
"| Reviewer | Runs | Useful | Negative | Useful rate | Known-clean pass | Known-blocked caught/missed | Infra errors | Stale | Input gaps | Auto/manual M/MB/FB | Cost | Sec/run | Cost/useful | Policy | Recommended role |",
|
|
60
|
+
"| --- | ---: | ---: | ---: | ---: | ---: | --- | ---: | ---: | ---: | --- | ---: | ---: | ---: | --- | --- |",
|
|
61
61
|
]
|
|
62
62
|
if isinstance(profiles, Mapping) and profiles:
|
|
63
63
|
for profile_id, stats in sorted(profiles.items()):
|
|
@@ -89,6 +89,7 @@ def render_value_report_text(report: Mapping[str, Any]) -> str:
|
|
|
89
89
|
str(stats.get("known_clean_pass_runs", 0)),
|
|
90
90
|
caught_missed,
|
|
91
91
|
str(stats.get("infra_error_runs", 0)),
|
|
92
|
+
str(stats.get("stale_runs", 0)),
|
|
92
93
|
str(stats.get("audit_input_insufficient_runs", 0)),
|
|
93
94
|
auto_manual,
|
|
94
95
|
str(stats.get("cost_usd", 0)),
|
|
@@ -109,7 +110,7 @@ def render_value_report_text(report: Mapping[str, Any]) -> str:
|
|
|
109
110
|
+ " |"
|
|
110
111
|
)
|
|
111
112
|
else:
|
|
112
|
-
lines.append("| none | 0 | 0 | 0 | | 0 | 0/0 | 0 | 0 | 0/0/0 | 0 | | | | |")
|
|
113
|
+
lines.append("| none | 0 | 0 | 0 | | 0 | 0/0 | 0 | 0 | 0 | 0/0/0 | 0 | | | | |")
|
|
113
114
|
|
|
114
115
|
recommendations = metrics.get("recommendations", []) if isinstance(metrics, Mapping) else []
|
|
115
116
|
lines.extend(["", "## Recommendations"])
|
|
@@ -188,6 +189,7 @@ def render_value_report_html(report: Mapping[str, Any]) -> str:
|
|
|
188
189
|
f"<td>{esc(stats.get('negative_findings', 0))}</td>"
|
|
189
190
|
f"<td>{esc(stats.get('useful_rate', ''))}</td>"
|
|
190
191
|
f"<td>{esc(caught_missed)}</td>"
|
|
192
|
+
f"<td>{esc(stats.get('stale_runs', 0))}</td>"
|
|
191
193
|
f"<td>{esc(auto_manual)}</td>"
|
|
192
194
|
f"<td>{esc(stats.get('cost_usd', 0))}</td>"
|
|
193
195
|
f"<td>{esc(stats.get('seconds_per_run', ''))}</td>"
|
|
@@ -195,7 +197,7 @@ def render_value_report_html(report: Mapping[str, Any]) -> str:
|
|
|
195
197
|
"</tr>"
|
|
196
198
|
)
|
|
197
199
|
else:
|
|
198
|
-
rows.append("<tr><td colspan='
|
|
200
|
+
rows.append("<tr><td colspan='11'>No reviewer rows yet.</td></tr>")
|
|
199
201
|
|
|
200
202
|
recommendation_items = (
|
|
201
203
|
"\n".join(f"<li>{esc(item)}</li>" for item in recommendations)
|
|
@@ -242,7 +244,7 @@ def render_value_report_html(report: Mapping[str, Any]) -> str:
|
|
|
242
244
|
" </section>\n"
|
|
243
245
|
" <section class='card'>\n"
|
|
244
246
|
" <h2>Reviewer / lens signal</h2>\n"
|
|
245
|
-
" <table><thead><tr><th>Reviewer</th><th>Runs</th><th>Useful</th><th>Negative</th><th>Useful rate</th><th>Blocked caught/missed</th><th>Auto/manual</th><th>Cost</th><th>Sec/run</th><th>Role</th></tr></thead>\n"
|
|
247
|
+
" <table><thead><tr><th>Reviewer</th><th>Runs</th><th>Useful</th><th>Negative</th><th>Useful rate</th><th>Blocked caught/missed</th><th>Stale</th><th>Auto/manual</th><th>Cost</th><th>Sec/run</th><th>Role</th></tr></thead>\n"
|
|
246
248
|
f" <tbody>{''.join(rows)}</tbody></table>\n"
|
|
247
249
|
" </section>\n"
|
|
248
250
|
" <section class='card'>\n"
|
|
@@ -209,6 +209,7 @@ class ClaudeAuditConfig:
|
|
|
209
209
|
max_plan_context_bytes: int = code_mower_plan_context.DEFAULT_MAX_TOTAL_BYTES
|
|
210
210
|
max_plan_context_file_bytes: int = code_mower_plan_context.DEFAULT_MAX_FILE_BYTES
|
|
211
211
|
merge_authority: bool = True
|
|
212
|
+
calibration_badge: str = ""
|
|
212
213
|
|
|
213
214
|
|
|
214
215
|
@dataclass
|
|
@@ -1026,10 +1027,13 @@ def format_comment(
|
|
|
1026
1027
|
merge_authority: bool = True,
|
|
1027
1028
|
actions_run_id: Optional[str] = None,
|
|
1028
1029
|
unknown_reason: str = "",
|
|
1030
|
+
calibration_badge: str = "",
|
|
1029
1031
|
) -> str:
|
|
1030
1032
|
posture = "merge-authority lane" if merge_authority else "informational only"
|
|
1031
1033
|
header = f"## Claude audit ({posture})\n\n"
|
|
1032
1034
|
header += f"Head SHA: `{head_sha}`\n"
|
|
1035
|
+
if badge := _normalize_calibration_badge(calibration_badge):
|
|
1036
|
+
header += f"Calibration: {badge}\n"
|
|
1033
1037
|
if actions_run_id:
|
|
1034
1038
|
header += f"{AUDIT_RUN_TRAILER_PREFIX} run_id={actions_run_id} -->\n"
|
|
1035
1039
|
if is_stale:
|
|
@@ -1079,6 +1083,10 @@ def format_comment(
|
|
|
1079
1083
|
return limit_comment_body(body, trailer, provider_name="Claude")
|
|
1080
1084
|
|
|
1081
1085
|
|
|
1086
|
+
def _normalize_calibration_badge(value: str | None) -> str:
|
|
1087
|
+
return " ".join(str(value or "").strip().split())[:120]
|
|
1088
|
+
|
|
1089
|
+
|
|
1082
1090
|
def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAuditResult:
|
|
1083
1091
|
audit_started = time.monotonic()
|
|
1084
1092
|
local_repo = config.repo_paths.get(repo)
|
|
@@ -1148,6 +1156,7 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
|
|
|
1148
1156
|
stale_end_sha=exc.actual_sha,
|
|
1149
1157
|
merge_authority=config.merge_authority,
|
|
1150
1158
|
actions_run_id=actions_run_id,
|
|
1159
|
+
calibration_badge=config.calibration_badge,
|
|
1151
1160
|
)
|
|
1152
1161
|
result = ClaudeAuditResult(
|
|
1153
1162
|
repo=repo,
|
|
@@ -1334,6 +1343,7 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
|
|
|
1334
1343
|
stale_end_sha=head_sha_end,
|
|
1335
1344
|
merge_authority=config.merge_authority,
|
|
1336
1345
|
actions_run_id=actions_run_id,
|
|
1346
|
+
calibration_badge=config.calibration_badge,
|
|
1337
1347
|
)
|
|
1338
1348
|
result_verdict = "STALE"
|
|
1339
1349
|
trailer = STALE_TRAILER
|
|
@@ -1345,6 +1355,7 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
|
|
|
1345
1355
|
merge_authority=config.merge_authority,
|
|
1346
1356
|
actions_run_id=actions_run_id,
|
|
1347
1357
|
unknown_reason=claude_cli_reason,
|
|
1358
|
+
calibration_badge=config.calibration_badge,
|
|
1348
1359
|
)
|
|
1349
1360
|
result_verdict = "UNKNOWN"
|
|
1350
1361
|
trailer = STALE_TRAILER
|
|
@@ -1354,6 +1365,7 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
|
|
|
1354
1365
|
head_sha_start,
|
|
1355
1366
|
merge_authority=config.merge_authority,
|
|
1356
1367
|
actions_run_id=actions_run_id,
|
|
1368
|
+
calibration_badge=config.calibration_badge,
|
|
1357
1369
|
)
|
|
1358
1370
|
result_verdict = parsed.verdict
|
|
1359
1371
|
trailer = BLOCKED_TRAILER if parsed.verdict == "BLOCKED" else DONE_TRAILER
|
|
@@ -1611,7 +1623,15 @@ def _parse_args(argv: Optional[List[str]] = None) -> argparse.Namespace:
|
|
|
1611
1623
|
"--informational",
|
|
1612
1624
|
dest="merge_authority",
|
|
1613
1625
|
action="store_false",
|
|
1614
|
-
help="Render audit comments as informational-only
|
|
1626
|
+
help="Render audit comments as informational-only lane comments.",
|
|
1627
|
+
)
|
|
1628
|
+
ap.add_argument(
|
|
1629
|
+
"--calibration-badge",
|
|
1630
|
+
default=os.environ.get("CLAUDE_AUDIT_CALIBRATION_BADGE", ""),
|
|
1631
|
+
help=(
|
|
1632
|
+
"Render a separate calibration status badge line without changing "
|
|
1633
|
+
"the lane's merge-authority posture."
|
|
1634
|
+
),
|
|
1615
1635
|
)
|
|
1616
1636
|
return ap.parse_args(argv)
|
|
1617
1637
|
|
|
@@ -1673,6 +1693,7 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|
|
1673
1693
|
max_plan_context_bytes=args.max_plan_context_bytes,
|
|
1674
1694
|
max_plan_context_file_bytes=args.max_plan_context_file_bytes,
|
|
1675
1695
|
merge_authority=args.merge_authority,
|
|
1696
|
+
calibration_badge=args.calibration_badge,
|
|
1676
1697
|
)
|
|
1677
1698
|
result = audit_pr(config, args.repo, args.pr)
|
|
1678
1699
|
except (OSError, RuntimeError, ValueError, subprocess.CalledProcessError) as exc:
|
|
@@ -1705,7 +1726,7 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|
|
1705
1726
|
|
|
1706
1727
|
|
|
1707
1728
|
def _audit_exit_code(verdict: str) -> int:
|
|
1708
|
-
return 2 if verdict == "UNKNOWN" else 0
|
|
1729
|
+
return 2 if str(verdict or "").upper() == "UNKNOWN" else 0
|
|
1709
1730
|
|
|
1710
1731
|
|
|
1711
1732
|
if __name__ == "__main__":
|
|
@@ -14,7 +14,7 @@ from pathlib import Path
|
|
|
14
14
|
if __package__ in {None, ""}:
|
|
15
15
|
raise SystemExit(
|
|
16
16
|
"code_mower.cli is a packaged entrypoint. Install Code Mower with "
|
|
17
|
-
"`pipx install code-mower==0.5.
|
|
17
|
+
"`pipx install code-mower==0.5.0b49`, or run source checkouts with "
|
|
18
18
|
"`PYTHONPATH=src python -m code_mower.cli`."
|
|
19
19
|
)
|
|
20
20
|
|
|
@@ -20,6 +20,7 @@ if __package__ in {None, ""}:
|
|
|
20
20
|
RUN_STATUS_CATEGORY_ALIASES,
|
|
21
21
|
RUN_STATUS_INFRA_ERROR,
|
|
22
22
|
RUN_STATUS_PASS,
|
|
23
|
+
RUN_STATUS_STALE,
|
|
23
24
|
RUN_STATUS_UNKNOWN,
|
|
24
25
|
TRUTH_EXPECTATION_ALIASES,
|
|
25
26
|
TRUTH_EXPECTATION_KNOWN_BLOCKED,
|
|
@@ -89,6 +90,7 @@ if __package__ in {None, ""}:
|
|
|
89
90
|
RUN_STATUS_CATEGORY_ALIASES,
|
|
90
91
|
RUN_STATUS_INFRA_ERROR,
|
|
91
92
|
RUN_STATUS_PASS,
|
|
93
|
+
RUN_STATUS_STALE,
|
|
92
94
|
RUN_STATUS_UNKNOWN,
|
|
93
95
|
TRUTH_EXPECTATION_ALIASES,
|
|
94
96
|
TRUTH_EXPECTATION_KNOWN_BLOCKED,
|
|
@@ -158,6 +160,7 @@ elif __package__ == "tools":
|
|
|
158
160
|
RUN_STATUS_CATEGORY_ALIASES,
|
|
159
161
|
RUN_STATUS_INFRA_ERROR,
|
|
160
162
|
RUN_STATUS_PASS,
|
|
163
|
+
RUN_STATUS_STALE,
|
|
161
164
|
RUN_STATUS_UNKNOWN,
|
|
162
165
|
TRUTH_EXPECTATION_ALIASES,
|
|
163
166
|
TRUTH_EXPECTATION_KNOWN_BLOCKED,
|
|
@@ -227,6 +230,7 @@ else: # pragma: no cover - exercised after package extraction.
|
|
|
227
230
|
RUN_STATUS_CATEGORY_ALIASES,
|
|
228
231
|
RUN_STATUS_INFRA_ERROR,
|
|
229
232
|
RUN_STATUS_PASS,
|
|
233
|
+
RUN_STATUS_STALE,
|
|
230
234
|
RUN_STATUS_UNKNOWN,
|
|
231
235
|
TRUTH_EXPECTATION_ALIASES,
|
|
232
236
|
TRUTH_EXPECTATION_KNOWN_BLOCKED,
|
|
@@ -296,6 +300,7 @@ _LEGACY_RUN_STATUS_EXPORTS = (
|
|
|
296
300
|
RUN_STATUS_CATEGORY_ALIASES,
|
|
297
301
|
RUN_STATUS_INFRA_ERROR,
|
|
298
302
|
RUN_STATUS_PASS,
|
|
303
|
+
RUN_STATUS_STALE,
|
|
299
304
|
RUN_STATUS_UNKNOWN,
|
|
300
305
|
_normalize_run_status_category,
|
|
301
306
|
)
|