code-mower 0.5.0b43__tar.gz → 0.5.0b45__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.
Files changed (305) hide show
  1. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/PKG-INFO +6 -6
  2. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/README.md +5 -5
  3. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/pyproject.toml +1 -1
  4. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/__init__.py +1 -1
  5. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/calibration/auto_discovery.py +3 -0
  6. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/claude_audit_pr.py +102 -23
  7. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/cli.py +4 -1
  8. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/code_mower_telemetry.py +36 -9
  9. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/codex_audit_pr.py +98 -9
  10. code_mower-0.5.0b45/src/code_mower/gate_health.py +739 -0
  11. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/init.py +28 -6
  12. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/migration_mirror.py +1 -0
  13. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/package_manifest.py +1 -0
  14. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/provider_runners/__init__.py +10 -0
  15. code_mower-0.5.0b45/src/code_mower/provider_runners/verdict_artifacts.py +247 -0
  16. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/code-mower.example.yml +3 -0
  17. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/lane_prompts/base-audit.md +3 -0
  18. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/workflows/builder-provenance.yml.j2 +1 -1
  19. code_mower-0.5.0b45/src/code_mower/templates/workflows/code-mower-gate-health.yml.j2 +55 -0
  20. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/workflows/self-hosted-local-audit.yml.j2 +24 -1
  21. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower.egg-info/PKG-INFO +6 -6
  22. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower.egg-info/SOURCES.txt +4 -0
  23. code_mower-0.5.0b45/templates/workflows/code-mower-gate-health.yml.j2 +55 -0
  24. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/workflows/self-hosted-local-audit.yml.j2 +24 -1
  25. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_calibration_truth.py +48 -0
  26. code_mower-0.5.0b45/tests/test_claude_audit_pr.py +321 -0
  27. code_mower-0.5.0b45/tests/test_codex_audit_pr.py +168 -0
  28. code_mower-0.5.0b45/tests/test_gate_health.py +669 -0
  29. code_mower-0.5.0b45/tests/test_prompts.py +18 -0
  30. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_release_hygiene.py +120 -135
  31. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_reviewer_metrics_core.py +112 -13
  32. code_mower-0.5.0b43/src/code_mower/provider_runners/verdict_artifacts.py +0 -103
  33. code_mower-0.5.0b43/src/code_mower/templates/workflows/code-mower-gate-health.yml.j2 +0 -321
  34. code_mower-0.5.0b43/templates/workflows/code-mower-gate-health.yml.j2 +0 -321
  35. code_mower-0.5.0b43/tests/test_claude_audit_pr.py +0 -175
  36. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/CODE_OF_CONDUCT.md +0 -0
  37. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/LICENSE +0 -0
  38. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/MANIFEST.in +0 -0
  39. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/NOTICE +0 -0
  40. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/SUPPORT.md +0 -0
  41. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/requirements/requirements.txt +0 -0
  42. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/setup.cfg +0 -0
  43. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/adapters/__init__.py +0 -0
  44. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/adapters/_base.py +0 -0
  45. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/adapters/cursor_bugbot.py +0 -0
  46. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/adapters/gitar.py +0 -0
  47. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/adapters/greptile.py +0 -0
  48. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/adapters/qodo.py +0 -0
  49. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/antigravity_cli_audit_pr.py +0 -0
  50. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/audit_handoff_log.py +0 -0
  51. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/audit_labeler_lib.py +0 -0
  52. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/audit_progress.py +0 -0
  53. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/blind_review_artifacts.py +0 -0
  54. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/blind_review_coordinator.py +0 -0
  55. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/bootstrap.py +0 -0
  56. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/builder_experiment.py +0 -0
  57. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/builder_runs.py +0 -0
  58. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/calibration/__init__.py +0 -0
  59. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/calibration/arms.py +0 -0
  60. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/calibration/commands.py +0 -0
  61. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/calibration/context_inputs.py +0 -0
  62. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/calibration/corpus.py +0 -0
  63. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/calibration/effect_report.py +0 -0
  64. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/calibration/evidence.py +0 -0
  65. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/calibration/evidence_report.py +0 -0
  66. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/calibration/identity.py +0 -0
  67. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/calibration/metrics.py +0 -0
  68. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/calibration/overlap.py +0 -0
  69. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/calibration/planning.py +0 -0
  70. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/calibration/policy.py +0 -0
  71. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/calibration/results.py +0 -0
  72. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/calibration/run_results.py +0 -0
  73. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/calibration/run_status.py +0 -0
  74. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/calibration/runner.py +0 -0
  75. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/calibration/truth.py +0 -0
  76. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/calibration/value_report.py +0 -0
  77. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/checks.py +0 -0
  78. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/claude_cli_bounce.py +0 -0
  79. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/claude_cli_environment.py +0 -0
  80. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/clear_stale.py +0 -0
  81. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/cloud.py +0 -0
  82. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/cloud_client/__init__.py +0 -0
  83. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/cloud_client/bundle.py +0 -0
  84. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/cloud_client/doctor.py +0 -0
  85. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/cloud_client/dogfood.py +0 -0
  86. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/cloud_client/endpoints.py +0 -0
  87. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/cloud_client/errors.py +0 -0
  88. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/cloud_client/events.py +0 -0
  89. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/cloud_client/export.py +0 -0
  90. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/cloud_client/git_metadata.py +0 -0
  91. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/cloud_client/manifest.py +0 -0
  92. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/cloud_client/operations.py +0 -0
  93. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/cloud_client/reports.py +0 -0
  94. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/cloud_client/setup.py +0 -0
  95. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/cloud_client/upload.py +0 -0
  96. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/code_mower_calibration.py +0 -0
  97. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/code_mower_context_packs.py +0 -0
  98. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/code_mower_merge.py +0 -0
  99. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/coderabbit_cli_audit_pr.py +0 -0
  100. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/codex_audit_env_preflight.py +0 -0
  101. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/codex_audit_schema_smoke.py +0 -0
  102. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/codex_audit_verdict.schema.json +0 -0
  103. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/config.py +0 -0
  104. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor.py +0 -0
  105. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/__init__.py +0 -0
  106. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/cloud.py +0 -0
  107. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/common.py +0 -0
  108. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/github.py +0 -0
  109. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/github_actions.py +0 -0
  110. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/github_actions_cost.py +0 -0
  111. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/github_actions_cost_summary.py +0 -0
  112. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/github_actions_failure_annotations.py +0 -0
  113. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/github_actions_failure_models.py +0 -0
  114. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/github_actions_failure_scan.py +0 -0
  115. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/github_actions_failure_selection.py +0 -0
  116. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/github_actions_failures.py +0 -0
  117. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/github_actions_permissions.py +0 -0
  118. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/github_api.py +0 -0
  119. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/github_branch.py +0 -0
  120. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/github_config.py +0 -0
  121. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/github_provider.py +0 -0
  122. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/github_repo.py +0 -0
  123. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/groups.py +0 -0
  124. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/models.py +0 -0
  125. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/output.py +0 -0
  126. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/presets.py +0 -0
  127. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/privacy.py +0 -0
  128. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/provider_api_model.py +0 -0
  129. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/provider_api_model_openai.py +0 -0
  130. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/provider_api_model_profiles.py +0 -0
  131. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/provider_env.py +0 -0
  132. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/provider_env_required.py +0 -0
  133. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/provider_env_tokens.py +0 -0
  134. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/provider_local_cli.py +0 -0
  135. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/provider_local_cli_commands.py +0 -0
  136. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/provider_local_cli_probe_config.py +0 -0
  137. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/provider_probe.py +0 -0
  138. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/provider_probe_auth.py +0 -0
  139. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/provider_probe_evaluation.py +0 -0
  140. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/provider_probe_json.py +0 -0
  141. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/provider_probe_remediation.py +0 -0
  142. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/provider_review_hygiene.py +0 -0
  143. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/providers.py +0 -0
  144. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/registry.py +0 -0
  145. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/runner.py +0 -0
  146. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/runtime.py +0 -0
  147. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/doctor_checks/runtime_github_auth.py +0 -0
  148. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/gemini_cli_audit_pr.py +0 -0
  149. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/grok_build_audit_pr.py +0 -0
  150. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/hermes_cli_audit_pr.py +0 -0
  151. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/lane_configs/__init__.py +0 -0
  152. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/lane_configs/aider.py +0 -0
  153. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/lane_configs/antigravity_cli.py +0 -0
  154. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/lane_configs/claude.py +0 -0
  155. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/lane_configs/codex.py +0 -0
  156. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/lane_configs/devin.py +0 -0
  157. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/lane_configs/gemini_cli.py +0 -0
  158. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/lane_configs/grok_build.py +0 -0
  159. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/lane_configs/hermes_cli.py +0 -0
  160. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/lane_configs/local_llm.py +0 -0
  161. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/local_llm_audit_pr.py +0 -0
  162. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/local_llm_bakeoff.py +0 -0
  163. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/local_llm_calibration.py +0 -0
  164. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/local_llm_profiles.py +0 -0
  165. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/migration.py +0 -0
  166. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/migration_install.py +0 -0
  167. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/migration_readiness.py +0 -0
  168. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/migration_rehearsal.py +0 -0
  169. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/next_steps.py +0 -0
  170. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/package.py +0 -0
  171. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/package_content.py +0 -0
  172. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/package_paths.py +0 -0
  173. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/package_rendering.py +0 -0
  174. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/package_static.py +0 -0
  175. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/plan_context.py +0 -0
  176. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/prompts.py +0 -0
  177. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/provider_registry.py +0 -0
  178. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/provider_runners/comments.py +0 -0
  179. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/provider_runners/git.py +0 -0
  180. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/provider_runners/github_auth.py +0 -0
  181. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/provider_runners/github_pr.py +0 -0
  182. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/provider_runners/pr_worktree.py +0 -0
  183. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/provider_runners/process.py +0 -0
  184. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/provider_runners/repo_paths.py +0 -0
  185. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/provider_runners/text_schema.py +0 -0
  186. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/provider_runners/workspace.py +0 -0
  187. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/providers/__init__.py +0 -0
  188. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/providers/local_cli.py +0 -0
  189. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/providers/provenance.py +0 -0
  190. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/release_readiness.py +0 -0
  191. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/reviewer_metrics.py +0 -0
  192. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/reviewer_spend.py +0 -0
  193. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/saas_reviewer_labeler.py +0 -0
  194. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/secrets.py +0 -0
  195. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/builder-experiment.example.json +0 -0
  196. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/calibration-corpus.example.json +0 -0
  197. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/calibration-corpus.json +0 -0
  198. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/context-packs.example.json +0 -0
  199. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/lane_prompts/calibration-policy.md +0 -0
  200. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/lane_prompts/context-driven-quality.md +0 -0
  201. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/lane_prompts/docs-design.md +0 -0
  202. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/lane_prompts/generic-programming.md +0 -0
  203. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/lane_prompts/operability.md +0 -0
  204. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/lane_prompts/package-runtime.md +0 -0
  205. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/lane_prompts/plan-conformance.md +0 -0
  206. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/lane_prompts/security-threat-model.md +0 -0
  207. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/product-support/code_mower +0 -0
  208. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/product-support/code_mower_standalone_pin.env +0 -0
  209. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/product-support/code_mower_standalone_shadow.sh +0 -0
  210. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/product-support/run_claude_audit_pr.sh +0 -0
  211. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/product-support/run_codex_audit_pr.sh +0 -0
  212. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/product-support/safe_gh_comment.py +0 -0
  213. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/product-support/status_report.py +0 -0
  214. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/providers.yml +0 -0
  215. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/reviewer-spend.example.json +0 -0
  216. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/reviewer-value-report.example.md +0 -0
  217. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/workflows/audit-label-cleanup.yml.j2 +0 -0
  218. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/workflows/code-mower-gate.yml.j2 +0 -0
  219. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/workflows/hosted-bridge.yml.j2 +0 -0
  220. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/workflows/local-cli-audit.yml.j2 +0 -0
  221. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/workflows/needs-owner-notify.yml.j2 +0 -0
  222. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/workflows/private-standalone-shadow.yml.j2 +0 -0
  223. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/workflows/review-clear-stale.yml.j2 +0 -0
  224. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/workflows/saas-reviewer-labeler.yml.j2 +0 -0
  225. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/workflows/trailer-comment-labeler.yml.j2 +0 -0
  226. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/templates/workflows/weekly-status.yml.j2 +0 -0
  227. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/trailer_comment_labeler.py +0 -0
  228. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/versioning.py +0 -0
  229. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower/work_orders.py +0 -0
  230. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower.egg-info/dependency_links.txt +0 -0
  231. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower.egg-info/entry_points.txt +0 -0
  232. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower.egg-info/requires.txt +0 -0
  233. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/src/code_mower.egg-info/top_level.txt +0 -0
  234. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/builder-experiment.example.json +0 -0
  235. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/calibration-corpus.example.json +0 -0
  236. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/calibration-corpus.json +0 -0
  237. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/code-mower.yml.j2 +0 -0
  238. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/context-packs.example.json +0 -0
  239. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/cursor/BUGBOT.md +0 -0
  240. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/providers/acp_bridge.yml +0 -0
  241. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/providers/aider.yml +0 -0
  242. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/providers/antigravity_cli.yml +0 -0
  243. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/providers/claude_audit.yml +0 -0
  244. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/providers/claude_review.yml +0 -0
  245. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/providers/coderabbit_cli.yml +0 -0
  246. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/providers/codex.yml +0 -0
  247. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/providers/cursor_bugbot.yml +0 -0
  248. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/providers/devin.yml +0 -0
  249. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/providers/gemini_cli.yml +0 -0
  250. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/providers/gitar.yml +0 -0
  251. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/providers/greptile.yml +0 -0
  252. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/providers/hermes_cli.yml +0 -0
  253. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/providers/local_llm.yml +0 -0
  254. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/providers/qodo.yml +0 -0
  255. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/providers.yml +0 -0
  256. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/reviewer-spend.example.json +0 -0
  257. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/reviewer-value-report.example.md +0 -0
  258. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/workflows/audit-label-cleanup.yml.j2 +0 -0
  259. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/workflows/blind-review-artifacts-dry-run.yml.j2 +0 -0
  260. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/workflows/builder-provenance.yml.j2 +0 -0
  261. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/workflows/code-mower-gate.yml.j2 +0 -0
  262. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/workflows/hosted-bridge.yml.j2 +0 -0
  263. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/workflows/local-cli-audit.yml.j2 +0 -0
  264. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/workflows/private-standalone-shadow.yml.j2 +0 -0
  265. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/workflows/review-clear-stale.yml.j2 +0 -0
  266. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/workflows/saas-reviewer-labeler.yml.j2 +0 -0
  267. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/templates/workflows/trailer-comment-labeler.yml.j2 +0 -0
  268. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_audit_comment_posture.py +0 -0
  269. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_builder_identity.py +0 -0
  270. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_builder_runs.py +0 -0
  271. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_calibration_effect_report.py +0 -0
  272. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_checks.py +0 -0
  273. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_claude_cli_bounce.py +0 -0
  274. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_clear_stale.py +0 -0
  275. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_cloud_client.py +0 -0
  276. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_cloud_git_metadata.py +0 -0
  277. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_cloud_manifest.py +0 -0
  278. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_cloud_reports.py +0 -0
  279. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_coderabbit_cli_audit_pr.py +0 -0
  280. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_codex_audit_env_preflight.py +0 -0
  281. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_doctor_actions_cost_summary.py +0 -0
  282. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_doctor_actions_failure_selection.py +0 -0
  283. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_doctor_boundaries.py +0 -0
  284. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_doctor_github_checks.py +0 -0
  285. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_doctor_output.py +0 -0
  286. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_doctor_privacy.py +0 -0
  287. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_doctor_provider_env_required.py +0 -0
  288. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_doctor_provider_env_tokens.py +0 -0
  289. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_doctor_provider_probe.py +0 -0
  290. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_doctor_provider_review_hygiene.py +0 -0
  291. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_doctor_registry.py +0 -0
  292. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_local_llm_github_helpers.py +0 -0
  293. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_plan_context.py +0 -0
  294. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_provider_local_cli.py +0 -0
  295. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_provider_runners_comments.py +0 -0
  296. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_provider_runners_git.py +0 -0
  297. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_provider_runners_github_pr.py +0 -0
  298. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_provider_runners_pr_worktree.py +0 -0
  299. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_provider_runners_process.py +0 -0
  300. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_provider_runners_repo_paths.py +0 -0
  301. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_provider_runners_text_schema.py +0 -0
  302. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_provider_runners_workspace.py +0 -0
  303. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_reviewer_spend.py +0 -0
  304. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/tests/test_trailer_comment_labeler.py +0 -0
  305. {code_mower-0.5.0b43 → code_mower-0.5.0b45}/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.0b43
