code-mower 0.5.0b43__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.
Files changed (299) hide show
  1. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/PKG-INFO +6 -6
  2. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/README.md +5 -5
  3. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/pyproject.toml +1 -1
  4. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/__init__.py +1 -1
  5. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/calibration/auto_discovery.py +3 -0
  6. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/claude_audit_pr.py +99 -23
  7. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/cli.py +1 -1
  8. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/code_mower_telemetry.py +10 -2
  9. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/codex_audit_pr.py +95 -9
  10. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/provider_runners/__init__.py +10 -0
  11. code_mower-0.5.0b44/src/code_mower/provider_runners/verdict_artifacts.py +241 -0
  12. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/builder-provenance.yml.j2 +1 -1
  13. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower.egg-info/PKG-INFO +6 -6
  14. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower.egg-info/SOURCES.txt +1 -0
  15. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_calibration_truth.py +48 -0
  16. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_claude_audit_pr.py +145 -1
  17. code_mower-0.5.0b44/tests/test_codex_audit_pr.py +166 -0
  18. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_release_hygiene.py +34 -34
  19. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_reviewer_metrics_core.py +93 -0
  20. code_mower-0.5.0b43/src/code_mower/provider_runners/verdict_artifacts.py +0 -103
  21. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/CODE_OF_CONDUCT.md +0 -0
  22. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/LICENSE +0 -0
  23. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/MANIFEST.in +0 -0
  24. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/NOTICE +0 -0
  25. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/SUPPORT.md +0 -0
  26. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/requirements/requirements.txt +0 -0
  27. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/setup.cfg +0 -0
  28. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/adapters/__init__.py +0 -0
  29. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/adapters/_base.py +0 -0
  30. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/adapters/cursor_bugbot.py +0 -0
  31. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/adapters/gitar.py +0 -0
  32. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/adapters/greptile.py +0 -0
  33. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/adapters/qodo.py +0 -0
  34. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/antigravity_cli_audit_pr.py +0 -0
  35. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/audit_handoff_log.py +0 -0
  36. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/audit_labeler_lib.py +0 -0
  37. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/audit_progress.py +0 -0
  38. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/blind_review_artifacts.py +0 -0
  39. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/blind_review_coordinator.py +0 -0
  40. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/bootstrap.py +0 -0
  41. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/builder_experiment.py +0 -0
  42. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/builder_runs.py +0 -0
  43. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/calibration/__init__.py +0 -0
  44. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/calibration/arms.py +0 -0
  45. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/calibration/commands.py +0 -0
  46. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/calibration/context_inputs.py +0 -0
  47. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/calibration/corpus.py +0 -0
  48. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/calibration/effect_report.py +0 -0
  49. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/calibration/evidence.py +0 -0
  50. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/calibration/evidence_report.py +0 -0
  51. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/calibration/identity.py +0 -0
  52. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/calibration/metrics.py +0 -0
  53. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/calibration/overlap.py +0 -0
  54. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/calibration/planning.py +0 -0
  55. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/calibration/policy.py +0 -0
  56. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/calibration/results.py +0 -0
  57. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/calibration/run_results.py +0 -0
  58. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/calibration/run_status.py +0 -0
  59. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/calibration/runner.py +0 -0
  60. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/calibration/truth.py +0 -0
  61. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/calibration/value_report.py +0 -0
  62. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/checks.py +0 -0
  63. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/claude_cli_bounce.py +0 -0
  64. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/claude_cli_environment.py +0 -0
  65. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/clear_stale.py +0 -0
  66. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/cloud.py +0 -0
  67. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/cloud_client/__init__.py +0 -0
  68. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/cloud_client/bundle.py +0 -0
  69. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/cloud_client/doctor.py +0 -0
  70. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/cloud_client/dogfood.py +0 -0
  71. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/cloud_client/endpoints.py +0 -0
  72. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/cloud_client/errors.py +0 -0
  73. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/cloud_client/events.py +0 -0
  74. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/cloud_client/export.py +0 -0
  75. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/cloud_client/git_metadata.py +0 -0
  76. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/cloud_client/manifest.py +0 -0
  77. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/cloud_client/operations.py +0 -0
  78. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/cloud_client/reports.py +0 -0
  79. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/cloud_client/setup.py +0 -0
  80. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/cloud_client/upload.py +0 -0
  81. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/code_mower_calibration.py +0 -0
  82. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/code_mower_context_packs.py +0 -0
  83. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/code_mower_merge.py +0 -0
  84. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/coderabbit_cli_audit_pr.py +0 -0
  85. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/codex_audit_env_preflight.py +0 -0
  86. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/codex_audit_schema_smoke.py +0 -0
  87. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/codex_audit_verdict.schema.json +0 -0
  88. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/config.py +0 -0
  89. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor.py +0 -0
  90. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/__init__.py +0 -0
  91. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/cloud.py +0 -0
  92. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/common.py +0 -0
  93. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github.py +0 -0
  94. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_actions.py +0 -0
  95. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_actions_cost.py +0 -0
  96. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_actions_cost_summary.py +0 -0
  97. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_actions_failure_annotations.py +0 -0
  98. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_actions_failure_models.py +0 -0
  99. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_actions_failure_scan.py +0 -0
  100. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_actions_failure_selection.py +0 -0
  101. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_actions_failures.py +0 -0
  102. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_actions_permissions.py +0 -0
  103. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_api.py +0 -0
  104. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_branch.py +0 -0
  105. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_config.py +0 -0
  106. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_provider.py +0 -0
  107. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/github_repo.py +0 -0
  108. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/groups.py +0 -0
  109. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/models.py +0 -0
  110. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/output.py +0 -0
  111. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/presets.py +0 -0
  112. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/privacy.py +0 -0
  113. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_api_model.py +0 -0
  114. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_api_model_openai.py +0 -0
  115. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_api_model_profiles.py +0 -0
  116. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_env.py +0 -0
  117. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_env_required.py +0 -0
  118. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_env_tokens.py +0 -0
  119. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_local_cli.py +0 -0
  120. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_local_cli_commands.py +0 -0
  121. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_local_cli_probe_config.py +0 -0
  122. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_probe.py +0 -0
  123. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_probe_auth.py +0 -0
  124. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_probe_evaluation.py +0 -0
  125. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_probe_json.py +0 -0
  126. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_probe_remediation.py +0 -0
  127. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/provider_review_hygiene.py +0 -0
  128. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/providers.py +0 -0
  129. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/registry.py +0 -0
  130. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/runner.py +0 -0
  131. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/runtime.py +0 -0
  132. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/doctor_checks/runtime_github_auth.py +0 -0
  133. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/gemini_cli_audit_pr.py +0 -0
  134. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/grok_build_audit_pr.py +0 -0
  135. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/hermes_cli_audit_pr.py +0 -0
  136. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/init.py +0 -0
  137. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/lane_configs/__init__.py +0 -0
  138. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/lane_configs/aider.py +0 -0
  139. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/lane_configs/antigravity_cli.py +0 -0
  140. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/lane_configs/claude.py +0 -0
  141. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/lane_configs/codex.py +0 -0
  142. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/lane_configs/devin.py +0 -0
  143. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/lane_configs/gemini_cli.py +0 -0
  144. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/lane_configs/grok_build.py +0 -0
  145. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/lane_configs/hermes_cli.py +0 -0
  146. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/lane_configs/local_llm.py +0 -0
  147. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/local_llm_audit_pr.py +0 -0
  148. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/local_llm_bakeoff.py +0 -0
  149. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/local_llm_calibration.py +0 -0
  150. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/local_llm_profiles.py +0 -0
  151. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/migration.py +0 -0
  152. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/migration_install.py +0 -0
  153. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/migration_mirror.py +0 -0
  154. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/migration_readiness.py +0 -0
  155. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/migration_rehearsal.py +0 -0
  156. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/next_steps.py +0 -0
  157. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/package.py +0 -0
  158. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/package_content.py +0 -0
  159. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/package_manifest.py +0 -0
  160. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/package_paths.py +0 -0
  161. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/package_rendering.py +0 -0
  162. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/package_static.py +0 -0
  163. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/plan_context.py +0 -0
  164. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/prompts.py +0 -0
  165. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/provider_registry.py +0 -0
  166. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/provider_runners/comments.py +0 -0
  167. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/provider_runners/git.py +0 -0
  168. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/provider_runners/github_auth.py +0 -0
  169. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/provider_runners/github_pr.py +0 -0
  170. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/provider_runners/pr_worktree.py +0 -0
  171. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/provider_runners/process.py +0 -0
  172. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/provider_runners/repo_paths.py +0 -0
  173. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/provider_runners/text_schema.py +0 -0
  174. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/provider_runners/workspace.py +0 -0
  175. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/providers/__init__.py +0 -0
  176. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/providers/local_cli.py +0 -0
  177. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/providers/provenance.py +0 -0
  178. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/release_readiness.py +0 -0
  179. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/reviewer_metrics.py +0 -0
  180. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/reviewer_spend.py +0 -0
  181. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/saas_reviewer_labeler.py +0 -0
  182. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/secrets.py +0 -0
  183. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/builder-experiment.example.json +0 -0
  184. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/calibration-corpus.example.json +0 -0
  185. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/calibration-corpus.json +0 -0
  186. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/code-mower.example.yml +0 -0
  187. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/context-packs.example.json +0 -0
  188. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/lane_prompts/base-audit.md +0 -0
  189. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/lane_prompts/calibration-policy.md +0 -0
  190. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/lane_prompts/context-driven-quality.md +0 -0
  191. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/lane_prompts/docs-design.md +0 -0
  192. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/lane_prompts/generic-programming.md +0 -0
  193. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/lane_prompts/operability.md +0 -0
  194. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/lane_prompts/package-runtime.md +0 -0
  195. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/lane_prompts/plan-conformance.md +0 -0
  196. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/lane_prompts/security-threat-model.md +0 -0
  197. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/product-support/code_mower +0 -0
  198. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/product-support/code_mower_standalone_pin.env +0 -0
  199. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/product-support/code_mower_standalone_shadow.sh +0 -0
  200. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/product-support/run_claude_audit_pr.sh +0 -0
  201. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/product-support/run_codex_audit_pr.sh +0 -0
  202. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/product-support/safe_gh_comment.py +0 -0
  203. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/product-support/status_report.py +0 -0
  204. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/providers.yml +0 -0
  205. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/reviewer-spend.example.json +0 -0
  206. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/reviewer-value-report.example.md +0 -0
  207. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/audit-label-cleanup.yml.j2 +0 -0
  208. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/code-mower-gate-health.yml.j2 +0 -0
  209. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/code-mower-gate.yml.j2 +0 -0
  210. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/hosted-bridge.yml.j2 +0 -0
  211. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/local-cli-audit.yml.j2 +0 -0
  212. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/needs-owner-notify.yml.j2 +0 -0
  213. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/private-standalone-shadow.yml.j2 +0 -0
  214. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/review-clear-stale.yml.j2 +0 -0
  215. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/saas-reviewer-labeler.yml.j2 +0 -0
  216. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/self-hosted-local-audit.yml.j2 +0 -0
  217. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/trailer-comment-labeler.yml.j2 +0 -0
  218. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/templates/workflows/weekly-status.yml.j2 +0 -0
  219. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/trailer_comment_labeler.py +0 -0
  220. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/versioning.py +0 -0
  221. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower/work_orders.py +0 -0
  222. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower.egg-info/dependency_links.txt +0 -0
  223. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower.egg-info/entry_points.txt +0 -0
  224. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower.egg-info/requires.txt +0 -0
  225. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/src/code_mower.egg-info/top_level.txt +0 -0
  226. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/builder-experiment.example.json +0 -0
  227. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/calibration-corpus.example.json +0 -0
  228. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/calibration-corpus.json +0 -0
  229. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/code-mower.yml.j2 +0 -0
  230. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/context-packs.example.json +0 -0
  231. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/cursor/BUGBOT.md +0 -0
  232. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/providers/acp_bridge.yml +0 -0
  233. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/providers/aider.yml +0 -0
  234. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/providers/antigravity_cli.yml +0 -0
  235. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/providers/claude_audit.yml +0 -0
  236. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/providers/claude_review.yml +0 -0
  237. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/providers/coderabbit_cli.yml +0 -0
  238. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/providers/codex.yml +0 -0
  239. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/providers/cursor_bugbot.yml +0 -0
  240. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/providers/devin.yml +0 -0
  241. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/providers/gemini_cli.yml +0 -0
  242. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/providers/gitar.yml +0 -0
  243. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/providers/greptile.yml +0 -0
  244. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/providers/hermes_cli.yml +0 -0
  245. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/providers/local_llm.yml +0 -0
  246. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/providers/qodo.yml +0 -0
  247. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/providers.yml +0 -0
  248. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/reviewer-spend.example.json +0 -0
  249. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/reviewer-value-report.example.md +0 -0
  250. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/workflows/audit-label-cleanup.yml.j2 +0 -0
  251. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/workflows/blind-review-artifacts-dry-run.yml.j2 +0 -0
  252. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/workflows/builder-provenance.yml.j2 +0 -0
  253. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/workflows/code-mower-gate-health.yml.j2 +0 -0
  254. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/workflows/code-mower-gate.yml.j2 +0 -0
  255. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/workflows/hosted-bridge.yml.j2 +0 -0
  256. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/workflows/local-cli-audit.yml.j2 +0 -0
  257. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/workflows/private-standalone-shadow.yml.j2 +0 -0
  258. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/workflows/review-clear-stale.yml.j2 +0 -0
  259. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/workflows/saas-reviewer-labeler.yml.j2 +0 -0
  260. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/workflows/self-hosted-local-audit.yml.j2 +0 -0
  261. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/templates/workflows/trailer-comment-labeler.yml.j2 +0 -0
  262. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_audit_comment_posture.py +0 -0
  263. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_builder_identity.py +0 -0
  264. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_builder_runs.py +0 -0
  265. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_calibration_effect_report.py +0 -0
  266. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_checks.py +0 -0
  267. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_claude_cli_bounce.py +0 -0
  268. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_clear_stale.py +0 -0
  269. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_cloud_client.py +0 -0
  270. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_cloud_git_metadata.py +0 -0
  271. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_cloud_manifest.py +0 -0
  272. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_cloud_reports.py +0 -0
  273. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_coderabbit_cli_audit_pr.py +0 -0
  274. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_codex_audit_env_preflight.py +0 -0
  275. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_doctor_actions_cost_summary.py +0 -0
  276. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_doctor_actions_failure_selection.py +0 -0
  277. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_doctor_boundaries.py +0 -0
  278. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_doctor_github_checks.py +0 -0
  279. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_doctor_output.py +0 -0
  280. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_doctor_privacy.py +0 -0
  281. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_doctor_provider_env_required.py +0 -0
  282. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_doctor_provider_env_tokens.py +0 -0
  283. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_doctor_provider_probe.py +0 -0
  284. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_doctor_provider_review_hygiene.py +0 -0
  285. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_doctor_registry.py +0 -0
  286. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_local_llm_github_helpers.py +0 -0
  287. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_plan_context.py +0 -0
  288. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_provider_local_cli.py +0 -0
  289. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_provider_runners_comments.py +0 -0
  290. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_provider_runners_git.py +0 -0
  291. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_provider_runners_github_pr.py +0 -0
  292. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_provider_runners_pr_worktree.py +0 -0
  293. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_provider_runners_process.py +0 -0
  294. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_provider_runners_repo_paths.py +0 -0
  295. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_provider_runners_text_schema.py +0 -0
  296. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_provider_runners_workspace.py +0 -0
  297. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_reviewer_spend.py +0 -0
  298. {code_mower-0.5.0b43 → code_mower-0.5.0b44}/tests/test_trailer_comment_labeler.py +0 -0
  299. {code_mower-0.5.0b43 → 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.0b43
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.0b43
106
+ pipx install --python python3.12 code-mower==0.5.0b44
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.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.0b43`.
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.43` from
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.0b43` on [PyPI](https://pypi.org/project/code-mower/).
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.0b43
91
+ pipx install --python python3.12 code-mower==0.5.0b44
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.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.0b43`.
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.43` from
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.0b43` on [PyPI](https://pypi.org/project/code-mower/).
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
 
@@ -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.0b44"
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.0b44"
@@ -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]],
@@ -996,6 +1025,9 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
996
1025
  parsed=parsed,
