code-mower 0.5.0b41__tar.gz → 0.5.0b42__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.0b41 → code_mower-0.5.0b42}/PKG-INFO +6 -6
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/README.md +5 -5
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/pyproject.toml +1 -1
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/__init__.py +1 -1
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/audit_labeler_lib.py +129 -1
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/claude_audit_pr.py +27 -2
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/cli.py +1 -1
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/cloud.py +7 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/cloud_client/operations.py +8 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/code_mower_telemetry.py +5 -1
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/codex_audit_pr.py +28 -4
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/init.py +91 -1
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/lane_configs/claude.py +1 -1
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/lane_configs/codex.py +1 -1
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/package_manifest.py +2 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/provider_runners/__init__.py +6 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/provider_runners/pr_worktree.py +36 -1
- {code_mower-0.5.0b41 → code_mower-0.5.0b42/src/code_mower}/templates/workflows/builder-provenance.yml.j2 +1 -1
- code_mower-0.5.0b42/src/code_mower/templates/workflows/code-mower-gate-health.yml.j2 +321 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42/src/code_mower}/templates/workflows/code-mower-gate.yml.j2 +36 -4
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/workflows/self-hosted-local-audit.yml.j2 +77 -5
- {code_mower-0.5.0b41 → code_mower-0.5.0b42/src/code_mower}/templates/workflows/trailer-comment-labeler.yml.j2 +2 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/trailer_comment_labeler.py +39 -3
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower.egg-info/PKG-INFO +6 -6
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower.egg-info/SOURCES.txt +2 -0
- code_mower-0.5.0b42/templates/workflows/code-mower-gate-health.yml.j2 +321 -0
- {code_mower-0.5.0b41/src/code_mower → code_mower-0.5.0b42}/templates/workflows/code-mower-gate.yml.j2 +36 -4
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/workflows/self-hosted-local-audit.yml.j2 +77 -5
- {code_mower-0.5.0b41/src/code_mower → code_mower-0.5.0b42}/templates/workflows/trailer-comment-labeler.yml.j2 +2 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_audit_comment_posture.py +15 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_provider_runners_pr_worktree.py +58 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_release_hygiene.py +461 -38
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_reviewer_metrics_core.py +47 -0
- code_mower-0.5.0b42/tests/test_trailer_comment_labeler.py +151 -0
- code_mower-0.5.0b41/tests/test_trailer_comment_labeler.py +0 -66
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/CODE_OF_CONDUCT.md +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/LICENSE +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/MANIFEST.in +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/NOTICE +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/SUPPORT.md +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/requirements/requirements.txt +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/setup.cfg +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/adapters/__init__.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/adapters/_base.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/adapters/cursor_bugbot.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/adapters/gitar.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/adapters/greptile.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/adapters/qodo.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/antigravity_cli_audit_pr.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/audit_handoff_log.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/audit_progress.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/blind_review_artifacts.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/blind_review_coordinator.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/bootstrap.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/builder_experiment.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/builder_runs.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/calibration/__init__.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/calibration/arms.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/calibration/auto_discovery.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/calibration/commands.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/calibration/context_inputs.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/calibration/corpus.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/calibration/effect_report.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/calibration/evidence.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/calibration/evidence_report.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/calibration/identity.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/calibration/metrics.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/calibration/overlap.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/calibration/planning.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/calibration/policy.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/calibration/results.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/calibration/run_results.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/calibration/run_status.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/calibration/runner.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/calibration/truth.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/calibration/value_report.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/checks.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/claude_cli_bounce.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/claude_cli_environment.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/clear_stale.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/cloud_client/__init__.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/cloud_client/bundle.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/cloud_client/doctor.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/cloud_client/dogfood.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/cloud_client/endpoints.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/cloud_client/errors.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/cloud_client/events.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/cloud_client/export.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/cloud_client/git_metadata.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/cloud_client/manifest.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/cloud_client/reports.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/cloud_client/setup.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/cloud_client/upload.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/code_mower_calibration.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/code_mower_context_packs.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/code_mower_merge.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/coderabbit_cli_audit_pr.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/codex_audit_env_preflight.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/codex_audit_schema_smoke.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/codex_audit_verdict.schema.json +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/config.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/__init__.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/cloud.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/common.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_cost.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_cost_summary.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_failure_annotations.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_failure_models.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_failure_scan.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_failure_selection.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_failures.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_permissions.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_api.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_branch.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_config.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_provider.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_repo.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/groups.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/models.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/output.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/presets.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/privacy.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_api_model.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_api_model_openai.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_api_model_profiles.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_env.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_env_required.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_env_tokens.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_local_cli.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_local_cli_commands.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_local_cli_probe_config.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_probe.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_probe_auth.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_probe_evaluation.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_probe_json.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_probe_remediation.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_review_hygiene.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/providers.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/registry.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/runner.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/runtime.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/runtime_github_auth.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/gemini_cli_audit_pr.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/grok_build_audit_pr.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/hermes_cli_audit_pr.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/lane_configs/__init__.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/lane_configs/aider.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/lane_configs/antigravity_cli.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/lane_configs/devin.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/lane_configs/gemini_cli.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/lane_configs/grok_build.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/lane_configs/hermes_cli.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/lane_configs/local_llm.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/local_llm_audit_pr.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/local_llm_bakeoff.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/local_llm_calibration.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/local_llm_profiles.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/migration.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/migration_install.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/migration_mirror.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/migration_readiness.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/migration_rehearsal.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/next_steps.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/package.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/package_content.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/package_paths.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/package_rendering.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/package_static.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/plan_context.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/prompts.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/provider_registry.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/provider_runners/comments.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/provider_runners/git.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/provider_runners/github_auth.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/provider_runners/github_pr.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/provider_runners/process.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/provider_runners/repo_paths.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/provider_runners/text_schema.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/provider_runners/verdict_artifacts.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/provider_runners/workspace.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/providers/__init__.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/providers/local_cli.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/providers/provenance.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/release_readiness.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/reviewer_metrics.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/reviewer_spend.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/saas_reviewer_labeler.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/secrets.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/builder-experiment.example.json +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/calibration-corpus.example.json +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/calibration-corpus.json +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/code-mower.example.yml +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/context-packs.example.json +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/base-audit.md +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/calibration-policy.md +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/context-driven-quality.md +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/docs-design.md +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/generic-programming.md +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/operability.md +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/package-runtime.md +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/plan-conformance.md +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/security-threat-model.md +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/product-support/code_mower +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/product-support/code_mower_standalone_pin.env +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/product-support/code_mower_standalone_shadow.sh +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/product-support/run_claude_audit_pr.sh +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/product-support/run_codex_audit_pr.sh +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/product-support/safe_gh_comment.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/product-support/status_report.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/providers.yml +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/reviewer-spend.example.json +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/reviewer-value-report.example.md +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/workflows/audit-label-cleanup.yml.j2 +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/workflows/hosted-bridge.yml.j2 +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/workflows/local-cli-audit.yml.j2 +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/workflows/needs-owner-notify.yml.j2 +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/workflows/private-standalone-shadow.yml.j2 +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/workflows/review-clear-stale.yml.j2 +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/workflows/saas-reviewer-labeler.yml.j2 +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/templates/workflows/weekly-status.yml.j2 +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/versioning.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower/work_orders.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower.egg-info/dependency_links.txt +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower.egg-info/entry_points.txt +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower.egg-info/requires.txt +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/src/code_mower.egg-info/top_level.txt +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/builder-experiment.example.json +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/calibration-corpus.example.json +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/calibration-corpus.json +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/code-mower.yml.j2 +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/context-packs.example.json +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/cursor/BUGBOT.md +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/providers/acp_bridge.yml +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/providers/aider.yml +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/providers/antigravity_cli.yml +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/providers/claude_audit.yml +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/providers/claude_review.yml +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/providers/coderabbit_cli.yml +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/providers/codex.yml +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/providers/cursor_bugbot.yml +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/providers/devin.yml +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/providers/gemini_cli.yml +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/providers/gitar.yml +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/providers/greptile.yml +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/providers/hermes_cli.yml +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/providers/local_llm.yml +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/providers/qodo.yml +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/providers.yml +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/reviewer-spend.example.json +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/reviewer-value-report.example.md +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/workflows/audit-label-cleanup.yml.j2 +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/workflows/blind-review-artifacts-dry-run.yml.j2 +0 -0
- {code_mower-0.5.0b41/src/code_mower → code_mower-0.5.0b42}/templates/workflows/builder-provenance.yml.j2 +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/workflows/hosted-bridge.yml.j2 +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/workflows/local-cli-audit.yml.j2 +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/workflows/private-standalone-shadow.yml.j2 +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/workflows/review-clear-stale.yml.j2 +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/templates/workflows/saas-reviewer-labeler.yml.j2 +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_builder_identity.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_builder_runs.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_calibration_effect_report.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_calibration_truth.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_checks.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_claude_cli_bounce.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_clear_stale.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_cloud_client.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_cloud_git_metadata.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_cloud_manifest.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_cloud_reports.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_coderabbit_cli_audit_pr.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_codex_audit_env_preflight.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_doctor_actions_cost_summary.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_doctor_actions_failure_selection.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_doctor_boundaries.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_doctor_github_checks.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_doctor_output.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_doctor_privacy.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_doctor_provider_env_required.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_doctor_provider_env_tokens.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_doctor_provider_probe.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_doctor_provider_review_hygiene.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_doctor_registry.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_local_llm_github_helpers.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_plan_context.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_provider_local_cli.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_provider_runners_comments.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_provider_runners_git.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_provider_runners_github_pr.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_provider_runners_process.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_provider_runners_repo_paths.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_provider_runners_text_schema.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_provider_runners_workspace.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/tests/test_reviewer_spend.py +0 -0
- {code_mower-0.5.0b41 → code_mower-0.5.0b42}/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.0b42
|
|
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.0b42
|
|
107
107
|
code-mower --version
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
`0.5.
|
|
110
|
+
`0.5.0b42` 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.0b42`.
|
|
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.42` 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.0b42` 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.0b42
|
|
92
92
|
code-mower --version
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
`0.5.
|
|
95
|
+
`0.5.0b42` 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.0b42`.
|
|
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.42` 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.0b42` 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
|
|
|
@@ -17,12 +17,16 @@ import urllib.error
|
|
|
17
17
|
import urllib.request
|
|
18
18
|
from dataclasses import dataclass
|
|
19
19
|
from pathlib import Path
|
|
20
|
-
from typing import Any, Dict, Mapping, Optional, Pattern, Sequence
|
|
20
|
+
from typing import Any, Callable, Dict, Mapping, Optional, Pattern, Sequence
|
|
21
21
|
from urllib.parse import quote
|
|
22
22
|
import re
|
|
23
23
|
|
|
24
24
|
MIN_ABBREVIATED_SHA_LENGTH = 7
|
|
25
25
|
AUTHOR_EXCLUSION_ENV = "CODE_MOWER_AUTHOR_EXCLUSION_JSON"
|
|
26
|
+
ACTIONS_RUN_MARKER_RE = re.compile(
|
|
27
|
+
r"<!--\s*CODE_MOWER_AUDIT_RUN:\s*run_id=([0-9]+)\s*-->"
|
|
28
|
+
)
|
|
29
|
+
TRUSTED_ACTIONS_AUDIT_EVENTS = frozenset({"pull_request_target"})
|
|
26
30
|
|
|
27
31
|
|
|
28
32
|
@dataclass(frozen=True)
|
|
@@ -291,6 +295,130 @@ def github_request_with_fallback(
|
|
|
291
295
|
raise RuntimeError(f"no GitHub tokens available for {method} {path}")
|
|
292
296
|
|
|
293
297
|
|
|
298
|
+
def parse_csv_set(raw: str) -> frozenset[str]:
|
|
299
|
+
return frozenset(item.strip() for item in raw.split(",") if item.strip())
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def parse_audit_run_id(body: str) -> str:
|
|
303
|
+
match = ACTIONS_RUN_MARKER_RE.search(body)
|
|
304
|
+
return match.group(1) if match else ""
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def workflow_path_matches(path: str, trusted_workflows: Sequence[str]) -> bool:
|
|
308
|
+
if not path:
|
|
309
|
+
return False
|
|
310
|
+
return any(path == item or path.endswith("/" + item) for item in trusted_workflows)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _pr_item_matches_head(
|
|
314
|
+
item: Mapping[str, Any],
|
|
315
|
+
*,
|
|
316
|
+
issue_number: int,
|
|
317
|
+
head_sha: str,
|
|
318
|
+
fallback_head_sha: str = "",
|
|
319
|
+
) -> bool:
|
|
320
|
+
if str(item.get("number") or "") != str(issue_number):
|
|
321
|
+
return False
|
|
322
|
+
pr_head = str(((item.get("head") or {}).get("sha")) or "")
|
|
323
|
+
if pr_head:
|
|
324
|
+
return sha_matches(pr_head, head_sha)
|
|
325
|
+
return bool(fallback_head_sha) and sha_matches(fallback_head_sha, head_sha)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def fetch_actions_run(
|
|
329
|
+
repo: str,
|
|
330
|
+
run_id: str,
|
|
331
|
+
*,
|
|
332
|
+
tokens: Sequence[GitHubToken],
|
|
333
|
+
) -> dict[str, Any]:
|
|
334
|
+
response = github_request_with_fallback(
|
|
335
|
+
"GET",
|
|
336
|
+
f"/repos/{repo}/actions/runs/{run_id}",
|
|
337
|
+
tokens=tokens,
|
|
338
|
+
)
|
|
339
|
+
return response if isinstance(response, dict) else {}
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def fetch_pull_requests_for_commit(
|
|
343
|
+
repo: str,
|
|
344
|
+
head_sha: str,
|
|
345
|
+
*,
|
|
346
|
+
tokens: Sequence[GitHubToken],
|
|
347
|
+
) -> list[dict[str, Any]]:
|
|
348
|
+
response = github_request_with_fallback(
|
|
349
|
+
"GET",
|
|
350
|
+
f"/repos/{repo}/commits/{head_sha}/pulls?per_page=100",
|
|
351
|
+
tokens=tokens,
|
|
352
|
+
)
|
|
353
|
+
return response if isinstance(response, list) else []
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def github_actions_comment_attested(
|
|
357
|
+
*,
|
|
358
|
+
repo: str,
|
|
359
|
+
body: str,
|
|
360
|
+
issue_number: int,
|
|
361
|
+
head_sha: str,
|
|
362
|
+
workflow_paths: Sequence[str],
|
|
363
|
+
tokens: Sequence[GitHubToken],
|
|
364
|
+
actions_run_lookup: Optional[Callable[[str], Mapping[str, Any]]] = None,
|
|
365
|
+
commit_pull_requests_lookup: Optional[Callable[[str], Sequence[Mapping[str, Any]]]] = None,
|
|
366
|
+
) -> bool:
|
|
367
|
+
"""Return whether a github-actions[bot] audit comment is tied to this PR head."""
|
|
368
|
+
trusted_workflows = tuple(parse_csv_set(",".join(workflow_paths)))
|
|
369
|
+
token_list = tuple(token for token in tokens if token.value)
|
|
370
|
+
run_id = parse_audit_run_id(body)
|
|
371
|
+
if not trusted_workflows or not token_list or not run_id or not head_sha:
|
|
372
|
+
return False
|
|
373
|
+
try:
|
|
374
|
+
if actions_run_lookup is None:
|
|
375
|
+
run = fetch_actions_run(repo, run_id, tokens=token_list)
|
|
376
|
+
else:
|
|
377
|
+
run = dict(actions_run_lookup(run_id))
|
|
378
|
+
except Exception:
|
|
379
|
+
return False
|
|
380
|
+
|
|
381
|
+
path = str(run.get("path") or "")
|
|
382
|
+
if not workflow_path_matches(path, trusted_workflows):
|
|
383
|
+
return False
|
|
384
|
+
if str(run.get("event") or "") not in TRUSTED_ACTIONS_AUDIT_EVENTS:
|
|
385
|
+
return False
|
|
386
|
+
|
|
387
|
+
run_head_sha = str(run.get("head_sha") or "")
|
|
388
|
+
pull_requests = run.get("pull_requests")
|
|
389
|
+
if isinstance(pull_requests, list) and pull_requests:
|
|
390
|
+
return any(
|
|
391
|
+
isinstance(item, Mapping)
|
|
392
|
+
and _pr_item_matches_head(
|
|
393
|
+
item,
|
|
394
|
+
issue_number=issue_number,
|
|
395
|
+
head_sha=head_sha,
|
|
396
|
+
fallback_head_sha=run_head_sha,
|
|
397
|
+
)
|
|
398
|
+
for item in pull_requests
|
|
399
|
+
)
|
|
400
|
+
if not run_head_sha or not sha_matches(run_head_sha, head_sha):
|
|
401
|
+
return False
|
|
402
|
+
|
|
403
|
+
try:
|
|
404
|
+
if commit_pull_requests_lookup is None:
|
|
405
|
+
associated_prs = fetch_pull_requests_for_commit(repo, run_head_sha, tokens=token_list)
|
|
406
|
+
else:
|
|
407
|
+
associated_prs = commit_pull_requests_lookup(run_head_sha)
|
|
408
|
+
except Exception:
|
|
409
|
+
return False
|
|
410
|
+
return any(
|
|
411
|
+
isinstance(item, Mapping)
|
|
412
|
+
and _pr_item_matches_head(
|
|
413
|
+
item,
|
|
414
|
+
issue_number=issue_number,
|
|
415
|
+
head_sha=head_sha,
|
|
416
|
+
fallback_head_sha=run_head_sha,
|
|
417
|
+
)
|
|
418
|
+
for item in associated_prs
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
|
|
294
422
|
def fetch_pull_request(
|
|
295
423
|
repo: str,
|
|
296
424
|
issue_number: int,
|
|
@@ -36,6 +36,7 @@ if __package__ in {None, "", "tools"}:
|
|
|
36
36
|
fetch_pull_request,
|
|
37
37
|
fetch_base_ref_sha as _shared_fetch_base_ref_sha,
|
|
38
38
|
fetch_pr_head_sha as _shared_fetch_pr_head_sha,
|
|
39
|
+
fetch_pr_head_sha_unless_local_matches as _shared_fetch_pr_head_sha_unless_local_matches,
|
|
39
40
|
limit_comment_body,
|
|
40
41
|
one_line as _one_line,
|
|
41
42
|
parse_repo_paths as _parse_repo_paths,
|
|
@@ -61,6 +62,7 @@ if __package__ in {None, "", "tools"}:
|
|
|
61
62
|
fetch_pull_request,
|
|
62
63
|
fetch_base_ref_sha as _shared_fetch_base_ref_sha,
|
|
63
64
|
fetch_pr_head_sha as _shared_fetch_pr_head_sha,
|
|
65
|
+
fetch_pr_head_sha_unless_local_matches as _shared_fetch_pr_head_sha_unless_local_matches,
|
|
64
66
|
limit_comment_body,
|
|
65
67
|
one_line as _one_line,
|
|
66
68
|
parse_repo_paths as _parse_repo_paths,
|
|
@@ -84,6 +86,7 @@ else: # pragma: no cover - exercised after package extraction.
|
|
|
84
86
|
fetch_base_ref_sha as _shared_fetch_base_ref_sha,
|
|
85
87
|
fetch_pull_request,
|
|
86
88
|
fetch_pr_head_sha as _shared_fetch_pr_head_sha,
|
|
89
|
+
fetch_pr_head_sha_unless_local_matches as _shared_fetch_pr_head_sha_unless_local_matches,
|
|
87
90
|
limit_comment_body,
|
|
88
91
|
one_line as _one_line,
|
|
89
92
|
parse_repo_paths as _parse_repo_paths,
|
|
@@ -113,6 +116,7 @@ MAX_FINDING_DETAIL_CHARS = 4_000
|
|
|
113
116
|
STALE_TRAILER = "<!-- CLAUDE_AUDIT_STATE: needs-claude-audit -->"
|
|
114
117
|
DONE_TRAILER = "<!-- CLAUDE_AUDIT_STATE: claude-audit-done -->"
|
|
115
118
|
BLOCKED_TRAILER = "<!-- CLAUDE_AUDIT_STATE: claude-audit-blocked -->"
|
|
119
|
+
AUDIT_RUN_TRAILER_PREFIX = "<!-- CODE_MOWER_AUDIT_RUN:"
|
|
116
120
|
|
|
117
121
|
|
|
118
122
|
CLAUDE_VERDICT_SCHEMA: Dict[str, Any] = {
|
|
@@ -387,7 +391,17 @@ def _fetch_base_sha_for_diff(local_repo: Path, base_ref: str) -> str:
|
|
|
387
391
|
return _shared_fetch_base_ref_sha(local_repo, base_ref)
|
|
388
392
|
|
|
389
393
|
|
|
390
|
-
def _fetch_pr_head_sha_for_diff(
|
|
394
|
+
def _fetch_pr_head_sha_for_diff(
|
|
395
|
+
local_repo: Path,
|
|
396
|
+
pr_number: int,
|
|
397
|
+
expected_head_sha: str | None = None,
|
|
398
|
+
) -> str:
|
|
399
|
+
if expected_head_sha:
|
|
400
|
+
return _shared_fetch_pr_head_sha_unless_local_matches(
|
|
401
|
+
local_repo,
|
|
402
|
+
pr_number,
|
|
403
|
+
expected_head_sha=expected_head_sha,
|
|
404
|
+
)
|
|
391
405
|
return _shared_fetch_pr_head_sha(local_repo, pr_number)
|
|
392
406
|
|
|
393
407
|
|
|
@@ -482,7 +496,11 @@ def _build_diff_context(
|
|
|
482
496
|
)
|
|
483
497
|
|
|
484
498
|
fetched_base_ref = _fetch_base_sha_for_diff(local_repo, base_ref)
|
|
485
|
-
fetched_head_ref = _fetch_pr_head_sha_for_diff(
|
|
499
|
+
fetched_head_ref = _fetch_pr_head_sha_for_diff(
|
|
500
|
+
local_repo,
|
|
501
|
+
pr_number,
|
|
502
|
+
expected_head_sha=expected_head_sha,
|
|
503
|
+
)
|
|
486
504
|
if fetched_head_ref.lower() != expected_head_sha.lower():
|
|
487
505
|
raise FetchedHeadMismatch(expected_head_sha, fetched_head_ref)
|
|
488
506
|
diff_range = f"{fetched_base_ref}...{fetched_head_ref}"
|
|
@@ -705,10 +723,13 @@ def format_comment(
|
|
|
705
723
|
stale_end_sha: Optional[str] = None,
|
|
706
724
|
is_unknown: bool = False,
|
|
707
725
|
merge_authority: bool = True,
|
|
726
|
+
actions_run_id: Optional[str] = None,
|
|
708
727
|
) -> str:
|
|
709
728
|
posture = "merge-authority lane" if merge_authority else "informational only"
|
|
710
729
|
header = f"## Claude audit ({posture})\n\n"
|
|
711
730
|
header += f"Head SHA: `{head_sha}`\n"
|
|
731
|
+
if actions_run_id:
|
|
732
|
+
header += f"{AUDIT_RUN_TRAILER_PREFIX} run_id={actions_run_id} -->\n"
|
|
712
733
|
if is_stale:
|
|
713
734
|
body = (
|
|
714
735
|
header
|
|
@@ -916,6 +937,7 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
|
|
|
916
937
|
pr_meta_after = fetch_pull_request(repo, pr_number, token=config.github_token)
|
|
917
938
|
head_sha_end = pr_meta_after["head"]["sha"]
|
|
918
939
|
is_stale = head_sha_start != head_sha_end
|
|
940
|
+
actions_run_id = os.environ.get("GITHUB_RUN_ID") or None
|
|
919
941
|
|
|
920
942
|
if is_stale:
|
|
921
943
|
comment_body = format_comment(
|
|
@@ -924,6 +946,7 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
|
|
|
924
946
|
is_stale=True,
|
|
925
947
|
stale_end_sha=head_sha_end,
|
|
926
948
|
merge_authority=config.merge_authority,
|
|
949
|
+
actions_run_id=actions_run_id,
|
|
927
950
|
)
|
|
928
951
|
result_verdict = "STALE"
|
|
929
952
|
trailer = STALE_TRAILER
|
|
@@ -933,6 +956,7 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
|
|
|
933
956
|
head_sha_start,
|
|
934
957
|
is_unknown=True,
|
|
935
958
|
merge_authority=config.merge_authority,
|
|
959
|
+
actions_run_id=actions_run_id,
|
|
936
960
|
)
|
|
937
961
|
result_verdict = "UNKNOWN"
|
|
938
962
|
trailer = STALE_TRAILER
|
|
@@ -941,6 +965,7 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
|
|
|
941
965
|
parsed,
|
|
942
966
|
head_sha_start,
|
|
943
967
|
merge_authority=config.merge_authority,
|
|
968
|
+
actions_run_id=actions_run_id,
|
|
944
969
|
)
|
|
945
970
|
result_verdict = parsed.verdict
|
|
946
971
|
trailer = BLOCKED_TRAILER if parsed.verdict == "BLOCKED" else DONE_TRAILER
|
|
@@ -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.0b42`, or run source checkouts with "
|
|
18
18
|
"`PYTHONPATH=src python -m code_mower.cli`."
|
|
19
19
|
)
|
|
20
20
|
|
|
@@ -418,6 +418,12 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
418
418
|
default=MAX_EVENT_COUNT,
|
|
419
419
|
help=f"number of verdict artifacts to include; max {MAX_EVENT_COUNT}",
|
|
420
420
|
)
|
|
421
|
+
reviewer_runs.add_argument(
|
|
422
|
+
"--offset",
|
|
423
|
+
type=int,
|
|
424
|
+
default=0,
|
|
425
|
+
help="skip this many newest matching verdict artifacts before applying --limit",
|
|
426
|
+
)
|
|
421
427
|
reviewer_runs.add_argument(
|
|
422
428
|
"--include-git-ref",
|
|
423
429
|
action="store_true",
|
|
@@ -674,6 +680,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
674
680
|
team_id=args.team_id,
|
|
675
681
|
install_id=args.install_id,
|
|
676
682
|
limit=args.limit,
|
|
683
|
+
offset=args.offset,
|
|
677
684
|
endpoint=args.endpoint,
|
|
678
685
|
token_env=args.token_env,
|
|
679
686
|
yes=args.yes,
|
|
@@ -315,6 +315,7 @@ def reviewer_runs_upload(
|
|
|
315
315
|
team_id: str,
|
|
316
316
|
install_id: str,
|
|
317
317
|
limit: int,
|
|
318
|
+
offset: int = 0,
|
|
318
319
|
endpoint: str,
|
|
319
320
|
token_env: str,
|
|
320
321
|
yes: bool,
|
|
@@ -323,6 +324,8 @@ def reviewer_runs_upload(
|
|
|
323
324
|
) -> dict[str, Any]:
|
|
324
325
|
if limit < 1 or limit > MAX_EVENT_COUNT:
|
|
325
326
|
raise CloudBundleError(f"--limit must be between 1 and {MAX_EVENT_COUNT}")
|
|
327
|
+
if offset < 0:
|
|
328
|
+
raise CloudBundleError("--offset must be non-negative")
|
|
326
329
|
repo_path = repo_path.expanduser().resolve()
|
|
327
330
|
detected_repo_slug = repo_slug or detect_repo_slug(repo_path)
|
|
328
331
|
if not detected_repo_slug:
|
|
@@ -336,6 +339,7 @@ def reviewer_runs_upload(
|
|
|
336
339
|
verdicts,
|
|
337
340
|
repo=detected_repo_slug,
|
|
338
341
|
limit=limit,
|
|
342
|
+
offset=offset,
|
|
339
343
|
include_git_ref=include_git_ref,
|
|
340
344
|
)
|
|
341
345
|
except ValueError as exc:
|
|
@@ -348,6 +352,7 @@ def reviewer_runs_upload(
|
|
|
348
352
|
"event_count": 0,
|
|
349
353
|
"verdicts": str(verdicts.expanduser()),
|
|
350
354
|
"git_ref_included": include_git_ref,
|
|
355
|
+
"offset": offset,
|
|
351
356
|
}
|
|
352
357
|
export_result = build_cloud_bundle(
|
|
353
358
|
reports=[],
|
|
@@ -370,6 +375,7 @@ def reviewer_runs_upload(
|
|
|
370
375
|
"status": "doctor_failed",
|
|
371
376
|
"repo_slug": detected_repo_slug,
|
|
372
377
|
"event_count": len(events),
|
|
378
|
+
"offset": offset,
|
|
373
379
|
"export": export_result,
|
|
374
380
|
"doctor": doctor_result,
|
|
375
381
|
}
|
|
@@ -380,6 +386,7 @@ def reviewer_runs_upload(
|
|
|
380
386
|
"status": "dry_run",
|
|
381
387
|
"repo_slug": detected_repo_slug,
|
|
382
388
|
"event_count": len(events),
|
|
389
|
+
"offset": offset,
|
|
383
390
|
"export": export_result,
|
|
384
391
|
"doctor": doctor_result,
|
|
385
392
|
"upload": build_dogfood_dry_run_preview(endpoint=endpoint, payload=payload),
|
|
@@ -394,6 +401,7 @@ def reviewer_runs_upload(
|
|
|
394
401
|
"status": "uploaded",
|
|
395
402
|
"repo_slug": detected_repo_slug,
|
|
396
403
|
"event_count": len(events),
|
|
404
|
+
"offset": offset,
|
|
397
405
|
"export": export_result,
|
|
398
406
|
"doctor": doctor_result,
|
|
399
407
|
"upload": post_upload_payload(
|
|
@@ -233,8 +233,11 @@ def export_reviewer_run_events_from_verdicts(
|
|
|
233
233
|
*,
|
|
234
234
|
repo: str = "",
|
|
235
235
|
limit: int = 0,
|
|
236
|
+
offset: int = 0,
|
|
236
237
|
include_git_ref: bool = False,
|
|
237
238
|
) -> list[dict[str, Any]]:
|
|
239
|
+
if offset < 0:
|
|
240
|
+
raise ValueError("offset must be non-negative")
|
|
238
241
|
repo_filter = _normalize_repo_slug(repo) if repo else ""
|
|
239
242
|
events: list[dict[str, Any]] = []
|
|
240
243
|
for path in _iter_verdict_artifact_paths(root.expanduser()):
|
|
@@ -262,7 +265,8 @@ def export_reviewer_run_events_from_verdicts(
|
|
|
262
265
|
key=lambda event: (str(event.get("created_at") or ""), str(event.get("event_id") or "")),
|
|
263
266
|
reverse=True,
|
|
264
267
|
)
|
|
265
|
-
|
|
268
|
+
selected = events[offset:] if offset else events
|
|
269
|
+
return selected[:limit] if limit else selected
|
|
266
270
|
|
|
267
271
|
|
|
268
272
|
def load_jsonl_events(path: Path) -> list[dict[str, Any]]:
|
|
@@ -17,8 +17,10 @@ Pipeline per audit:
|
|
|
17
17
|
`CODEX_AUDIT_REPO_PATHS` (the CLI requires a real git checkout for
|
|
18
18
|
the built-in review).
|
|
19
19
|
2. Fetch the PR head SHA via the GitHub API.
|
|
20
|
-
3.
|
|
21
|
-
the
|
|
20
|
+
3. Ensure the PR head commit is locally available: skip the PR-head fetch
|
|
21
|
+
when the configured checkout already matches the GitHub API head SHA,
|
|
22
|
+
otherwise `git fetch origin pull/<N>/head`; then create a temporary
|
|
23
|
+
worktree at the head SHA (detached, so we don't pollute branch namespace).
|
|
22
24
|
4. Run `codex exec --ignore-user-config --sandbox read-only review
|
|
23
25
|
--base origin/main` from that worktree, capturing the final review
|
|
24
26
|
prose via `--output-last-message`.
|
|
@@ -91,6 +93,7 @@ if __package__ in {None, "", "tools"}:
|
|
|
91
93
|
fetch_pull_request,
|
|
92
94
|
fetch_base_ref as _shared_fetch_base_ref,
|
|
93
95
|
fetch_pr_head as _shared_fetch_pr_head,
|
|
96
|
+
fetch_pr_head_unless_local_matches as _shared_fetch_pr_head_unless_local_matches,
|
|
94
97
|
limit_comment_body,
|
|
95
98
|
one_line as _one_line,
|
|
96
99
|
parse_repo_paths as _parse_repo_paths,
|
|
@@ -113,6 +116,7 @@ if __package__ in {None, "", "tools"}:
|
|
|
113
116
|
fetch_pull_request,
|
|
114
117
|
fetch_base_ref as _shared_fetch_base_ref,
|
|
115
118
|
fetch_pr_head as _shared_fetch_pr_head,
|
|
119
|
+
fetch_pr_head_unless_local_matches as _shared_fetch_pr_head_unless_local_matches,
|
|
116
120
|
limit_comment_body,
|
|
117
121
|
one_line as _one_line,
|
|
118
122
|
parse_repo_paths as _parse_repo_paths,
|
|
@@ -136,6 +140,7 @@ else: # pragma: no cover - exercised after package extraction.
|
|
|
136
140
|
fetch_base_ref as _shared_fetch_base_ref,
|
|
137
141
|
fetch_pull_request,
|
|
138
142
|
fetch_pr_head as _shared_fetch_pr_head,
|
|
143
|
+
fetch_pr_head_unless_local_matches as _shared_fetch_pr_head_unless_local_matches,
|
|
139
144
|
limit_comment_body,
|
|
140
145
|
one_line as _one_line,
|
|
141
146
|
parse_repo_paths as _parse_repo_paths,
|
|
@@ -305,6 +310,7 @@ class ReviewContextDiagnostics:
|
|
|
305
310
|
STALE_TRAILER = "<!-- CODEX_AUDIT_STATE: needs-codex-audit -->"
|
|
306
311
|
DONE_TRAILER = "<!-- CODEX_AUDIT_STATE: codex-audit-done -->"
|
|
307
312
|
BLOCKED_TRAILER = "<!-- CODEX_AUDIT_STATE: codex-audit-blocked -->"
|
|
313
|
+
AUDIT_RUN_TRAILER_PREFIX = "<!-- CODE_MOWER_AUDIT_RUN:"
|
|
308
314
|
|
|
309
315
|
|
|
310
316
|
# ----- Structured verdict validation -----
|
|
@@ -654,8 +660,19 @@ def _remove_worktree(local_repo: Path, worktree_path: Path) -> None:
|
|
|
654
660
|
_shared_remove_worktree(local_repo, worktree_path)
|
|
655
661
|
|
|
656
662
|
|
|
657
|
-
def _fetch_pr_head(
|
|
663
|
+
def _fetch_pr_head(
|
|
664
|
+
local_repo: Path,
|
|
665
|
+
pr_number: int,
|
|
666
|
+
expected_head_sha: str | None = None,
|
|
667
|
+
) -> None:
|
|
658
668
|
"""Ensure the PR's head commit is locally available."""
|
|
669
|
+
if expected_head_sha:
|
|
670
|
+
_shared_fetch_pr_head_unless_local_matches(
|
|
671
|
+
local_repo,
|
|
672
|
+
pr_number,
|
|
673
|
+
expected_head_sha=expected_head_sha,
|
|
674
|
+
)
|
|
675
|
+
return
|
|
659
676
|
_shared_fetch_pr_head(local_repo, pr_number)
|
|
660
677
|
|
|
661
678
|
|
|
@@ -1172,11 +1189,14 @@ def format_comment(
|
|
|
1172
1189
|
stale_end_sha: Optional[str] = None,
|
|
1173
1190
|
is_unknown: bool = False,
|
|
1174
1191
|
merge_authority: bool = True,
|
|
1192
|
+
actions_run_id: Optional[str] = None,
|
|
1175
1193
|
) -> str:
|
|
1176
1194
|
"""Build the GitHub comment body with header, prose, and trailer."""
|
|
1177
1195
|
posture = "merge-authority lane" if merge_authority else "informational only"
|
|
1178
1196
|
header = f"## Codex audit ({posture})\n\n"
|
|
1179
1197
|
header += f"Head SHA: `{head_sha}`\n"
|
|
1198
|
+
if actions_run_id:
|
|
1199
|
+
header += f"{AUDIT_RUN_TRAILER_PREFIX} run_id={actions_run_id} -->\n"
|
|
1180
1200
|
if is_stale:
|
|
1181
1201
|
body = (
|
|
1182
1202
|
header
|
|
@@ -1293,7 +1313,7 @@ def audit_pr(config: AuditConfig, repo: str, pr_number: int) -> AuditResult:
|
|
|
1293
1313
|
|
|
1294
1314
|
# Ensure both the PR head AND the base ref are locally fetched
|
|
1295
1315
|
# before running the review. Stale base = wrong diff = wrong review.
|
|
1296
|
-
_fetch_pr_head(local_repo, pr_number)
|
|
1316
|
+
_fetch_pr_head(local_repo, pr_number, head_sha_start)
|
|
1297
1317
|
_fetch_base_ref(local_repo, config.base_ref)
|
|
1298
1318
|
review_context_summary = "review context diagnostics unavailable"
|
|
1299
1319
|
try:
|
|
@@ -1455,6 +1475,7 @@ def audit_pr(config: AuditConfig, repo: str, pr_number: int) -> AuditResult:
|
|
|
1455
1475
|
pr_meta_after = fetch_pull_request(repo, pr_number, token=config.github_token)
|
|
1456
1476
|
head_sha_end = pr_meta_after["head"]["sha"]
|
|
1457
1477
|
is_stale = head_sha_start != head_sha_end
|
|
1478
|
+
actions_run_id = os.environ.get("GITHUB_RUN_ID") or None
|
|
1458
1479
|
|
|
1459
1480
|
if is_stale:
|
|
1460
1481
|
comment_body = format_comment(
|
|
@@ -1463,6 +1484,7 @@ def audit_pr(config: AuditConfig, repo: str, pr_number: int) -> AuditResult:
|
|
|
1463
1484
|
is_stale=True,
|
|
1464
1485
|
stale_end_sha=head_sha_end,
|
|
1465
1486
|
merge_authority=config.merge_authority,
|
|
1487
|
+
actions_run_id=actions_run_id,
|
|
1466
1488
|
)
|
|
1467
1489
|
result_verdict = "STALE"
|
|
1468
1490
|
trailer = STALE_TRAILER
|
|
@@ -1497,6 +1519,7 @@ def audit_pr(config: AuditConfig, repo: str, pr_number: int) -> AuditResult:
|
|
|
1497
1519
|
head_sha_start,
|
|
1498
1520
|
is_unknown=True,
|
|
1499
1521
|
merge_authority=config.merge_authority,
|
|
1522
|
+
actions_run_id=actions_run_id,
|
|
1500
1523
|
)
|
|
1501
1524
|
result_verdict = "UNKNOWN"
|
|
1502
1525
|
trailer = STALE_TRAILER
|
|
@@ -1505,6 +1528,7 @@ def audit_pr(config: AuditConfig, repo: str, pr_number: int) -> AuditResult:
|
|
|
1505
1528
|
parsed,
|
|
1506
1529
|
head_sha_start,
|
|
1507
1530
|
merge_authority=config.merge_authority,
|
|
1531
|
+
actions_run_id=actions_run_id,
|
|
1508
1532
|
)
|
|
1509
1533
|
result_verdict = parsed.verdict
|
|
1510
1534
|
trailer = BLOCKED_TRAILER if parsed.verdict == "BLOCKED" else DONE_TRAILER
|