code-mower 0.5.0b42__tar.gz → 0.5.0b44__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.0b42 → code_mower-0.5.0b44}/PKG-INFO +6 -6
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/README.md +5 -5
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/pyproject.toml +1 -1
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/__init__.py +1 -1
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/calibration/auto_discovery.py +3 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/claude_audit_pr.py +286 -17
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/cli.py +1 -1
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/code_mower_telemetry.py +10 -2
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/codex_audit_pr.py +95 -9
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/provider_runners/__init__.py +10 -0
- code_mower-0.5.0b44/src/code_mower/provider_runners/verdict_artifacts.py +241 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/builder-provenance.yml.j2 +1 -1
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower.egg-info/PKG-INFO +6 -6
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower.egg-info/SOURCES.txt +2 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_calibration_truth.py +48 -0
- code_mower-0.5.0b44/tests/test_claude_audit_pr.py +319 -0
- code_mower-0.5.0b44/tests/test_codex_audit_pr.py +166 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_release_hygiene.py +34 -34
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_reviewer_metrics_core.py +93 -0
- code_mower-0.5.0b42/src/code_mower/provider_runners/verdict_artifacts.py +0 -103
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/CODE_OF_CONDUCT.md +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/LICENSE +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/MANIFEST.in +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/NOTICE +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/SUPPORT.md +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/requirements/requirements.txt +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/setup.cfg +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/adapters/__init__.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/adapters/_base.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/adapters/cursor_bugbot.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/adapters/gitar.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/adapters/greptile.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/adapters/qodo.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/antigravity_cli_audit_pr.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/audit_handoff_log.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/audit_labeler_lib.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/audit_progress.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/blind_review_artifacts.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/blind_review_coordinator.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/bootstrap.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/builder_experiment.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/builder_runs.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/calibration/__init__.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/calibration/arms.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/calibration/commands.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/calibration/context_inputs.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/calibration/corpus.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/calibration/effect_report.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/calibration/evidence.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/calibration/evidence_report.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/calibration/identity.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/calibration/metrics.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/calibration/overlap.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/calibration/planning.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/calibration/policy.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/calibration/results.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/calibration/run_results.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/calibration/run_status.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/calibration/runner.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/calibration/truth.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/calibration/value_report.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/checks.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/claude_cli_bounce.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/claude_cli_environment.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/clear_stale.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/cloud.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/cloud_client/__init__.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/cloud_client/bundle.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/cloud_client/doctor.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/cloud_client/dogfood.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/cloud_client/endpoints.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/cloud_client/errors.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/cloud_client/events.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/cloud_client/export.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/cloud_client/git_metadata.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/cloud_client/manifest.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/cloud_client/operations.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/cloud_client/reports.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/cloud_client/setup.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/cloud_client/upload.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/code_mower_calibration.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/code_mower_context_packs.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/code_mower_merge.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/coderabbit_cli_audit_pr.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/codex_audit_env_preflight.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/codex_audit_schema_smoke.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/codex_audit_verdict.schema.json +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/config.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/__init__.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/cloud.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/common.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_actions.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_actions_cost.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_actions_cost_summary.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_actions_failure_annotations.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_actions_failure_models.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_actions_failure_scan.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_actions_failure_selection.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_actions_failures.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_actions_permissions.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_api.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_branch.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_config.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_provider.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_repo.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/groups.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/models.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/output.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/presets.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/privacy.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_api_model.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_api_model_openai.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_api_model_profiles.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_env.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_env_required.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_env_tokens.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_local_cli.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_local_cli_commands.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_local_cli_probe_config.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_probe.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_probe_auth.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_probe_evaluation.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_probe_json.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_probe_remediation.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_review_hygiene.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/providers.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/registry.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/runner.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/runtime.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/runtime_github_auth.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/gemini_cli_audit_pr.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/grok_build_audit_pr.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/hermes_cli_audit_pr.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/init.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/lane_configs/__init__.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/lane_configs/aider.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/lane_configs/antigravity_cli.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/lane_configs/claude.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/lane_configs/codex.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/lane_configs/devin.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/lane_configs/gemini_cli.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/lane_configs/grok_build.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/lane_configs/hermes_cli.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/lane_configs/local_llm.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/local_llm_audit_pr.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/local_llm_bakeoff.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/local_llm_calibration.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/local_llm_profiles.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/migration.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/migration_install.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/migration_mirror.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/migration_readiness.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/migration_rehearsal.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/next_steps.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/package.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/package_content.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/package_manifest.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/package_paths.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/package_rendering.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/package_static.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/plan_context.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/prompts.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/provider_registry.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/provider_runners/comments.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/provider_runners/git.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/provider_runners/github_auth.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/provider_runners/github_pr.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/provider_runners/pr_worktree.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/provider_runners/process.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/provider_runners/repo_paths.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/provider_runners/text_schema.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/provider_runners/workspace.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/providers/__init__.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/providers/local_cli.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/providers/provenance.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/release_readiness.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/reviewer_metrics.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/reviewer_spend.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/saas_reviewer_labeler.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/secrets.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/builder-experiment.example.json +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/calibration-corpus.example.json +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/calibration-corpus.json +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/code-mower.example.yml +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/context-packs.example.json +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/lane_prompts/base-audit.md +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/lane_prompts/calibration-policy.md +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/lane_prompts/context-driven-quality.md +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/lane_prompts/docs-design.md +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/lane_prompts/generic-programming.md +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/lane_prompts/operability.md +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/lane_prompts/package-runtime.md +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/lane_prompts/plan-conformance.md +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/lane_prompts/security-threat-model.md +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/product-support/code_mower +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/product-support/code_mower_standalone_pin.env +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/product-support/code_mower_standalone_shadow.sh +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/product-support/run_claude_audit_pr.sh +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/product-support/run_codex_audit_pr.sh +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/product-support/safe_gh_comment.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/product-support/status_report.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/providers.yml +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/reviewer-spend.example.json +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/reviewer-value-report.example.md +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/audit-label-cleanup.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/code-mower-gate-health.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/code-mower-gate.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/hosted-bridge.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/local-cli-audit.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/needs-owner-notify.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/private-standalone-shadow.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/review-clear-stale.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/saas-reviewer-labeler.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/self-hosted-local-audit.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/trailer-comment-labeler.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/weekly-status.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/trailer_comment_labeler.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/versioning.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower/work_orders.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower.egg-info/dependency_links.txt +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower.egg-info/entry_points.txt +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower.egg-info/requires.txt +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/src/code_mower.egg-info/top_level.txt +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/builder-experiment.example.json +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/calibration-corpus.example.json +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/calibration-corpus.json +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/code-mower.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/context-packs.example.json +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/cursor/BUGBOT.md +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/providers/acp_bridge.yml +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/providers/aider.yml +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/providers/antigravity_cli.yml +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/providers/claude_audit.yml +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/providers/claude_review.yml +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/providers/coderabbit_cli.yml +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/providers/codex.yml +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/providers/cursor_bugbot.yml +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/providers/devin.yml +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/providers/gemini_cli.yml +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/providers/gitar.yml +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/providers/greptile.yml +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/providers/hermes_cli.yml +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/providers/local_llm.yml +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/providers/qodo.yml +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/providers.yml +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/reviewer-spend.example.json +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/reviewer-value-report.example.md +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/workflows/audit-label-cleanup.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/workflows/blind-review-artifacts-dry-run.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/workflows/builder-provenance.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/workflows/code-mower-gate-health.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/workflows/code-mower-gate.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/workflows/hosted-bridge.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/workflows/local-cli-audit.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/workflows/private-standalone-shadow.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/workflows/review-clear-stale.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/workflows/saas-reviewer-labeler.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/workflows/self-hosted-local-audit.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/templates/workflows/trailer-comment-labeler.yml.j2 +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_audit_comment_posture.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_builder_identity.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_builder_runs.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_calibration_effect_report.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_checks.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_claude_cli_bounce.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_clear_stale.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_cloud_client.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_cloud_git_metadata.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_cloud_manifest.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_cloud_reports.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_coderabbit_cli_audit_pr.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_codex_audit_env_preflight.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_doctor_actions_cost_summary.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_doctor_actions_failure_selection.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_doctor_boundaries.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_doctor_github_checks.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_doctor_output.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_doctor_privacy.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_doctor_provider_env_required.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_doctor_provider_env_tokens.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_doctor_provider_probe.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_doctor_provider_review_hygiene.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_doctor_registry.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_local_llm_github_helpers.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_plan_context.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_provider_local_cli.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_provider_runners_comments.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_provider_runners_git.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_provider_runners_github_pr.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_provider_runners_pr_worktree.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_provider_runners_process.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_provider_runners_repo_paths.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_provider_runners_text_schema.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_provider_runners_workspace.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_reviewer_spend.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/tests/test_trailer_comment_labeler.py +0 -0
- {code_mower-0.5.0b42 → code_mower-0.5.0b44}/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.0b44
|
|
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.0b44
|
|
107
107
|
code-mower --version
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
`0.5.
|
|
110
|
+
`0.5.0b44` 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.0b44`.
|
|
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.44` 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.0b44` 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.0b44
|
|
92
92
|
code-mower --version
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
`0.5.
|
|
95
|
+
`0.5.0b44` 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.0b44`.
|
|
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.44` 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.0b44` 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
|
|
|
@@ -12,6 +12,7 @@ from typing import Any, Mapping, Sequence
|
|
|
12
12
|
from .corpus import parse_int
|
|
13
13
|
from .identity import safe_slug
|
|
14
14
|
from .run_status import RUN_STATUS_BLOCKED, RUN_STATUS_PASS, RUN_STATUS_UNKNOWN
|
|
15
|
+
from ..provider_runners import is_fixture_verdict_comment
|
|
15
16
|
|
|
16
17
|
AUTO_DISCOVERY_SCHEMA = "code_mower.calibrationAutoDiscover.v1"
|
|
17
18
|
TRUTH_EXPECTATION_UNKNOWN = "unknown"
|
|
@@ -147,6 +148,8 @@ def _audit_runs_from_comments(comments: Sequence[Any]) -> list[dict[str, Any]]:
|
|
|
147
148
|
body = str(comment.get("body") or "")
|
|
148
149
|
if not body:
|
|
149
150
|
continue
|
|
151
|
+
if is_fixture_verdict_comment(body):
|
|
152
|
+
continue
|
|
150
153
|
for match in AUDIT_STATE_RE.finditer(body):
|
|
151
154
|
state = match.group("state")
|
|
152
155
|
reviewer = _provider_from_audit_state(match.group("provider"), state)
|
|
@@ -32,11 +32,13 @@ if __package__ in {None, "", "tools"}:
|
|
|
32
32
|
from tools.audit_progress import AuditProgress, run_subprocess_with_progress
|
|
33
33
|
from tools.claude_cli_environment import clean_claude_cli_env, env_flag
|
|
34
34
|
from tools.provider_runners import (
|
|
35
|
+
audit_runtime_quarantine_reason as _audit_runtime_quarantine_reason,
|
|
35
36
|
clip_text as _clip_text,
|
|
36
37
|
fetch_pull_request,
|
|
37
38
|
fetch_base_ref_sha as _shared_fetch_base_ref_sha,
|
|
38
39
|
fetch_pr_head_sha as _shared_fetch_pr_head_sha,
|
|
39
40
|
fetch_pr_head_sha_unless_local_matches as _shared_fetch_pr_head_sha_unless_local_matches,
|
|
41
|
+
is_fixture_structured_verdict as _is_fixture_structured_verdict,
|
|
40
42
|
limit_comment_body,
|
|
41
43
|
one_line as _one_line,
|
|
42
44
|
parse_repo_paths as _parse_repo_paths,
|
|
@@ -58,11 +60,13 @@ if __package__ in {None, "", "tools"}:
|
|
|
58
60
|
from audit_progress import AuditProgress, run_subprocess_with_progress # type: ignore
|
|
59
61
|
from claude_cli_environment import clean_claude_cli_env, env_flag # type: ignore
|
|
60
62
|
from provider_runners import ( # type: ignore
|
|
63
|
+
audit_runtime_quarantine_reason as _audit_runtime_quarantine_reason,
|
|
61
64
|
clip_text as _clip_text,
|
|
62
65
|
fetch_pull_request,
|
|
63
66
|
fetch_base_ref_sha as _shared_fetch_base_ref_sha,
|
|
64
67
|
fetch_pr_head_sha as _shared_fetch_pr_head_sha,
|
|
65
68
|
fetch_pr_head_sha_unless_local_matches as _shared_fetch_pr_head_sha_unless_local_matches,
|
|
69
|
+
is_fixture_structured_verdict as _is_fixture_structured_verdict,
|
|
66
70
|
limit_comment_body,
|
|
67
71
|
one_line as _one_line,
|
|
68
72
|
parse_repo_paths as _parse_repo_paths,
|
|
@@ -82,11 +86,13 @@ else: # pragma: no cover - exercised after package extraction.
|
|
|
82
86
|
from .claude_cli_environment import clean_claude_cli_env, env_flag
|
|
83
87
|
from .provider_runners import (
|
|
84
88
|
FetchedHeadMismatch,
|
|
89
|
+
audit_runtime_quarantine_reason as _audit_runtime_quarantine_reason,
|
|
85
90
|
clip_text as _clip_text,
|
|
86
91
|
fetch_base_ref_sha as _shared_fetch_base_ref_sha,
|
|
87
92
|
fetch_pull_request,
|
|
88
93
|
fetch_pr_head_sha as _shared_fetch_pr_head_sha,
|
|
89
94
|
fetch_pr_head_sha_unless_local_matches as _shared_fetch_pr_head_sha_unless_local_matches,
|
|
95
|
+
is_fixture_structured_verdict as _is_fixture_structured_verdict,
|
|
90
96
|
limit_comment_body,
|
|
91
97
|
one_line as _one_line,
|
|
92
98
|
parse_repo_paths as _parse_repo_paths,
|
|
@@ -112,6 +118,11 @@ MAX_SUMMARY_CHARS = 4_000
|
|
|
112
118
|
MAX_FINDING_TITLE_CHARS = 300
|
|
113
119
|
MAX_FINDING_FILE_CHARS = 500
|
|
114
120
|
MAX_FINDING_DETAIL_CHARS = 4_000
|
|
121
|
+
MAX_CLAUDE_AUDIT_ATTEMPTS = 2
|
|
122
|
+
MIN_BLOCKED_VERDICT_PROSE_CHARS = 80
|
|
123
|
+
SCHEMA_PLACEHOLDER_TEXT = frozenset({"test", "example", "placeholder"})
|
|
124
|
+
CLAUDE_RAW_OUTPUT_SCHEMA = "code_mower.claudeAuditRawOutput.v1"
|
|
125
|
+
BLOCKER_SEVERITIES = frozenset({"P0", "P1", "P2"})
|
|
115
126
|
|
|
116
127
|
STALE_TRAILER = "<!-- CLAUDE_AUDIT_STATE: needs-claude-audit -->"
|
|
117
128
|
DONE_TRAILER = "<!-- CLAUDE_AUDIT_STATE: claude-audit-done -->"
|
|
@@ -181,6 +192,8 @@ class ClaudeVerdict:
|
|
|
181
192
|
p2_count: int = 0
|
|
182
193
|
p3_count: int = 0
|
|
183
194
|
mismatch_note: str = ""
|
|
195
|
+
summary: str = ""
|
|
196
|
+
findings: Tuple[Dict[str, Any], ...] = field(default_factory=tuple)
|
|
184
197
|
|
|
185
198
|
@property
|
|
186
199
|
def blocker_count(self) -> int:
|
|
@@ -207,6 +220,7 @@ class ClaudeAuditResult:
|
|
|
207
220
|
class DiffContext:
|
|
208
221
|
stat: str
|
|
209
222
|
diff: str
|
|
223
|
+
changed_files: Tuple[str, ...]
|
|
210
224
|
was_truncated: bool
|
|
211
225
|
requested_max_bytes: int
|
|
212
226
|
hard_limit_bytes: int
|
|
@@ -337,7 +351,7 @@ def parse_structured_claude_verdict(data: Any) -> ClaudeVerdict:
|
|
|
337
351
|
|
|
338
352
|
p_counts[int(severity[1])] += 1
|
|
339
353
|
if len(rendered_findings) < MAX_RENDERED_FINDINGS:
|
|
340
|
-
rendered_findings.append(raw_finding)
|
|
354
|
+
rendered_findings.append(dict(raw_finding))
|
|
341
355
|
|
|
342
356
|
blocker_count = p_counts[0] + p_counts[1] + p_counts[2]
|
|
343
357
|
if blocker_count > 0:
|
|
@@ -362,9 +376,162 @@ def parse_structured_claude_verdict(data: Any) -> ClaudeVerdict:
|
|
|
362
376
|
p2_count=p_counts[2],
|
|
363
377
|
p3_count=p_counts[3],
|
|
364
378
|
mismatch_note=mismatch_note,
|
|
379
|
+
summary=summary,
|
|
380
|
+
findings=tuple(dict(finding) for finding in raw_findings),
|
|
365
381
|
)
|
|
366
382
|
|
|
367
383
|
|
|
384
|
+
def _placeholder_equal(value: object) -> bool:
|
|
385
|
+
return str(value or "").strip().lower() in SCHEMA_PLACEHOLDER_TEXT
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def _normalize_diff_path(value: object) -> str:
|
|
389
|
+
path = str(value or "").strip().replace("\\", "/")
|
|
390
|
+
while path.startswith("./"):
|
|
391
|
+
path = path[2:]
|
|
392
|
+
if path.startswith(("a/", "b/")):
|
|
393
|
+
path = path[2:]
|
|
394
|
+
return path
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def _diff_file_matches(file_path: str, allowed_files: set[str]) -> bool:
|
|
398
|
+
if file_path in allowed_files:
|
|
399
|
+
return True
|
|
400
|
+
suffix_matches = [
|
|
401
|
+
path
|
|
402
|
+
for path in allowed_files
|
|
403
|
+
if path.endswith(f"/{file_path}") or file_path.endswith(f"/{path}")
|
|
404
|
+
]
|
|
405
|
+
return len(suffix_matches) == 1
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def _claude_verdict_guardrail_reason(
|
|
409
|
+
parsed: ClaudeVerdict,
|
|
410
|
+
changed_files: Tuple[str, ...],
|
|
411
|
+
) -> Optional[str]:
|
|
412
|
+
if parsed.verdict == "UNKNOWN":
|
|
413
|
+
return None
|
|
414
|
+
|
|
415
|
+
if _placeholder_equal(parsed.summary):
|
|
416
|
+
return "structured verdict summary matched a schema placeholder value"
|
|
417
|
+
|
|
418
|
+
allowed_files = {_normalize_diff_path(path) for path in changed_files}
|
|
419
|
+
allowed_files.discard("")
|
|
420
|
+
for idx, finding in enumerate(parsed.findings):
|
|
421
|
+
where = f"findings[{idx}]"
|
|
422
|
+
if _placeholder_equal(finding.get("title")):
|
|
423
|
+
return f"{where}.title matched a schema placeholder value"
|
|
424
|
+
if _placeholder_equal(finding.get("detail")):
|
|
425
|
+
return f"{where}.detail matched a schema placeholder value"
|
|
426
|
+
|
|
427
|
+
file_path = _normalize_diff_path(finding.get("file"))
|
|
428
|
+
severity = str(finding.get("severity") or "")
|
|
429
|
+
if (
|
|
430
|
+
severity in BLOCKER_SEVERITIES
|
|
431
|
+
and (not file_path or not _diff_file_matches(file_path, allowed_files))
|
|
432
|
+
):
|
|
433
|
+
return f"{where}.file {file_path!r} is not present in the PR diff"
|
|
434
|
+
|
|
435
|
+
if (
|
|
436
|
+
parsed.verdict == "BLOCKED"
|
|
437
|
+
and len(parsed.prose.strip()) < MIN_BLOCKED_VERDICT_PROSE_CHARS
|
|
438
|
+
):
|
|
439
|
+
return "structured blocked verdict body is too short to be credible"
|
|
440
|
+
|
|
441
|
+
return None
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def _apply_claude_verdict_guardrails(
|
|
445
|
+
parsed: ClaudeVerdict,
|
|
446
|
+
changed_files: Tuple[str, ...],
|
|
447
|
+
) -> Tuple[ClaudeVerdict, Optional[str]]:
|
|
448
|
+
reason = _claude_verdict_guardrail_reason(parsed, changed_files)
|
|
449
|
+
if reason is None:
|
|
450
|
+
return parsed, None
|
|
451
|
+
return _unknown_structured_verdict(reason), reason
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def _fixture_structured_quarantine_reason(parsed: ClaudeVerdict) -> Optional[str]:
|
|
455
|
+
if _is_fixture_structured_verdict(parsed.summary, list(parsed.findings)):
|
|
456
|
+
return "fixture-shaped structured verdict rejected by guardrail"
|
|
457
|
+
return None
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def _quarantine_is_test_only(reason: Optional[str]) -> bool:
|
|
461
|
+
return reason == "PYTEST_CURRENT_TEST is set"
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def _comment_with_runtime_quarantine_note(comment_body: str, reason: str) -> str:
|
|
465
|
+
note = (
|
|
466
|
+
"\nRuntime quarantine: local verdict artifact was quarantined because "
|
|
467
|
+
f"{reason}. Posting UNKNOWN so the lane requeues visibly.\n"
|
|
468
|
+
)
|
|
469
|
+
marker = f"\n\n{STALE_TRAILER}"
|
|
470
|
+
if marker in comment_body:
|
|
471
|
+
body = comment_body.replace(marker, note + "\n" + STALE_TRAILER, 1)
|
|
472
|
+
else:
|
|
473
|
+
body = comment_body.rstrip() + note + "\n" + STALE_TRAILER + "\n"
|
|
474
|
+
return limit_comment_body(body, STALE_TRAILER, provider_name="Claude")
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def _write_claude_raw_output_sidecar(
|
|
478
|
+
artifact_path: Optional[Path],
|
|
479
|
+
attempts: List[Dict[str, Any]],
|
|
480
|
+
) -> None:
|
|
481
|
+
if artifact_path is None:
|
|
482
|
+
return
|
|
483
|
+
sidecar_path = artifact_path.with_suffix(".claude-raw-output.json")
|
|
484
|
+
payload = {"schema": CLAUDE_RAW_OUTPUT_SCHEMA, "attempts": attempts}
|
|
485
|
+
try:
|
|
486
|
+
text = json.dumps(payload, indent=2, sort_keys=True) + "\n"
|
|
487
|
+
sidecar_path.write_text(text, encoding="utf-8")
|
|
488
|
+
except OSError as exc:
|
|
489
|
+
print(
|
|
490
|
+
f"warning: failed to write Claude raw output sidecar {sidecar_path}: {exc}",
|
|
491
|
+
file=sys.stderr,
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def _guardrail_retry_prompt(
|
|
496
|
+
prompt: str,
|
|
497
|
+
reason: str,
|
|
498
|
+
changed_files: Tuple[str, ...],
|
|
499
|
+
) -> str:
|
|
500
|
+
files = "\n".join(f"- {path}" for path in changed_files[:50])
|
|
501
|
+
if len(changed_files) > 50:
|
|
502
|
+
files += f"\n- ... {len(changed_files) - 50} additional file(s) omitted"
|
|
503
|
+
return (
|
|
504
|
+
f"{prompt.rstrip()}\n\n"
|
|
505
|
+
"Trusted wrapper retry instruction:\n"
|
|
506
|
+
f"The previous structured verdict was rejected: {reason}. "
|
|
507
|
+
"Return a fresh JSON verdict only. Do not use placeholder values such "
|
|
508
|
+
"as 'test', 'example', or 'placeholder'. P0/P1/P2 findings must cite a "
|
|
509
|
+
"file from the PR diff, using the repository-relative path or a unique "
|
|
510
|
+
"suffix. PR diff files:\n"
|
|
511
|
+
f"{files}\n"
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
def _record_posted_comment_url(
|
|
516
|
+
artifact_path: Optional[Path],
|
|
517
|
+
posted_comment_url: Optional[str],
|
|
518
|
+
) -> None:
|
|
519
|
+
if artifact_path is None or not posted_comment_url:
|
|
520
|
+
return
|
|
521
|
+
try:
|
|
522
|
+
payload = json.loads(artifact_path.read_text(encoding="utf-8"))
|
|
523
|
+
if not isinstance(payload, dict):
|
|
524
|
+
return
|
|
525
|
+
payload["posted_comment_url"] = posted_comment_url
|
|
526
|
+
text = json.dumps(payload, indent=2, sort_keys=True) + "\n"
|
|
527
|
+
artifact_path.write_text(text, encoding="utf-8")
|
|
528
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
529
|
+
print(
|
|
530
|
+
f"warning: failed to update audit verdict artifact {artifact_path}: {exc}",
|
|
531
|
+
file=sys.stderr,
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
|
|
368
535
|
def _claude_env() -> Dict[str, str]:
|
|
369
536
|
preserve_auth_overrides = env_flag(os.environ.get("CODE_MOWER_CLAUDE_KEEP_AUTH_ENV"))
|
|
370
537
|
env, _removed = clean_claude_cli_env(
|
|
@@ -505,6 +672,13 @@ def _build_diff_context(
|
|
|
505
672
|
raise FetchedHeadMismatch(expected_head_sha, fetched_head_ref)
|
|
506
673
|
diff_range = f"{fetched_base_ref}...{fetched_head_ref}"
|
|
507
674
|
stat = _run_git(local_repo, ["diff", "--stat", "--find-renames", diff_range])
|
|
675
|
+
changed_files_text = _run_git(
|
|
676
|
+
local_repo,
|
|
677
|
+
["-c", "core.quotePath=false", "diff", "--name-only", "--find-renames", diff_range],
|
|
678
|
+
)
|
|
679
|
+
changed_files = tuple(
|
|
680
|
+
line.strip() for line in changed_files_text.splitlines() if line.strip()
|
|
681
|
+
)
|
|
508
682
|
included_diff, full_diff_bytes, was_truncated = _run_git_limited(
|
|
509
683
|
local_repo,
|
|
510
684
|
["diff", "--find-renames", "--unified=80", diff_range],
|
|
@@ -518,6 +692,7 @@ def _build_diff_context(
|
|
|
518
692
|
return DiffContext(
|
|
519
693
|
stat=stat,
|
|
520
694
|
diff=included_diff,
|
|
695
|
+
changed_files=changed_files,
|
|
521
696
|
was_truncated=was_truncated,
|
|
522
697
|
requested_max_bytes=max_diff_bytes,
|
|
523
698
|
hard_limit_bytes=hard_limit,
|
|
@@ -850,6 +1025,9 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
|
|
|
850
1025
|
parsed=parsed,
|
|
851
1026
|
)
|
|
852
1027
|
if not config.dry_run:
|
|
1028
|
+
quarantine_reason = _audit_runtime_quarantine_reason(
|
|
1029
|
+
comment_body=comment_body,
|
|
1030
|
+
)
|
|
853
1031
|
artifact_path = write_audit_verdict_artifact(
|
|
854
1032
|
lane_id="claude-audit",
|
|
855
1033
|
repo=repo,
|
|
@@ -859,6 +1037,7 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
|
|
|
859
1037
|
verdict=result.verdict,
|
|
860
1038
|
trailer=result.trailer,
|
|
861
1039
|
comment_body=comment_body,
|
|
1040
|
+
quarantine_reason=quarantine_reason,
|
|
862
1041
|
)
|
|
863
1042
|
result.verdict_artifact_path = artifact_path
|
|
864
1043
|
if artifact_path is not None:
|
|
@@ -867,13 +1046,35 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
|
|
|
867
1046
|
file=sys.stderr,
|
|
868
1047
|
flush=True,
|
|
869
1048
|
)
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
1049
|
+
if quarantine_reason:
|
|
1050
|
+
print(
|
|
1051
|
+
" runtime guard quarantined verdict artifact: "
|
|
1052
|
+
f"{quarantine_reason}",
|
|
1053
|
+
file=sys.stderr,
|
|
1054
|
+
flush=True,
|
|
1055
|
+
)
|
|
1056
|
+
if quarantine_reason and _quarantine_is_test_only(quarantine_reason):
|
|
1057
|
+
print(" skipped GitHub post for pytest-only quarantine", file=sys.stderr, flush=True)
|
|
1058
|
+
else:
|
|
1059
|
+
if quarantine_reason:
|
|
1060
|
+
result.verdict = "UNKNOWN"
|
|
1061
|
+
result.trailer = STALE_TRAILER
|
|
1062
|
+
comment_body = _comment_with_runtime_quarantine_note(
|
|
1063
|
+
comment_body,
|
|
1064
|
+
quarantine_reason,
|
|
1065
|
+
)
|
|
1066
|
+
result.comment_body = comment_body
|
|
1067
|
+
posted = post_pr_comment(repo, pr_number, comment_body, token=config.github_token)
|
|
1068
|
+
result.posted_comment_url = posted.get("html_url")
|
|
1069
|
+
_record_posted_comment_url(
|
|
1070
|
+
result.verdict_artifact_path,
|
|
1071
|
+
result.posted_comment_url,
|
|
1072
|
+
)
|
|
1073
|
+
print(
|
|
1074
|
+
f"posted {repo}#{pr_number} verdict=STALE "
|
|
1075
|
+
f"url={result.posted_comment_url}",
|
|
1076
|
+
file=sys.stderr,
|
|
1077
|
+
)
|
|
877
1078
|
config.progress.emit(
|
|
878
1079
|
"audit",
|
|
879
1080
|
status="finish",
|
|
@@ -927,8 +1128,48 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
|
|
|
927
1128
|
),
|
|
928
1129
|
)
|
|
929
1130
|
|
|
1131
|
+
raw_output_attempts: List[Dict[str, Any]] = []
|
|
1132
|
+
parsed = _unknown_structured_verdict("Claude audit did not run")
|
|
1133
|
+
claude_stdout = ""
|
|
1134
|
+
claude_stderr = ""
|
|
1135
|
+
attempt_prompt = prompt
|
|
1136
|
+
final_fixture_reason: Optional[str] = None
|
|
930
1137
|
t0 = time.time()
|
|
931
|
-
|
|
1138
|
+
for attempt in range(1, MAX_CLAUDE_AUDIT_ATTEMPTS + 1):
|
|
1139
|
+
parsed_candidate, attempt_stdout, attempt_stderr = run_claude_audit(
|
|
1140
|
+
config,
|
|
1141
|
+
attempt_prompt,
|
|
1142
|
+
)
|
|
1143
|
+
claude_stdout = attempt_stdout
|
|
1144
|
+
claude_stderr = attempt_stderr
|
|
1145
|
+
parsed, guardrail_reason = _apply_claude_verdict_guardrails(
|
|
1146
|
+
parsed_candidate,
|
|
1147
|
+
diff_context.changed_files,
|
|
1148
|
+
)
|
|
1149
|
+
final_fixture_reason = _fixture_structured_quarantine_reason(parsed_candidate)
|
|
1150
|
+
raw_output_attempts.append(
|
|
1151
|
+
{
|
|
1152
|
+
"attempt": attempt,
|
|
1153
|
+
"stdout": attempt_stdout,
|
|
1154
|
+
"stderr": attempt_stderr,
|
|
1155
|
+
"verdict": parsed_candidate.verdict,
|
|
1156
|
+
"guardrail_rejection": guardrail_reason,
|
|
1157
|
+
}
|
|
1158
|
+
)
|
|
1159
|
+
if guardrail_reason and attempt < MAX_CLAUDE_AUDIT_ATTEMPTS:
|
|
1160
|
+
print(
|
|
1161
|
+
" structured-verdict guardrail rejected Claude output: "
|
|
1162
|
+
f"{guardrail_reason}; retrying once",
|
|
1163
|
+
file=sys.stderr,
|
|
1164
|
+
flush=True,
|
|
1165
|
+
)
|
|
1166
|
+
attempt_prompt = _guardrail_retry_prompt(
|
|
1167
|
+
prompt,
|
|
1168
|
+
guardrail_reason,
|
|
1169
|
+
diff_context.changed_files,
|
|
1170
|
+
)
|
|
1171
|
+
continue
|
|
1172
|
+
break
|
|
932
1173
|
dt = time.time() - t0
|
|
933
1174
|
print(f" claude audit completed in {dt:.0f}s", file=sys.stderr, flush=True)
|
|
934
1175
|
if parsed.mismatch_note:
|
|
@@ -984,6 +1225,10 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
|
|
|
984
1225
|
)
|
|
985
1226
|
|
|
986
1227
|
if not config.dry_run:
|
|
1228
|
+
quarantine_reason = _audit_runtime_quarantine_reason(
|
|
1229
|
+
comment_body=comment_body,
|
|
1230
|
+
fixture_reason=final_fixture_reason if parsed.verdict == "UNKNOWN" else None,
|
|
1231
|
+
)
|
|
987
1232
|
artifact_path = write_audit_verdict_artifact(
|
|
988
1233
|
lane_id="claude-audit",
|
|
989
1234
|
repo=repo,
|
|
@@ -993,6 +1238,7 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
|
|
|
993
1238
|
verdict=result_verdict,
|
|
994
1239
|
trailer=trailer,
|
|
995
1240
|
comment_body=comment_body,
|
|
1241
|
+
quarantine_reason=quarantine_reason,
|
|
996
1242
|
)
|
|
997
1243
|
result.verdict_artifact_path = artifact_path
|
|
998
1244
|
if artifact_path is not None:
|
|
@@ -1001,17 +1247,40 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
|
|
|
1001
1247
|
file=sys.stderr,
|
|
1002
1248
|
flush=True,
|
|
1003
1249
|
)
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1250
|
+
_write_claude_raw_output_sidecar(artifact_path, raw_output_attempts)
|
|
1251
|
+
if quarantine_reason:
|
|
1252
|
+
print(
|
|
1253
|
+
" runtime guard quarantined verdict artifact: "
|
|
1254
|
+
f"{quarantine_reason}",
|
|
1255
|
+
file=sys.stderr,
|
|
1256
|
+
flush=True,
|
|
1257
|
+
)
|
|
1258
|
+
if quarantine_reason and _quarantine_is_test_only(quarantine_reason):
|
|
1259
|
+
print(" skipped GitHub post for pytest-only quarantine", file=sys.stderr, flush=True)
|
|
1260
|
+
else:
|
|
1261
|
+
if quarantine_reason:
|
|
1262
|
+
result.verdict = "UNKNOWN"
|
|
1263
|
+
result.trailer = STALE_TRAILER
|
|
1264
|
+
comment_body = _comment_with_runtime_quarantine_note(
|
|
1265
|
+
comment_body,
|
|
1266
|
+
quarantine_reason,
|
|
1267
|
+
)
|
|
1268
|
+
result.comment_body = comment_body
|
|
1269
|
+
posted = post_pr_comment(repo, pr_number, comment_body, token=config.github_token)
|
|
1270
|
+
result.posted_comment_url = posted.get("html_url")
|
|
1271
|
+
_record_posted_comment_url(
|
|
1272
|
+
result.verdict_artifact_path,
|
|
1273
|
+
result.posted_comment_url,
|
|
1274
|
+
)
|
|
1275
|
+
print(
|
|
1276
|
+
f"posted {repo}#{pr_number} verdict={result_verdict} "
|
|
1277
|
+
f"url={result.posted_comment_url}",
|
|
1278
|
+
file=sys.stderr,
|
|
1279
|
+
)
|
|
1011
1280
|
config.progress.emit(
|
|
1012
1281
|
"audit",
|
|
1013
1282
|
status="finish",
|
|
1014
|
-
detail=f"{repo}#{pr_number} verdict={
|
|
1283
|
+
detail=f"{repo}#{pr_number} verdict={result.verdict}",
|
|
1015
1284
|
)
|
|
1016
1285
|
return result
|
|
1017
1286
|
|
|
@@ -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.0b44`, or run source checkouts with "
|
|
18
18
|
"`PYTHONPATH=src python -m code_mower.cli`."
|
|
19
19
|
)
|
|
20
20
|
|
|
@@ -18,12 +18,16 @@ if __package__ in {None, ""}:
|
|
|
18
18
|
sys.path.insert(0, str(module_dir.parent))
|
|
19
19
|
if module_dir.name == "code_mower": # pragma: no cover - extracted direct CLI.
|
|
20
20
|
from code_mower import audit_handoff_log
|
|
21
|
+
from code_mower.provider_runners import is_fixture_verdict_artifact
|
|
21
22
|
else:
|
|
22
23
|
from tools import audit_handoff_log
|
|
24
|
+
from tools.provider_runners import is_fixture_verdict_artifact
|
|
23
25
|
elif __package__ == "tools":
|
|
24
26
|
from tools import audit_handoff_log
|
|
27
|
+
from tools.provider_runners import is_fixture_verdict_artifact
|
|
25
28
|
else: # pragma: no cover - exercised after package extraction.
|
|
26
29
|
from . import audit_handoff_log
|
|
30
|
+
from .provider_runners import is_fixture_verdict_artifact
|
|
27
31
|
|
|
28
32
|
|
|
29
33
|
PASS_VERDICTS = {"completed", "done", "pass", "passed", "success", "succeeded"}
|
|
@@ -31,7 +35,6 @@ BLOCKED_VERDICTS = {"block", "blocked", "fail", "failed", "failure"}
|
|
|
31
35
|
VERDICT_ARTIFACT_SCHEMA = "code_mower.auditVerdictArtifact.v1"
|
|
32
36
|
BENCHMARK_EVENT_SCHEMA = "code_mower.benchmarkEvent.v1"
|
|
33
37
|
VERDICT_ARTIFACT_DIR_ENV = "CODE_MOWER_VERDICT_ARTIFACT_DIR"
|
|
34
|
-
DEFAULT_VERDICT_ARTIFACT_DIR = Path.home() / ".cache" / "code-mower-audits" / "verdicts"
|
|
35
38
|
SEVERITY_RE = re.compile(r"\b(P[0-3])=(\d+)\b")
|
|
36
39
|
AUDIT_TRAILER_RE = re.compile(
|
|
37
40
|
r"<!--\s*(?P<prefix>[A-Z0-9_]+_AUDIT_STATE)\s*:\s*(?P<label>[a-z0-9_.-]+)\s*-->",
|
|
@@ -43,7 +46,10 @@ def default_verdict_artifact_dir() -> Path:
|
|
|
43
46
|
configured = os.environ.get(VERDICT_ARTIFACT_DIR_ENV, "").strip()
|
|
44
47
|
if configured:
|
|
45
48
|
return Path(configured).expanduser()
|
|
46
|
-
|
|
49
|
+
cache_home = os.environ.get("XDG_CACHE_HOME", "").strip()
|
|
50
|
+
if cache_home:
|
|
51
|
+
return Path(cache_home).expanduser() / "code-mower-audits" / "verdicts"
|
|
52
|
+
return Path.home() / ".cache" / "code-mower-audits" / "verdicts"
|
|
47
53
|
|
|
48
54
|
|
|
49
55
|
def _normalize_repo_slug(value: str) -> str:
|
|
@@ -249,6 +255,8 @@ def export_reviewer_run_events_from_verdicts(
|
|
|
249
255
|
raise ValueError(f"{path}: verdict artifact must be a JSON object")
|
|
250
256
|
if payload.get("schema") != VERDICT_ARTIFACT_SCHEMA:
|
|
251
257
|
continue
|
|
258
|
+
if is_fixture_verdict_artifact(payload):
|
|
259
|
+
continue
|
|
252
260
|
artifact_repo = _normalize_repo_slug(
|
|
253
261
|
str(payload.get("repo") or _repo_slug_from_artifact_path(path))
|
|
254
262
|
)
|