997
1026
  )
998
1027
  if not config.dry_run:
1028
+ quarantine_reason = _audit_runtime_quarantine_reason(
1029
+ comment_body=comment_body,
1030
+ )
999
1031
  artifact_path = write_audit_verdict_artifact(
1000
1032
  lane_id="claude-audit",
1001
1033
  repo=repo,
@@ -1005,6 +1037,7 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
1005
1037
  verdict=result.verdict,
1006
1038
  trailer=result.trailer,
1007
1039
  comment_body=comment_body,
1040
+ quarantine_reason=quarantine_reason,
1008
1041
  )
1009
1042
  result.verdict_artifact_path = artifact_path
1010
1043
  if artifact_path is not None:
@@ -1013,17 +1046,35 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
1013
1046
  file=sys.stderr,
1014
1047
  flush=True,
1015
1048
  )
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
- )
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
+ )
1027
1078
  config.progress.emit(
1028
1079
  "audit",
1029
1080
  status="finish",
@@ -1082,6 +1133,7 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
1082
1133
  claude_stdout = ""
1083
1134
  claude_stderr = ""
1084
1135
  attempt_prompt = prompt
1136
+ final_fixture_reason: Optional[str] = None
1085
1137
  t0 = time.time()
1086
1138
  for attempt in range(1, MAX_CLAUDE_AUDIT_ATTEMPTS + 1):
1087
1139
  parsed_candidate, attempt_stdout, attempt_stderr = run_claude_audit(
@@ -1094,6 +1146,7 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
1094
1146
  parsed_candidate,
1095
1147
  diff_context.changed_files,
1096
1148
  )
1149
+ final_fixture_reason = _fixture_structured_quarantine_reason(parsed_candidate)
1097
1150
  raw_output_attempts.append(
1098
1151
  {
1099
1152
  "attempt": attempt,
@@ -1172,6 +1225,10 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
1172
1225
  )
1173
1226
 
1174
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
+ )
1175
1232
  artifact_path = write_audit_verdict_artifact(
1176
1233
  lane_id="claude-audit",
1177
1234
  repo=repo,
@@ -1181,6 +1238,7 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
1181
1238
  verdict=result_verdict,
1182
1239
  trailer=trailer,
1183
1240
  comment_body=comment_body,
1241
+ quarantine_reason=quarantine_reason,
1184
1242
  )
1185
1243
  result.verdict_artifact_path = artifact_path
1186
1244
  if artifact_path is not None:
@@ -1190,21 +1248,39 @@ def audit_pr(config: ClaudeAuditConfig, repo: str, pr_number: int) -> ClaudeAudi
1190
1248
  flush=True,
1191
1249
  )