3
+ Version: 0.5.0b45
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.0b43
106
+ pipx install --python python3.12 code-mower==0.5.0b45
107
107
  code-mower --version
108
108
  ```
109
109
 
110
- `0.5.0b43` is a beta release. Until Code Mower publishes a stable `1.0`
110
+ `0.5.0b45` 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.0b43`.
158
+ the latest 10/10 public-package readiness proof for `code-mower==0.5.0b45`.
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.43` from
313
+ The current public beta is `v0.5.0-beta.45` from
314
314
  [codemower-ai/code-mower](https://github.com/codemower-ai/code-mower), published
315
- as `code-mower==0.5.0b43` on [PyPI](https://pypi.org/project/code-mower/).
315
+ as `code-mower==0.5.0b45` 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.0b43
91
+ pipx install --python python3.12 code-mower==0.5.0b45
92
92
  code-mower --version
93
93
  ```
94
94
 
95
- `0.5.0b43` is a beta release. Until Code Mower publishes a stable `1.0`
95
+ `0.5.0b45` 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.0b43`.
143
+ the latest 10/10 public-package readiness proof for `code-mower==0.5.0b45`.
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.43` from
298
+ The current public beta is `v0.5.0-beta.45` from
299
299
  [codemower-ai/code-mower](https://github.com/codemower-ai/code-mower), published
300
- as `code-mower==0.5.0b43` on [PyPI](https://pypi.org/project/code-mower/).
300
+ as `code-mower==0.5.0b45` 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
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "code-mower"
7
- version = "0.5.0b43"
7
+ version = "0.5.0b45"
8
8
  description = "Multi-reviewer AI code audit orchestration"
9
9
  requires-python = ">=3.11"
10
10
  readme = "README.md"
@@ -1,3 +1,3 @@
1
1
  """Code Mower package."""
2
2
 
3
- __version__ = "0.5.0b43"
3
+ __version__ = "0.5.0b45"
@@ -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,
@@ -445,6 +451,29 @@ def _apply_claude_verdict_guardrails(
445
451
  return _unknown_structured_verdict(reason), reason
446
452
 
447
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
+
448
477
  def _write_claude_raw_output_sidecar(
449
478
  artifact_path: Optional[Path],
450
479
  attempts: List[Dict[str, Any]],
@@ -917,6 +946,7 @@ def format_comment(
917
946
 
918
947
 
919
948
  def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAuditResult:
949
+ audit_started = time.monotonic()
920
950
  local_repo = config.repo_paths.get(repo)
921
951
  if local_repo is None:
922
952
  raise ValueError(
@@ -996,6 +1026,9 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
996
1026
  parsed=parsed,
997
1027
  )
998
1028
  if not config.dry_run:
1029
+ quarantine_reason = _audit_runtime_quarantine_reason(
1030
+ comment_body=comment_body,
1031
+ )
999
1032
  artifact_path = write_audit_verdict_artifact(
1000
1033
  lane_id="claude-audit",
1001
1034
  repo=repo,
@@ -1005,6 +1038,8 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
1005
1038
  verdict=result.verdict,
1006
1039
  trailer=result.trailer,
1007
1040
  comment_body=comment_body,
1041
+ quarantine_reason=quarantine_reason,
1042
+ duration_seconds=time.monotonic() - audit_started,
1008
1043
  )
1009
1044
  result.verdict_artifact_path = artifact_path
1010
1045
  if artifact_path is not None:
@@ -1013,17 +1048,35 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
1013
1048
  file=sys.stderr,
1014
1049
  flush=True,
1015
1050
  )
1016
- posted = post_pr_comment(repo, pr_number, comment_body, token=config.github_token)
1017
- result.posted_comment_url = posted.get("html_url")
1018
- _record_posted_comment_url(
1019
- result.verdict_artifact_path,
1020
- result.posted_comment_url,
1021
- )
1022
- print(
1023
- f"posted {repo}#{pr_number} verdict=STALE "
1024
- f"url={result.posted_comment_url}",
1025
- file=sys.stderr,
1026
- )
1051
+ if quarantine_reason:
1052
+ print(
1053
+ " runtime guard quarantined verdict artifact: "
1054
+ f"{quarantine_reason}",
1055
+ file=sys.stderr,
1056
+ flush=True,
1057
+ )
1058
+ if quarantine_reason and _quarantine_is_test_only(quarantine_reason):
1059
+ print(" skipped GitHub post for pytest-only quarantine", file=sys.stderr, flush=True)
1060
+ else:
1061
+ if quarantine_reason:
1062
+ result.verdict = "UNKNOWN"
1063
+ result.trailer = STALE_TRAILER
1064
+ comment_body = _comment_with_runtime_quarantine_note(
1065
+ comment_body,
1066
+ quarantine_reason,
1067
+ )
1068
+ result.comment_body = comment_body
1069
+ posted = post_pr_comment(repo, pr_number, comment_body, token=config.github_token)
1070
+ result.posted_comment_url = posted.get("html_url")
1071
+ _record_posted_comment_url(
1072
+ result.verdict_artifact_path,
1073
+ result.posted_comment_url,
1074
+ )
1075
+ print(
1076
+ f"posted {repo}#{pr_number} verdict=STALE "
1077
+ f"url={result.posted_comment_url}",
1078
+ file=sys.stderr,
1079
+ )
1027
1080
  config.progress.emit(
1028
1081
  "audit",
1029
1082
  status="finish",
@@ -1082,6 +1135,7 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
1082
1135
  claude_stdout = ""
1083
1136
  claude_stderr = ""
1084
1137
  attempt_prompt = prompt
1138
+ final_fixture_reason: Optional[str] = None
1085
1139
  t0 = time.time()
1086
1140
  for attempt in range(1, MAX_CLAUDE_AUDIT_ATTEMPTS + 1):
1087
1141
  parsed_candidate, attempt_stdout, attempt_stderr = run_claude_audit(
@@ -1094,6 +1148,7 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
1094
1148
  parsed_candidate,
1095
1149
  diff_context.changed_files,
1096
1150
  )
1151
+ final_fixture_reason = _fixture_structured_quarantine_reason(parsed_candidate)
1097
1152
  raw_output_attempts.append(
1098
1153
  {
1099
1154
  "attempt": attempt,
@@ -1172,6 +1227,10 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
1172
1227
  )
1173
1228
 
1174
1229
  if not config.dry_run:
1230
+ quarantine_reason = _audit_runtime_quarantine_reason(
1231
+ comment_body=comment_body,
1232
+ fixture_reason=final_fixture_reason if parsed.verdict == "UNKNOWN" else None,
1233
+ )
1175
1234
  artifact_path = write_audit_verdict_artifact(
1176
1235
  lane_id="claude-audit",
1177
1236
  repo=repo,
@@ -1181,6 +1240,8 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
1181
1240
  verdict=result_verdict,
1182
1241
  trailer=trailer,
1183
1242
  comment_body=comment_body,
1243
+ quarantine_reason=quarantine_reason,
1244
+ duration_seconds=time.monotonic() - audit_started,
1184
1245
  )
1185
1246
  result.verdict_artifact_path = artifact_path
1186
1247
  if artifact_path is not None:
@@ -1190,21 +1251,39 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
1190
1251
  flush=True,
1191
1252
  )
1192
1253
  _write_claude_raw_output_sidecar(artifact_path, raw_output_attempts)
1193
- posted = post_pr_comment(repo, pr_number, comment_body, token=config.github_token)
1194
- result.posted_comment_url = posted.get("html_url")
1195
- _record_posted_comment_url(
1196
- result.verdict_artifact_path,
1197
- result.posted_comment_url,
1198
- )
1199
- print(
1200
- f"posted {repo}#{pr_number} verdict={result_verdict} "
1201
- f"url={result.posted_comment_url}",
1202
- file=sys.stderr,
1203
- )
1254
+ if quarantine_reason:
1255
+ print(
1256
+ " runtime guard quarantined verdict artifact: "
1257
+ f"{quarantine_reason}",
1258
+ file=sys.stderr,
1259
+ flush=True,
1260
+ )
1261
+ if quarantine_reason and _quarantine_is_test_only(quarantine_reason):
1262
+ print(" skipped GitHub post for pytest-only quarantine", file=sys.stderr, flush=True)
1263
+ else:
1264
+ if quarantine_reason:
1265
+ result.verdict = "UNKNOWN"
1266
+ result.trailer = STALE_TRAILER
1267
+ comment_body = _comment_with_runtime_quarantine_note(
1268
+ comment_body,
1269
+ quarantine_reason,
1270
+ )
1271
+ result.comment_body = comment_body
1272
+ posted = post_pr_comment(repo, pr_number, comment_body, token=config.github_token)
1273
+ result.posted_comment_url = posted.get("html_url")
1274
+ _record_posted_comment_url(
1275
+ result.verdict_artifact_path,
1276
+ result.posted_comment_url,
1277
+ )
1278
+ print(
1279
+ f"posted {repo}#{pr_number} verdict={result_verdict} "
1280
+ f"url={result.posted_comment_url}",
1281
+ file=sys.stderr,
1282
+ )
1204
1283
  config.progress.emit(
1205
1284
  "audit",
1206
1285
  status="finish",
1207
- detail=f"{repo}#{pr_number} verdict={result_verdict}",
1286
+ detail=f"{repo}#{pr_number} verdict={result.verdict}",
1208
1287
  )
1209
1288
  return result
1210
1289
 
@@ -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.0b43`, or run source checkouts with "
17
+ "`pipx install code-mower==0.5.0b45`, or run source checkouts with "
18
18
  "`PYTHONPATH=src python -m code_mower.cli`."
19
19
  )
20
20
 
@@ -40,6 +40,7 @@ from . import code_mower_telemetry
40
40
  from . import config as code_mower_config
41
41
  from . import doctor as code_mower_doctor
42
42
  from . import gemini_cli_audit_pr
43
+ from . import gate_health as code_mower_gate_health
43
44
  from . import grok_build_audit_pr
44
45
  from . import hermes_cli_audit_pr
45
46
  from . import init as code_mower_init
@@ -398,6 +399,7 @@ COMMAND_DESCRIPTIONS: dict[str, str] = {
398
399
  "codex-audit-schema-smoke": "Smoke-test Codex audit schema parsing.",
399
400
  "doctor": "Check runtime, GitHub, providers, privacy, and cloud setup.",
400
401
  "gemini-cli": "Run a Gemini CLI structured audit lane.",
402
+ "gate-health": "Alert when audit gate infrastructure stalls.",
401
403
  "grok-build": "Run a Grok Build informational audit lane.",
402
404
  "hermes-cli": "Run a Hermes CLI structured audit lane.",
403
405
  "init": "Render safe easy-mode setup output.",
@@ -529,6 +531,7 @@ COMMAND_HANDLERS: dict[str, CommandHandler] = {
529
531
  "codex-audit-schema-smoke": codex_audit_schema_smoke.main,
530
532
  "doctor": code_mower_doctor.main,
531
533
  "gemini-cli": gemini_cli_audit_pr.main,
534
+ "gate-health": code_mower_gate_health.main,
532
535
  "grok-build": grok_build_audit_pr.main,
533
536
  "hermes-cli": hermes_cli_audit_pr.main,
534
537
  "init": _init_main,
@@ -6,6 +6,7 @@ from __future__ import annotations
6
6
  import argparse
7
7
  import hashlib
8
8
  import json
9
+ import math
9
10
  import os
10
11
  import re
11
12
  import sys
@@ -18,12 +19,16 @@ if __package__ in {None, ""}:
18
19
  sys.path.insert(0, str(module_dir.parent))
19
20
  if module_dir.name == "code_mower": # pragma: no cover - extracted direct CLI.
20
21
  from code_mower import audit_handoff_log
22
+ from code_mower.provider_runners import is_fixture_verdict_artifact
21
23
  else:
22
24
  from tools import audit_handoff_log
25
+ from tools.provider_runners import is_fixture_verdict_artifact
23
26
  elif __package__ == "tools":
24
27
  from tools import audit_handoff_log
28
+ from tools.provider_runners import is_fixture_verdict_artifact
25
29
  else: # pragma: no cover - exercised after package extraction.
26
30
  from . import audit_handoff_log
31
+ from .provider_runners import is_fixture_verdict_artifact
27
32
 
28
33
 
29
34
  PASS_VERDICTS = {"completed", "done", "pass", "passed", "success", "succeeded"}
@@ -31,7 +36,6 @@ BLOCKED_VERDICTS = {"block", "blocked", "fail", "failed", "failure"}
31
36
  VERDICT_ARTIFACT_SCHEMA = "code_mower.auditVerdictArtifact.v1"
32
37
  BENCHMARK_EVENT_SCHEMA = "code_mower.benchmarkEvent.v1"
33
38
  VERDICT_ARTIFACT_DIR_ENV = "CODE_MOWER_VERDICT_ARTIFACT_DIR"
34
- DEFAULT_VERDICT_ARTIFACT_DIR = Path.home() / ".cache" / "code-mower-audits" / "verdicts"
35
39
  SEVERITY_RE = re.compile(r"\b(P[0-3])=(\d+)\b")
36
40
  AUDIT_TRAILER_RE = re.compile(
37
41
  r"<!--\s*(?P<prefix>[A-Z0-9_]+_AUDIT_STATE)\s*:\s*(?P<label>[a-z0-9_.-]+)\s*-->",
@@ -43,7 +47,10 @@ def default_verdict_artifact_dir() -> Path:
43
47
  configured = os.environ.get(VERDICT_ARTIFACT_DIR_ENV, "").strip()
44
48
  if configured:
45
49
  return Path(configured).expanduser()
46
- return DEFAULT_VERDICT_ARTIFACT_DIR
50
+ cache_home = os.environ.get("XDG_CACHE_HOME", "").strip()
51
+ if cache_home:
52
+ return Path(cache_home).expanduser() / "code-mower-audits" / "verdicts"
53
+ return Path.home() / ".cache" / "code-mower-audits" / "verdicts"
47
54
 
48
55
 
49
56
  def _normalize_repo_slug(value: str) -> str:
@@ -115,6 +122,19 @@ def _artifact_severity_counts(payload: Mapping[str, Any]) -> tuple[dict[str, int
115
122
  return comment_counts, "unavailable"
116
123
 
117
124
 
125
+ def _artifact_duration_seconds(payload: Mapping[str, Any]) -> float | None:
126
+ raw = payload.get("duration_seconds")
127
+ if raw is None:
128
+ return None
129
+ try:
130
+ value = float(raw)
131
+ except (TypeError, ValueError):
132
+ return None
133
+ if not math.isfinite(value) or value < 0:
134
+ return None
135
+ return round(value, 3)
136
+
137
+
118
138
  def _normal_artifact_verdict(value: Any) -> str:
119
139
  verdict = str(value or "").strip().lower()
120
140
  if verdict in PASS_VERDICTS:
@@ -199,6 +219,17 @@ def reviewer_run_event_from_verdict_artifact(
199
219
  head = str(payload.get("head_sha_end") or payload.get("head_sha_start") or "")
200
220
  dimensions["head_sha"] = head
201
221
  dimensions["head_sha_short"] = head[:12]
222
+ metrics: dict[str, Any] = {
223
+ "finding_count": finding_count,
224
+ "p0_count": severities["p0"],
225
+ "p1_count": severities["p1"],
226
+ "p2_count": severities["p2"],
227
+ "p3_count": severities["p3"],
228
+ }
229
+ duration_seconds = _artifact_duration_seconds(payload)
230
+ if duration_seconds is not None:
231
+ metrics["duration_seconds_total"] = duration_seconds
232
+ dimensions["duration_source"] = "verdict_artifact"
202
233
  return {
203
234
  "schema": BENCHMARK_EVENT_SCHEMA,
204
235
  "event_type": "reviewer_run",
@@ -209,13 +240,7 @@ def reviewer_run_event_from_verdict_artifact(
209
240
  "provider": _lane_provider(lane_id),
210
241
  "lens": "base",
211
242
  "status": status,
212
- "metrics": {
213
- "finding_count": finding_count,
214
- "p0_count": severities["p0"],
215
- "p1_count": severities["p1"],
216
- "p2_count": severities["p2"],
217
- "p3_count": severities["p3"],
218
- },
243
+ "metrics": metrics,
219
244
  "dimensions": dimensions,
220
245
  }
221
246
 
@@ -249,6 +274,8 @@ def export_reviewer_run_events_from_verdicts(
249
274
  raise ValueError(f"{path}: verdict artifact must be a JSON object")
250
275
  if payload.get("schema") != VERDICT_ARTIFACT_SCHEMA:
251
276
  continue
277
+ if is_fixture_verdict_artifact(payload):
278
+ continue
252
279
  artifact_repo = _normalize_repo_slug(
253
280
  str(payload.get("repo") or _repo_slug_from_artifact_path(path))
254
281
  )