1192
1250
  _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
- )
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
+ )
1204
1280
  config.progress.emit(
1205
1281
  "audit",
1206
1282
  status="finish",
1207
- detail=f"{repo}#{pr_number} verdict={result_verdict}",
1283
+ detail=f"{repo}#{pr_number} verdict={result.verdict}",
1208
1284
  )
1209
1285
  return result
1210
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.0b43`, or run source checkouts with "
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
- return DEFAULT_VERDICT_ARTIFACT_DIR
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
  )
@@ -89,11 +89,13 @@ if __package__ in {None, "", "tools"}:
89
89
  from tools import reviewer_spend
90
90
  from tools.audit_progress import AuditProgress, run_subprocess_with_progress
91
91
  from tools.provider_runners import (
92
+ audit_runtime_quarantine_reason as _audit_runtime_quarantine_reason,
92
93
  clip_text as _clip_text,
93
94
  fetch_pull_request,
94
95
  fetch_base_ref as _shared_fetch_base_ref,
95
96
  fetch_pr_head as _shared_fetch_pr_head,
96
97
  fetch_pr_head_unless_local_matches as _shared_fetch_pr_head_unless_local_matches,
98
+ is_fixture_structured_verdict as _is_fixture_structured_verdict,
97
99
  limit_comment_body,
98
100
  one_line as _one_line,
99
101
  parse_repo_paths as _parse_repo_paths,
@@ -112,11 +114,13 @@ if __package__ in {None, "", "tools"}:
112
114
  import reviewer_spend # type: ignore
113
115
  from audit_progress import AuditProgress, run_subprocess_with_progress # type: ignore
114
116
  from provider_runners import ( # type: ignore
117
+ audit_runtime_quarantine_reason as _audit_runtime_quarantine_reason,
115
118
  clip_text as _clip_text,
116
119
  fetch_pull_request,
117
120
  fetch_base_ref as _shared_fetch_base_ref,
118
121
  fetch_pr_head as _shared_fetch_pr_head,
119
122
  fetch_pr_head_unless_local_matches as _shared_fetch_pr_head_unless_local_matches,
123
+ is_fixture_structured_verdict as _is_fixture_structured_verdict,
120
124
  limit_comment_body,
121
125
  one_line as _one_line,
122
126
  parse_repo_paths as _parse_repo_paths,
@@ -135,12 +139,14 @@ else: # pragma: no cover - exercised after package extraction.
135
139
  from . import reviewer_spend
136
140
  from .audit_progress import AuditProgress, run_subprocess_with_progress
137
141
  from .provider_runners import (
142
+ audit_runtime_quarantine_reason as _audit_runtime_quarantine_reason,
138
143
  clip_text as _clip_text,
139
144
  create_temp_worktree as _shared_create_temp_worktree,
140
145
  fetch_base_ref as _shared_fetch_base_ref,
141
146
  fetch_pull_request,
142
147
  fetch_pr_head as _shared_fetch_pr_head,
143
148
  fetch_pr_head_unless_local_matches as _shared_fetch_pr_head_unless_local_matches,
149
+ is_fixture_structured_verdict as _is_fixture_structured_verdict,
144
150
  limit_comment_body,
145
151
  one_line as _one_line,
146
152
  parse_repo_paths as _parse_repo_paths,
@@ -240,6 +246,7 @@ class CodexVerdict:
240
246
  # (e.g. verdict=pass but P2 findings are present). This is not
241
247
  # rendered into the public PR comment; audit_pr() logs it to stderr.
242
248
  mismatch_note: str = ""
249
+ quarantine_reason: str = ""
243
250
 
244
251
  @property
245
252
  def blocker_count(self) -> int:
@@ -258,6 +265,7 @@ class CodexVerdict:
258
265
  "blocker_count": self.blocker_count,
259
266
  "findings": self.findings,
260
267
  "mismatch_note": self.mismatch_note,
268
+ "quarantine_reason": self.quarantine_reason,
261
269
  }
262
270
 
263
271
 
@@ -313,6 +321,23 @@ BLOCKED_TRAILER = "<!-- CODEX_AUDIT_STATE: codex-audit-blocked -->"
313
321
  AUDIT_RUN_TRAILER_PREFIX = "<!-- CODE_MOWER_AUDIT_RUN:"
314
322
 
315
323
 
324
+ def _quarantine_is_test_only(reason: Optional[str]) -> bool:
325
+ return reason == "PYTEST_CURRENT_TEST is set"
326
+
327
+
328
+ def _comment_with_runtime_quarantine_note(comment_body: str, reason: str) -> str:
329
+ note = (
330
+ "\nRuntime quarantine: local verdict artifact was quarantined because "
331
+ f"{reason}. Posting UNKNOWN so the lane requeues visibly.\n"
332
+ )
333
+ marker = f"\n\n{STALE_TRAILER}"
334
+ if marker in comment_body:
335
+ body = comment_body.replace(marker, note + "\n" + STALE_TRAILER, 1)
336
+ else:
337
+ body = comment_body.rstrip() + note + "\n" + STALE_TRAILER + "\n"
338
+ return limit_comment_body(body, STALE_TRAILER, provider_name="Codex")
339
+
340
+
316
341
  # ----- Structured verdict validation -----
317
342
 
318
343
 
@@ -446,6 +471,20 @@ def parse_structured_codex_verdict(data: Any) -> CodexVerdict:
446
471
  rendered_findings.append(raw_finding)
447
472
 
448
473
  blocker_count = p_counts[0] + p_counts[1] + p_counts[2]
474
+ if _is_fixture_structured_verdict(summary, raw_findings):
475
+ return CodexVerdict(
476
+ verdict="UNKNOWN",
477
+ prose=(
478
+ "(structured Codex verdict is unusable: fixture-shaped structured "
479
+ "verdict; requeue and retry)"
480
+ ),
481
+ p0_count=p_counts[0],
482
+ p1_count=p_counts[1],
483
+ p2_count=p_counts[2],
484
+ p3_count=p_counts[3],
485
+ findings=finding_lines,
486
+ quarantine_reason="fixture-shaped structured verdict",
487
+ )
449
488
  if blocker_count > 0:
450
489
  verdict = "BLOCKED"
451
490
  mismatch_note = (
@@ -1367,6 +1406,9 @@ def audit_pr(config: AuditConfig, repo: str, pr_number: int) -> AuditResult:
1367
1406
  comment_body=comment_body, codex_stdout="", codex_stderr="",
1368
1407
  )
1369
1408
  if not config.dry_run:
1409
+ quarantine_reason = _audit_runtime_quarantine_reason(
1410
+ comment_body=comment_body,
1411
+ )
1370
1412
  artifact_path = write_audit_verdict_artifact(
1371
1413
  lane_id="codex-audit",
1372
1414
  repo=repo,
@@ -1376,6 +1418,7 @@ def audit_pr(config: AuditConfig, repo: str, pr_number: int) -> AuditResult:
1376
1418
  verdict=result.verdict,
1377
1419
  trailer=result.trailer,
1378
1420
  comment_body=comment_body,
1421
+ quarantine_reason=quarantine_reason,
1379
1422
  )
1380
1423
  result.verdict_artifact_path = artifact_path
1381
1424
  if artifact_path is not None:
@@ -1395,9 +1438,27 @@ def audit_pr(config: AuditConfig, repo: str, pr_number: int) -> AuditResult:
1395
1438
  # comment. See generated product-support wrapper
1396
1439
  # finish_lock handling for the wrapper-side history and
1397
1440
  # the general silent-success/silent-failure lesson.
1398
- posted = post_pr_comment(repo, pr_number, comment_body,
1399
- token=config.github_token)
1400
- result.posted_comment_url = posted.get("html_url")
1441
+ if quarantine_reason:
1442
+ print(
1443
+ " runtime guard quarantined verdict artifact: "
1444
+ f"{quarantine_reason}",
1445
+ file=sys.stderr,
1446
+ flush=True,
1447
+ )
1448
+ if quarantine_reason and _quarantine_is_test_only(quarantine_reason):
1449
+ print(" skipped GitHub post for pytest-only quarantine", file=sys.stderr, flush=True)
1450
+ else:
1451
+ if quarantine_reason:
1452
+ result.verdict = "UNKNOWN"
1453
+ result.trailer = STALE_TRAILER
1454
+ comment_body = _comment_with_runtime_quarantine_note(
1455
+ comment_body,
1456
+ quarantine_reason,
1457
+ )
1458
+ result.comment_body = comment_body
1459
+ posted = post_pr_comment(repo, pr_number, comment_body,
1460
+ token=config.github_token)
1461
+ result.posted_comment_url = posted.get("html_url")
1401
1462
  config.progress.emit(
1402
1463
  "audit",
1403
1464
  status="finish",
@@ -1547,6 +1608,12 @@ def audit_pr(config: AuditConfig, repo: str, pr_number: int) -> AuditResult:
1547
1608
  )
1548
1609
 
1549
1610
  if not config.dry_run:
1611
+ quarantine_reason = _audit_runtime_quarantine_reason(
1612
+ comment_body=comment_body,
1613
+ fixture_reason=(
1614
+ parsed.quarantine_reason if parsed.verdict == "UNKNOWN" else None
1615
+ ),
1616
+ )
1550
1617
  artifact_path = write_audit_verdict_artifact(
1551
1618
  lane_id="codex-audit",
1552
1619
  repo=repo,
@@ -1556,6 +1623,7 @@ def audit_pr(config: AuditConfig, repo: str, pr_number: int) -> AuditResult:
1556
1623
  verdict=result_verdict,
1557
1624
  trailer=trailer,
1558
1625
  comment_body=comment_body,
1626
+ quarantine_reason=quarantine_reason,
1559
1627
  )
1560
1628
  result.verdict_artifact_path = artifact_path
1561
1629
  if artifact_path is not None:
@@ -1568,16 +1636,34 @@ def audit_pr(config: AuditConfig, repo: str, pr_number: int) -> AuditResult:
1568
1636
  # at the STALE-handler call site. tl;dr: post_pr_comment() raising
1569
1637
  # is the only signal the wrapper's finish_lock has that posting
1570
1638
  # failed. Do not catch the exception here.
1571
- posted = post_pr_comment(repo, pr_number, comment_body,
1572
- token=config.github_token)
1573
- result.posted_comment_url = posted.get("html_url")
1574
- print(f"posted {repo}#{pr_number} verdict={result_verdict} "
1575
- f"url={result.posted_comment_url}", file=sys.stderr)
1639
+ if quarantine_reason:
1640
+ print(
1641
+ " runtime guard quarantined verdict artifact: "
1642
+ f"{quarantine_reason}",
1643
+ file=sys.stderr,
1644
+ flush=True,
1645
+ )
1646
+ if quarantine_reason and _quarantine_is_test_only(quarantine_reason):
1647
+ print(" skipped GitHub post for pytest-only quarantine", file=sys.stderr, flush=True)
1648
+ else:
1649
+ if quarantine_reason:
1650
+ result.verdict = "UNKNOWN"
1651
+ result.trailer = STALE_TRAILER
1652
+ comment_body = _comment_with_runtime_quarantine_note(
1653
+ comment_body,
1654
+ quarantine_reason,
1655
+ )
1656
+ result.comment_body = comment_body
1657
+ posted = post_pr_comment(repo, pr_number, comment_body,
1658
+ token=config.github_token)
1659
+ result.posted_comment_url = posted.get("html_url")
1660
+ print(f"posted {repo}#{pr_number} verdict={result_verdict} "
1661
+ f"url={result.posted_comment_url}", file=sys.stderr)
1576
1662
 
1577
1663
  config.progress.emit(
1578
1664
  "audit",
1579
1665
  status="finish",
1580
- detail=f"{repo}#{pr_number} verdict={result_verdict}",
1666
+ detail=f"{repo}#{pr_number} verdict={result.verdict}",
1581
1667
  )
1582
1668
  return result
1583
1669
 
@@ -36,6 +36,11 @@ from .pr_worktree import (
36
36
  from .repo_paths import parse_repo_paths
37
37
  from .text_schema import clip_text, one_line, require_exact_keys
38
38
  from .verdict_artifacts import (
39
+ audit_runtime_quarantine_reason,
40
+ fixture_verdict_comment_reason,
41
+ is_fixture_structured_verdict,
42
+ is_fixture_verdict_artifact,
43
+ is_fixture_verdict_comment,
39
44
  load_audit_verdict_artifact,
40
45
  repost_audit_verdict_artifact,
41
46
  write_audit_verdict_artifact,
@@ -59,11 +64,16 @@ __all__ = [
59
64
  "fetch_pr_head_sha_or_raise",
60
65
  "fetch_pr_head_sha_unless_local_matches",
61
66
  "fetch_pr_head_unless_local_matches",
67
+ "fixture_verdict_comment_reason",
68
+ "is_fixture_verdict_artifact",
69
+ "is_fixture_verdict_comment",
70
+ "is_fixture_structured_verdict",
62
71
  "local_checkout_matches_head",
63
72
  "load_audit_verdict_artifact",
64
73
  "local_head_sha",
65
74
  "limit_comment_body",
66
75
  "MAX_GITHUB_COMMENT_CHARS",
76
+ "audit_runtime_quarantine_reason",
67
77
  "clip_text",
68
78
  "DEFAULT_HOME_ENV_KEYS",
69
79
  "one_line",