code-mower 0.5.0b14__tar.gz → 0.5.0b16__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 (270) hide show
  1. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/PKG-INFO +6 -6
  2. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/README.md +5 -5
  3. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/pyproject.toml +1 -1
  4. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/__init__.py +1 -1
  5. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/cli.py +1 -1
  6. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/__init__.py +2 -0
  7. code_mower-0.5.0b16/src/code_mower/doctor_checks/provider_review_hygiene.py +87 -0
  8. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/providers.py +6 -1
  9. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/runner.py +1 -0
  10. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/package_manifest.py +5 -0
  11. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/providers/provenance.py +42 -13
  12. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/release_readiness.py +6 -0
  13. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower.egg-info/PKG-INFO +6 -6
  14. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower.egg-info/SOURCES.txt +2 -0
  15. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_doctor_boundaries.py +1 -0
  16. code_mower-0.5.0b16/tests/test_doctor_provider_review_hygiene.py +98 -0
  17. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_provider_local_cli.py +35 -0
  18. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_release_hygiene.py +65 -34
  19. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/CODE_OF_CONDUCT.md +0 -0
  20. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/LICENSE +0 -0
  21. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/MANIFEST.in +0 -0
  22. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/NOTICE +0 -0
  23. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/SUPPORT.md +0 -0
  24. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/requirements/requirements.txt +0 -0
  25. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/setup.cfg +0 -0
  26. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/adapters/__init__.py +0 -0
  27. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/adapters/_base.py +0 -0
  28. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/adapters/cursor_bugbot.py +0 -0
  29. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/adapters/gitar.py +0 -0
  30. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/adapters/greptile.py +0 -0
  31. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/adapters/qodo.py +0 -0
  32. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/antigravity_cli_audit_pr.py +0 -0
  33. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/audit_handoff_log.py +0 -0
  34. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/audit_labeler_lib.py +0 -0
  35. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/audit_progress.py +0 -0
  36. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/blind_review_artifacts.py +0 -0
  37. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/blind_review_coordinator.py +0 -0
  38. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/bootstrap.py +0 -0
  39. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/builder_experiment.py +0 -0
  40. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/calibration/__init__.py +0 -0
  41. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/calibration/arms.py +0 -0
  42. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/calibration/auto_discovery.py +0 -0
  43. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/calibration/commands.py +0 -0
  44. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/calibration/context_inputs.py +0 -0
  45. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/calibration/corpus.py +0 -0
  46. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/calibration/effect_report.py +0 -0
  47. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/calibration/evidence.py +0 -0
  48. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/calibration/evidence_report.py +0 -0
  49. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/calibration/identity.py +0 -0
  50. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/calibration/metrics.py +0 -0
  51. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/calibration/overlap.py +0 -0
  52. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/calibration/planning.py +0 -0
  53. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/calibration/policy.py +0 -0
  54. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/calibration/results.py +0 -0
  55. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/calibration/run_results.py +0 -0
  56. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/calibration/run_status.py +0 -0
  57. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/calibration/runner.py +0 -0
  58. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/calibration/truth.py +0 -0
  59. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/calibration/value_report.py +0 -0
  60. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/checks.py +0 -0
  61. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/claude_audit_pr.py +0 -0
  62. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/claude_cli_bounce.py +0 -0
  63. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/claude_cli_environment.py +0 -0
  64. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/clear_stale.py +0 -0
  65. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/cloud.py +0 -0
  66. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/cloud_client/__init__.py +0 -0
  67. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/cloud_client/bundle.py +0 -0
  68. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/cloud_client/doctor.py +0 -0
  69. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/cloud_client/dogfood.py +0 -0
  70. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/cloud_client/endpoints.py +0 -0
  71. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/cloud_client/errors.py +0 -0
  72. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/cloud_client/events.py +0 -0
  73. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/cloud_client/export.py +0 -0
  74. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/cloud_client/git_metadata.py +0 -0
  75. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/cloud_client/manifest.py +0 -0
  76. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/cloud_client/operations.py +0 -0
  77. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/cloud_client/reports.py +0 -0
  78. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/cloud_client/setup.py +0 -0
  79. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/cloud_client/upload.py +0 -0
  80. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/code_mower_calibration.py +0 -0
  81. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/code_mower_context_packs.py +0 -0
  82. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/code_mower_merge.py +0 -0
  83. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/code_mower_telemetry.py +0 -0
  84. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/coderabbit_cli_audit_pr.py +0 -0
  85. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/codex_audit_env_preflight.py +0 -0
  86. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/codex_audit_pr.py +0 -0
  87. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/codex_audit_schema_smoke.py +0 -0
  88. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/codex_audit_verdict.schema.json +0 -0
  89. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/config.py +0 -0
  90. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor.py +0 -0
  91. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/cloud.py +0 -0
  92. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/common.py +0 -0
  93. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/github.py +0 -0
  94. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/github_actions.py +0 -0
  95. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/github_actions_cost.py +0 -0
  96. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/github_actions_cost_summary.py +0 -0
  97. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/github_actions_failure_annotations.py +0 -0
  98. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/github_actions_failure_models.py +0 -0
  99. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/github_actions_failure_scan.py +0 -0
  100. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/github_actions_failure_selection.py +0 -0
  101. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/github_actions_failures.py +0 -0
  102. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/github_actions_permissions.py +0 -0
  103. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/github_api.py +0 -0
  104. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/github_branch.py +0 -0
  105. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/github_config.py +0 -0
  106. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/github_provider.py +0 -0
  107. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/github_repo.py +0 -0
  108. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/groups.py +0 -0
  109. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/models.py +0 -0
  110. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/output.py +0 -0
  111. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/presets.py +0 -0
  112. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/privacy.py +0 -0
  113. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/provider_api_model.py +0 -0
  114. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/provider_api_model_openai.py +0 -0
  115. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/provider_api_model_profiles.py +0 -0
  116. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/provider_env.py +0 -0
  117. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/provider_env_required.py +0 -0
  118. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/provider_env_tokens.py +0 -0
  119. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/provider_local_cli.py +0 -0
  120. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/provider_local_cli_commands.py +0 -0
  121. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/provider_local_cli_probe_config.py +0 -0
  122. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/provider_probe.py +0 -0
  123. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/provider_probe_auth.py +0 -0
  124. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/provider_probe_evaluation.py +0 -0
  125. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/provider_probe_json.py +0 -0
  126. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/provider_probe_remediation.py +0 -0
  127. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/registry.py +0 -0
  128. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/runtime.py +0 -0
  129. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/doctor_checks/runtime_github_auth.py +0 -0
  130. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/gemini_cli_audit_pr.py +0 -0
  131. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/hermes_cli_audit_pr.py +0 -0
  132. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/init.py +0 -0
  133. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/lane_configs/__init__.py +0 -0
  134. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/lane_configs/aider.py +0 -0
  135. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/lane_configs/antigravity_cli.py +0 -0
  136. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/lane_configs/claude.py +0 -0
  137. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/lane_configs/codex.py +0 -0
  138. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/lane_configs/devin.py +0 -0
  139. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/lane_configs/gemini_cli.py +0 -0
  140. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/lane_configs/hermes_cli.py +0 -0
  141. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/lane_configs/local_llm.py +0 -0
  142. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/local_llm_audit_pr.py +0 -0
  143. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/local_llm_bakeoff.py +0 -0
  144. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/local_llm_calibration.py +0 -0
  145. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/local_llm_profiles.py +0 -0
  146. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/migration.py +0 -0
  147. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/migration_install.py +0 -0
  148. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/migration_mirror.py +0 -0
  149. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/migration_readiness.py +0 -0
  150. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/migration_rehearsal.py +0 -0
  151. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/next_steps.py +0 -0
  152. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/package.py +0 -0
  153. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/package_content.py +0 -0
  154. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/package_paths.py +0 -0
  155. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/package_rendering.py +0 -0
  156. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/package_static.py +0 -0
  157. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/prompts.py +0 -0
  158. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/provider_registry.py +0 -0
  159. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/provider_runners/__init__.py +0 -0
  160. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/provider_runners/comments.py +0 -0
  161. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/provider_runners/git.py +0 -0
  162. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/provider_runners/github_auth.py +0 -0
  163. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/provider_runners/github_pr.py +0 -0
  164. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/provider_runners/pr_worktree.py +0 -0
  165. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/provider_runners/process.py +0 -0
  166. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/provider_runners/repo_paths.py +0 -0
  167. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/provider_runners/text_schema.py +0 -0
  168. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/provider_runners/verdict_artifacts.py +0 -0
  169. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/provider_runners/workspace.py +0 -0
  170. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/providers/__init__.py +0 -0
  171. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/providers/local_cli.py +0 -0
  172. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/reviewer_metrics.py +0 -0
  173. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/saas_reviewer_labeler.py +0 -0
  174. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/secrets.py +0 -0
  175. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/builder-experiment.example.json +0 -0
  176. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/calibration-corpus.example.json +0 -0
  177. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/calibration-corpus.json +0 -0
  178. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/code-mower.example.yml +0 -0
  179. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/context-packs.example.json +0 -0
  180. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/lane_prompts/base-audit.md +0 -0
  181. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/lane_prompts/calibration-policy.md +0 -0
  182. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/lane_prompts/context-driven-quality.md +0 -0
  183. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/lane_prompts/docs-design.md +0 -0
  184. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/lane_prompts/generic-programming.md +0 -0
  185. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/lane_prompts/operability.md +0 -0
  186. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/lane_prompts/package-runtime.md +0 -0
  187. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/lane_prompts/security-threat-model.md +0 -0
  188. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/product-support/code_mower +0 -0
  189. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/product-support/code_mower_standalone_pin.env +0 -0
  190. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/product-support/code_mower_standalone_shadow.sh +0 -0
  191. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/product-support/run_claude_audit_pr.sh +0 -0
  192. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/product-support/run_codex_audit_pr.sh +0 -0
  193. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/product-support/safe_gh_comment.py +0 -0
  194. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/providers.yml +0 -0
  195. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/reviewer-spend.example.json +0 -0
  196. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/reviewer-value-report.example.md +0 -0
  197. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/workflows/audit-label-cleanup.yml.j2 +0 -0
  198. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/workflows/hosted-bridge.yml.j2 +0 -0
  199. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/workflows/local-cli-audit.yml.j2 +0 -0
  200. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/workflows/private-standalone-shadow.yml.j2 +0 -0
  201. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/workflows/review-clear-stale.yml.j2 +0 -0
  202. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/workflows/saas-reviewer-labeler.yml.j2 +0 -0
  203. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/templates/workflows/trailer-comment-labeler.yml.j2 +0 -0
  204. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/trailer_comment_labeler.py +0 -0
  205. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower/versioning.py +0 -0
  206. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower.egg-info/dependency_links.txt +0 -0
  207. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower.egg-info/entry_points.txt +0 -0
  208. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower.egg-info/requires.txt +0 -0
  209. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/src/code_mower.egg-info/top_level.txt +0 -0
  210. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/builder-experiment.example.json +0 -0
  211. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/calibration-corpus.example.json +0 -0
  212. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/calibration-corpus.json +0 -0
  213. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/code-mower.yml.j2 +0 -0
  214. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/context-packs.example.json +0 -0
  215. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/cursor/BUGBOT.md +0 -0
  216. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/providers/acp_bridge.yml +0 -0
  217. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/providers/aider.yml +0 -0
  218. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/providers/antigravity_cli.yml +0 -0
  219. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/providers/claude_audit.yml +0 -0
  220. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/providers/claude_review.yml +0 -0
  221. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/providers/coderabbit_cli.yml +0 -0
  222. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/providers/codex.yml +0 -0
  223. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/providers/cursor_bugbot.yml +0 -0
  224. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/providers/devin.yml +0 -0
  225. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/providers/gemini_cli.yml +0 -0
  226. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/providers/gitar.yml +0 -0
  227. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/providers/greptile.yml +0 -0
  228. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/providers/hermes_cli.yml +0 -0
  229. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/providers/local_llm.yml +0 -0
  230. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/providers/qodo.yml +0 -0
  231. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/providers.yml +0 -0
  232. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/reviewer-spend.example.json +0 -0
  233. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/reviewer-value-report.example.md +0 -0
  234. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/workflows/audit-label-cleanup.yml.j2 +0 -0
  235. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/workflows/blind-review-artifacts-dry-run.yml.j2 +0 -0
  236. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/workflows/hosted-bridge.yml.j2 +0 -0
  237. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/workflows/local-cli-audit.yml.j2 +0 -0
  238. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/workflows/private-standalone-shadow.yml.j2 +0 -0
  239. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/workflows/review-clear-stale.yml.j2 +0 -0
  240. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/workflows/saas-reviewer-labeler.yml.j2 +0 -0
  241. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/templates/workflows/trailer-comment-labeler.yml.j2 +0 -0
  242. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_calibration_effect_report.py +0 -0
  243. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_calibration_truth.py +0 -0
  244. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_checks.py +0 -0
  245. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_claude_cli_bounce.py +0 -0
  246. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_clear_stale.py +0 -0
  247. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_cloud_client.py +0 -0
  248. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_cloud_git_metadata.py +0 -0
  249. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_cloud_manifest.py +0 -0
  250. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_cloud_reports.py +0 -0
  251. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_coderabbit_cli_audit_pr.py +0 -0
  252. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_doctor_actions_cost_summary.py +0 -0
  253. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_doctor_actions_failure_selection.py +0 -0
  254. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_doctor_github_checks.py +0 -0
  255. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_doctor_output.py +0 -0
  256. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_doctor_privacy.py +0 -0
  257. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_doctor_provider_env_required.py +0 -0
  258. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_doctor_provider_env_tokens.py +0 -0
  259. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_doctor_provider_probe.py +0 -0
  260. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_doctor_registry.py +0 -0
  261. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_local_llm_github_helpers.py +0 -0
  262. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_provider_runners_comments.py +0 -0
  263. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_provider_runners_git.py +0 -0
  264. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_provider_runners_github_pr.py +0 -0
  265. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_provider_runners_pr_worktree.py +0 -0
  266. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_provider_runners_process.py +0 -0
  267. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_provider_runners_repo_paths.py +0 -0
  268. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_provider_runners_text_schema.py +0 -0
  269. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_provider_runners_workspace.py +0 -0
  270. {code_mower-0.5.0b14 → code_mower-0.5.0b16}/tests/test_reviewer_metrics_core.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-mower
3
- Version: 0.5.0b14
3
+ Version: 0.5.0b16
4
4
  Summary: Multi-reviewer AI code audit orchestration
5
5
  License-Expression: Apache-2.0
6
6
  Requires-Python: >=3.11
@@ -101,11 +101,11 @@ Code Mower currently targets Python 3.11+; Python 3.12 is recommended.
101
101
 
102
102
  ```bash
103
103
  python3.12 --version
104
- pipx install --python python3.12 code-mower==0.5.0b14
104
+ pipx install --python python3.12 code-mower==0.5.0b16
105
105
  code-mower --version
106
106
  ```
107
107
 
108
- `0.5.0b14` is a beta release. Until Code Mower publishes a stable `1.0`
108
+ `0.5.0b16` is a beta release. Until Code Mower publishes a stable `1.0`
109
109
  line, use the explicit beta version above or allow prereleases with:
110
110
 
111
111
  ```bash
@@ -148,7 +148,7 @@ First-time command map: [docs/launch-command-surface.md](docs/launch-command-sur
148
148
 
149
149
  The current PyPI beta has been rehearsed end-to-end from a clean install:
150
150
  [First-User Install Rehearsal](docs/first-user-install-rehearsal.md) records
151
- the latest 10/10 public-package readiness proof for `code-mower==0.5.0b14`.
151
+ the latest 10/10 public-package readiness proof for `code-mower==0.5.0b16`.
152
152
 
153
153
  ## Why Not Just Run Codex Or Claude Yourself?
154
154
 
@@ -253,9 +253,9 @@ best?" to "which AI builder plus reviewer loop ships best on this product?" See
253
253
 
254
254
  ## Installation Status
255
255
 
256
- The current public beta is `v0.5.0-beta.14` from
256
+ The current public beta is `v0.5.0-beta.16` from
257
257
  [codemower-ai/code-mower](https://github.com/codemower-ai/code-mower), published
258
- as `code-mower==0.5.0b14` on [PyPI](https://pypi.org/project/code-mower/).
258
+ as `code-mower==0.5.0b16` on [PyPI](https://pypi.org/project/code-mower/).
259
259
  GitHub releases remain the auditable source for tags, build artifacts, and
260
260
  release notes.
261
261
 
@@ -86,11 +86,11 @@ Code Mower currently targets Python 3.11+; Python 3.12 is recommended.
86
86
 
87
87
  ```bash
88
88
  python3.12 --version
89
- pipx install --python python3.12 code-mower==0.5.0b14
89
+ pipx install --python python3.12 code-mower==0.5.0b16
90
90
  code-mower --version
91
91
  ```
92
92
 
93
- `0.5.0b14` is a beta release. Until Code Mower publishes a stable `1.0`
93
+ `0.5.0b16` is a beta release. Until Code Mower publishes a stable `1.0`
94
94
  line, use the explicit beta version above or allow prereleases with:
95
95
 
96
96
  ```bash
@@ -133,7 +133,7 @@ First-time command map: [docs/launch-command-surface.md](docs/launch-command-sur
133
133
 
134
134
  The current PyPI beta has been rehearsed end-to-end from a clean install:
135
135
  [First-User Install Rehearsal](docs/first-user-install-rehearsal.md) records
136
- the latest 10/10 public-package readiness proof for `code-mower==0.5.0b14`.
136
+ the latest 10/10 public-package readiness proof for `code-mower==0.5.0b16`.
137
137
 
138
138
  ## Why Not Just Run Codex Or Claude Yourself?
139
139
 
@@ -238,9 +238,9 @@ best?" to "which AI builder plus reviewer loop ships best on this product?" See
238
238
 
239
239
  ## Installation Status
240
240
 
241
- The current public beta is `v0.5.0-beta.14` from
241
+ The current public beta is `v0.5.0-beta.16` from
242
242
  [codemower-ai/code-mower](https://github.com/codemower-ai/code-mower), published
243
- as `code-mower==0.5.0b14` on [PyPI](https://pypi.org/project/code-mower/).
243
+ as `code-mower==0.5.0b16` on [PyPI](https://pypi.org/project/code-mower/).
244
244
  GitHub releases remain the auditable source for tags, build artifacts, and
245
245
  release notes.
246
246
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "code-mower"
7
- version = "0.5.0b14"
7
+ version = "0.5.0b16"
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.0b14"
3
+ __version__ = "0.5.0b16"
@@ -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.0b14`, or run source checkouts with "
17
+ "`pipx install code-mower==0.5.0b16`, or run source checkouts with "
18
18
  "`PYTHONPATH=src python -m code_mower.cli`."
19
19
  )
20
20
 
@@ -36,6 +36,7 @@ from .presets import (
36
36
  )
37
37
  from .providers import (
38
38
  check_lane_runtime,
39
+ check_review_hygiene,
39
40
  effective_lane,
40
41
  provider_template_coverage,
41
42
  selected_lanes,
@@ -83,6 +84,7 @@ __all__ = [
83
84
  "check_github_auth_surface",
84
85
  "check_github_setup",
85
86
  "check_lane_runtime",
87
+ "check_review_hygiene",
86
88
  "check_pytest",
87
89
  "check_python_runtime",
88
90
  "check_ripgrep",
@@ -0,0 +1,87 @@
1
+ """Review-hygiene checks for merge-authority provider lanes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Mapping
6
+
7
+ from .common import DoctorCheck, STATUS_FAIL, STATUS_PASS, STATUS_SKIP, as_sequence
8
+
9
+
10
+ def check_review_hygiene(
11
+ lane_id: str,
12
+ lane: Mapping[str, Any],
13
+ *,
14
+ effective_lane: Mapping[str, Any] | None = None,
15
+ ) -> DoctorCheck:
16
+ """Verify that merge-authority lanes have stale terminal-label protection."""
17
+
18
+ merge_authority_lane = effective_lane if effective_lane is not None else lane
19
+ if not bool(merge_authority_lane.get("merge_authority", False)):
20
+ return DoctorCheck(
21
+ name="provider.review_hygiene",
22
+ status=STATUS_SKIP,
23
+ lane=lane_id,
24
+ message="lane is not merge authority; stale terminal-label guard is optional",
25
+ )
26
+
27
+ review_hygiene = lane.get("review_hygiene")
28
+ if not isinstance(review_hygiene, Mapping):
29
+ review_hygiene = {}
30
+
31
+ workflow = str(review_hygiene.get("workflow") or "").strip()
32
+ token_env = str(review_hygiene.get("token_env") or "").strip()
33
+ dispatch_workflow = str(review_hygiene.get("dispatch_workflow") or "").strip()
34
+ trusted_authors = tuple(str(author) for author in as_sequence(review_hygiene.get("trusted_authors", [])))
35
+ missing: list[str] = []
36
+ if not workflow:
37
+ missing.append("workflow")
38
+ if not token_env:
39
+ missing.append("token_env")
40
+
41
+ detail = {
42
+ "workflow": workflow,
43
+ "token_env": token_env,
44
+ "dispatch_workflow": dispatch_workflow,
45
+ "trusted_authors": list(trusted_authors),
46
+ "merge_authority": True,
47
+ }
48
+
49
+ if missing:
50
+ return DoctorCheck(
51
+ name="provider.review_hygiene",
52
+ status=STATUS_FAIL,
53
+ lane=lane_id,
54
+ message=(
55
+ "merge-authority lane is missing stale terminal-label guard config: "
56
+ + ", ".join(missing)
57
+ ),
58
+ detail={**detail, "missing": missing},
59
+ remediation=(
60
+ "Add review_hygiene.workflow and review_hygiene.token_env to this "
61
+ "lane, or regenerate workflows with `code-mower init --easy --apply`."
62
+ ),
63
+ )
64
+
65
+ if token_env != "GITHUB_TOKEN":
66
+ return DoctorCheck(
67
+ name="provider.review_hygiene",
68
+ status=STATUS_FAIL,
69
+ lane=lane_id,
70
+ message=(
71
+ "merge-authority lane uses unsupported stale terminal-label token_env: "
72
+ f"{token_env}"
73
+ ),
74
+ detail={**detail, "supported_token_env": "GITHUB_TOKEN"},
75
+ remediation=(
76
+ "Use review_hygiene.token_env: GITHUB_TOKEN, or update the generated "
77
+ "clear-stale workflow template before relying on a custom token."
78
+ ),
79
+ )
80
+
81
+ return DoctorCheck(
82
+ name="provider.review_hygiene",
83
+ status=STATUS_PASS,
84
+ lane=lane_id,
85
+ message=f"stale terminal-label guard configured via {workflow}",
86
+ detail=detail,
87
+ )
@@ -24,9 +24,11 @@ from .provider_probe import (
24
24
  evaluate_json_probe,
25
25
  local_cli_probe_remediation,
26
26
  )
27
+ from .provider_review_hygiene import check_review_hygiene
27
28
 
28
29
  __all__ = [
29
30
  "check_lane_runtime",
31
+ "check_review_hygiene",
30
32
  "effective_lane",
31
33
  "evaluate_json_probe",
32
34
  "local_cli_probe_remediation",
@@ -109,10 +111,13 @@ def check_lane_runtime(
109
111
  lane_id: str,
110
112
  lane: Mapping[str, Any],
111
113
  *,
114
+ source_lane: Mapping[str, Any] | None = None,
112
115
  probe_runtime: bool,
113
116
  http_timeout: int,
114
117
  ) -> list[DoctorCheck]:
115
- checks = check_token_env(lane_id, lane)
118
+ hygiene_source = source_lane if source_lane is not None else lane
119
+ checks = [check_review_hygiene(lane_id, hygiene_source, effective_lane=lane)]
120
+ checks.extend(check_token_env(lane_id, lane))
116
121
  checks.extend(check_required_env(lane_id, lane))
117
122
  driver = str(lane.get("driver", ""))
118
123
  if driver == "local_cli":
@@ -162,6 +162,7 @@ def run_doctor(
162
162
  check_lane_runtime(
163
163
  lane_id,
164
164
  effective,
165
+ source_lane=lane,
165
166
  probe_runtime=probe_runtime,
166
167
  http_timeout=http_timeout,
167
168
  )
@@ -187,6 +187,11 @@ PACKAGE_FILES = (
187
187
  "src/code_mower/doctor_checks/provider_probe_remediation.py",
188
188
  "core",
189
189
  ),
190
+ (
191
+ "src/code_mower/doctor_checks/provider_review_hygiene.py",
192
+ "src/code_mower/doctor_checks/provider_review_hygiene.py",
193
+ "core",
194
+ ),
190
195
  ("src/code_mower/doctor_checks/privacy.py", "src/code_mower/doctor_checks/privacy.py", "core"),
191
196
  ("src/code_mower/doctor_checks/providers.py", "src/code_mower/doctor_checks/providers.py", "core"),
192
197
  ("src/code_mower/doctor_checks/registry.py", "src/code_mower/doctor_checks/registry.py", "core"),
@@ -143,22 +143,50 @@ def _first_text(values: Any) -> str:
143
143
  return ""
144
144
 
145
145
 
146
+ def _text_values(values: Any) -> tuple[str, ...]:
147
+ if isinstance(values, str):
148
+ text = _safe_text(values)
149
+ return (text,) if text else ()
150
+ if isinstance(values, (list, tuple)):
151
+ result: list[str] = []
152
+ for value in values:
153
+ text = _safe_text(value)
154
+ if text and text not in result:
155
+ result.append(text)
156
+ return tuple(result)
157
+ return ()
158
+
159
+
146
160
  def _env_value(config: Mapping[str, Any], key: str) -> str:
147
161
  env_name = _safe_text(config.get(key))
148
162
  return _safe_text(os.environ.get(env_name)) if env_name else ""
149
163
 
150
164
 
151
- def _configured_command(config: Mapping[str, Any], provider: str) -> str:
165
+ def _configured_command_candidates(
166
+ config: Mapping[str, Any],
167
+ provider: str,
168
+ ) -> tuple[str, ...]:
152
169
  override = _env_value(config, "command_env")
153
170
  if override:
154
- return override
171
+ return (override,)
172
+ candidates: list[str] = []
155
173
  command = _safe_text(config.get("command"))
156
174
  if command:
157
- return command
158
- alternate = _first_text(config.get("alternate_commands"))
159
- if alternate:
160
- return alternate
161
- return provider
175
+ candidates.append(command)
176
+ for alternate in _text_values(config.get("alternate_commands")):
177
+ if alternate not in candidates:
178
+ candidates.append(alternate)
179
+ if not candidates:
180
+ candidates.append(provider)
181
+ return tuple(candidates)
182
+
183
+
184
+ def _select_available_command(candidates: tuple[str, ...]) -> tuple[str, str]:
185
+ for command in candidates:
186
+ resolved = shutil.which(command)
187
+ if resolved:
188
+ return command, resolved
189
+ return (candidates[0] if candidates else "", "")
162
190
 
163
191
 
164
192
  def build_provider_lane_tool_provenance(
@@ -178,7 +206,8 @@ def build_provider_lane_tool_provenance(
178
206
  config = _provider_config(lane)
179
207
  provider = _safe_text(_lane_value(lane, "provider")) or lane_id
180
208
  driver = _safe_text(_lane_value(lane, "driver")) or "unknown"
181
- command = _configured_command(config, provider)
209
+ command_candidates = _configured_command_candidates(config, provider)
210
+ command, resolved_command = _select_available_command(command_candidates)
182
211
  model = _env_value(config, "model_env") or _safe_text(config.get("default_model"))
183
212
  provider_from_env = _env_value(config, "provider_env")
184
213
  if provider_from_env:
@@ -197,16 +226,16 @@ def build_provider_lane_tool_provenance(
197
226
  "lane_id": lane_id,
198
227
  "driver": driver,
199
228
  "command": command if driver == "local_cli" else "",
229
+ "command_candidates": list(command_candidates) if driver == "local_cli" else [],
200
230
  "model_known": bool(model),
201
231
  "version_known": False,
202
232
  }
203
233
  tool_version = ""
204
234
  if driver == "local_cli" and include_version_probe and command:
205
- resolved = shutil.which(command)
206
- detail["command_found"] = bool(resolved)
207
- if resolved:
208
- detail["path_basename"] = os.path.basename(resolved)
209
- version_detail = detect_local_cli_version(resolved)
235
+ detail["command_found"] = bool(resolved_command)
236
+ if resolved_command:
237
+ detail["path_basename"] = os.path.basename(resolved_command)
238
+ version_detail = detect_local_cli_version(resolved_command)
210
239
  tool_version = _safe_text(version_detail.get("tool_version"))
211
240
  detail["version_known"] = bool(tool_version)
212
241
  detail["tool_version_available"] = bool(
@@ -358,6 +358,9 @@ def render_release_readiness(repo_path: Path) -> dict[str, Any]:
358
358
  if (
359
359
  _needs_job(testpypi_job, "verify-distributions")
360
360
  and "inputs.publish_testpypi" in testpypi_job_text
361
+ and "inputs.publish_testpypi == true" in testpypi_job_text
362
+ and "github.event_name == 'workflow_dispatch'" in testpypi_job_text
363
+ and "github.event_name == 'release'" in testpypi_job_text
361
364
  and "CODE_MOWER_TESTPYPI_PUBLISH" in testpypi_job_text
362
365
  and _job_uses_environment(testpypi_job, "testpypi")
363
366
  and _permissions_include(testpypi_job, "id-token", "write")
@@ -376,6 +379,9 @@ def render_release_readiness(repo_path: Path) -> dict[str, Any]:
376
379
  if (
377
380
  _needs_job(pypi_job, "verify-distributions")
378
381
  and "inputs.publish_pypi" in pypi_job_text
382
+ and "inputs.publish_pypi == true" in pypi_job_text
383
+ and "github.event_name == 'workflow_dispatch'" in pypi_job_text
384
+ and "github.event_name == 'release'" in pypi_job_text
379
385
  and "CODE_MOWER_PYPI_PUBLISH" in pypi_job_text
380
386
  and _job_uses_environment(pypi_job, "pypi")
381
387
  and _permissions_include(pypi_job, "id-token", "write")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-mower
3
- Version: 0.5.0b14
3
+ Version: 0.5.0b16
4
4
  Summary: Multi-reviewer AI code audit orchestration
5
5
  License-Expression: Apache-2.0
6
6
  Requires-Python: >=3.11
@@ -101,11 +101,11 @@ Code Mower currently targets Python 3.11+; Python 3.12 is recommended.
101
101
 
102
102
  ```bash
103
103
  python3.12 --version
104
- pipx install --python python3.12 code-mower==0.5.0b14
104
+ pipx install --python python3.12 code-mower==0.5.0b16
105
105
  code-mower --version
106
106
  ```
107
107
 
108
- `0.5.0b14` is a beta release. Until Code Mower publishes a stable `1.0`
108
+ `0.5.0b16` is a beta release. Until Code Mower publishes a stable `1.0`
109
109
  line, use the explicit beta version above or allow prereleases with:
110
110
 
111
111
  ```bash
@@ -148,7 +148,7 @@ First-time command map: [docs/launch-command-surface.md](docs/launch-command-sur
148
148
 
149
149
  The current PyPI beta has been rehearsed end-to-end from a clean install:
150
150
  [First-User Install Rehearsal](docs/first-user-install-rehearsal.md) records
151
- the latest 10/10 public-package readiness proof for `code-mower==0.5.0b14`.
151
+ the latest 10/10 public-package readiness proof for `code-mower==0.5.0b16`.
152
152
 
153
153
  ## Why Not Just Run Codex Or Claude Yourself?
154
154
 
@@ -253,9 +253,9 @@ best?" to "which AI builder plus reviewer loop ships best on this product?" See
253
253
 
254
254
  ## Installation Status
255
255
 
256
- The current public beta is `v0.5.0-beta.14` from
256
+ The current public beta is `v0.5.0-beta.16` from
257
257
  [codemower-ai/code-mower](https://github.com/codemower-ai/code-mower), published
258
- as `code-mower==0.5.0b14` on [PyPI](https://pypi.org/project/code-mower/).
258
+ as `code-mower==0.5.0b16` on [PyPI](https://pypi.org/project/code-mower/).
259
259
  GitHub releases remain the auditable source for tags, build artifacts, and
260
260
  release notes.
261
261
 
@@ -143,6 +143,7 @@ src/code_mower/doctor_checks/provider_probe_auth.py
143
143
  src/code_mower/doctor_checks/provider_probe_evaluation.py
144
144
  src/code_mower/doctor_checks/provider_probe_json.py
145
145
  src/code_mower/doctor_checks/provider_probe_remediation.py
146
+ src/code_mower/doctor_checks/provider_review_hygiene.py
146
147
  src/code_mower/doctor_checks/providers.py
147
148
  src/code_mower/doctor_checks/registry.py
148
149
  src/code_mower/doctor_checks/runner.py
@@ -251,6 +252,7 @@ tests/test_doctor_privacy.py
251
252
  tests/test_doctor_provider_env_required.py
252
253
  tests/test_doctor_provider_env_tokens.py
253
254
  tests/test_doctor_provider_probe.py
255
+ tests/test_doctor_provider_review_hygiene.py
254
256
  tests/test_doctor_registry.py
255
257
  tests/test_local_llm_github_helpers.py
256
258
  tests/test_provider_local_cli.py
@@ -107,6 +107,7 @@ class DoctorBoundaryTests(unittest.TestCase):
107
107
  "code_mower.doctor_checks.provider_probe_evaluation",
108
108
  "code_mower.doctor_checks.provider_probe_json",
109
109
  "code_mower.doctor_checks.provider_probe_remediation",
110
+ "code_mower.doctor_checks.provider_review_hygiene",
110
111
  "code_mower.doctor_checks.providers",
111
112
  "code_mower.doctor_checks.privacy",
112
113
  "code_mower.doctor_checks.registry",
@@ -0,0 +1,98 @@
1
+ from __future__ import annotations
2
+
3
+ import unittest
4
+
5
+ from code_mower.doctor_checks.provider_review_hygiene import check_review_hygiene
6
+
7
+
8
+ class DoctorProviderReviewHygieneTests(unittest.TestCase):
9
+ def test_merge_authority_lane_requires_stale_guard(self) -> None:
10
+ check = check_review_hygiene(
11
+ "devin",
12
+ {
13
+ "merge_authority": True,
14
+ "review_hygiene": {},
15
+ },
16
+ )
17
+
18
+ self.assertEqual(check.status, "fail")
19
+ self.assertEqual(check.name, "provider.review_hygiene")
20
+ self.assertEqual(check.lane, "devin")
21
+ self.assertIn("workflow", check.detail["missing"])
22
+ self.assertIn("token_env", check.detail["missing"])
23
+ self.assertIn("code-mower init --easy --apply", check.remediation)
24
+
25
+ def test_template_defaults_do_not_satisfy_source_hygiene_requirement(self) -> None:
26
+ check = check_review_hygiene(
27
+ "codex",
28
+ {},
29
+ effective_lane={
30
+ "merge_authority": True,
31
+ "review_hygiene": {
32
+ "workflow": ".github/workflows/codex-clear-stale.yml",
33
+ "token_env": "GITHUB_TOKEN",
34
+ },
35
+ },
36
+ )
37
+
38
+ self.assertEqual(check.status, "fail")
39
+ self.assertEqual(check.detail["missing"], ["workflow", "token_env"])
40
+ self.assertIn("missing stale terminal-label guard config", check.message)
41
+
42
+ def test_merge_authority_lane_passes_with_stale_guard(self) -> None:
43
+ check = check_review_hygiene(
44
+ "codex",
45
+ {
46
+ "merge_authority": True,
47
+ "review_hygiene": {
48
+ "workflow": ".github/workflows/codex-clear-stale.yml",
49
+ "token_env": "GITHUB_TOKEN",
50
+ "dispatch_workflow": "codex-audit-labeler.yml",
51
+ "trusted_authors": ["codex[bot]"],
52
+ },
53
+ },
54
+ )
55
+
56
+ self.assertEqual(check.status, "pass")
57
+ self.assertEqual(
58
+ check.message,
59
+ "stale terminal-label guard configured via .github/workflows/codex-clear-stale.yml",
60
+ )
61
+ self.assertEqual(check.detail["workflow"], ".github/workflows/codex-clear-stale.yml")
62
+ self.assertEqual(check.detail["token_env"], "GITHUB_TOKEN")
63
+ self.assertEqual(check.detail["dispatch_workflow"], "codex-audit-labeler.yml")
64
+ self.assertEqual(check.detail["trusted_authors"], ["codex[bot]"])
65
+
66
+ def test_merge_authority_lane_rejects_custom_hygiene_token_until_template_supports_it(self) -> None:
67
+ check = check_review_hygiene(
68
+ "devin",
69
+ {
70
+ "merge_authority": True,
71
+ "review_hygiene": {
72
+ "workflow": ".github/workflows/devin-clear-stale.yml",
73
+ "token_env": "DEVIN_AUDIT_LABEL_TOKEN",
74
+ },
75
+ },
76
+ )
77
+
78
+ self.assertEqual(check.status, "fail")
79
+ self.assertEqual(check.detail["token_env"], "DEVIN_AUDIT_LABEL_TOKEN")
80
+ self.assertEqual(check.detail["supported_token_env"], "GITHUB_TOKEN")
81
+ self.assertIn("unsupported", check.message)
82
+
83
+ def test_informational_lane_skips_stale_guard_requirement(self) -> None:
84
+ check = check_review_hygiene(
85
+ "gitar",
86
+ {
87
+ "merge_authority": False,
88
+ "review_hygiene": {},
89
+ },
90
+ )
91
+
92
+ self.assertEqual(check.status, "skip")
93
+ self.assertEqual(check.lane, "gitar")
94
+ self.assertIn("optional", check.message)
95
+
96
+
97
+ if __name__ == "__main__":
98
+ unittest.main()
@@ -83,6 +83,41 @@ def test_provider_lane_tool_provenance_reads_version_and_model_env(
83
83
  assert tool["model"] == "model-v1"
84
84
  assert tool["lens"] == "security-threat-model"
85
85
  assert detail["lane_id"] == "gemini_cli"
86
+ assert detail["command_candidates"] == ["provider-cli"]
86
87
  assert detail["command_found"] is True
87
88
  assert detail["model_known"] is True
88
89
  assert detail["version_known"] is True
90
+
91
+
92
+ def test_provider_lane_tool_provenance_uses_available_alternate_command(
93
+ monkeypatch,
94
+ tmp_path: Path,
95
+ ) -> None:
96
+ executable = _write_executable(
97
+ tmp_path / "antigravity",
98
+ "#!/bin/sh\nprintf 'antigravity 1.2.3\\n'\n",
99
+ )
100
+ monkeypatch.setenv("PATH", os.fspath(executable.parent))
101
+ lane = {
102
+ "driver": "local_cli",
103
+ "provider": "antigravity",
104
+ "provider_config": {
105
+ "command": "agy",
106
+ "alternate_commands": ("antigravity",),
107
+ "model_env": "ANTIGRAVITY_MODEL",
108
+ },
109
+ }
110
+
111
+ tool, detail = build_provider_lane_tool_provenance(
112
+ "antigravity_cli",
113
+ lane,
114
+ source="unit-test",
115
+ )
116
+
117
+ assert tool["tool_name"] == "antigravity"
118
+ assert tool["tool_version"] == "antigravity 1.2.3"
119
+ assert tool["provider"] == "antigravity"
120
+ assert detail["command"] == "antigravity"
121
+ assert detail["command_candidates"] == ["agy", "antigravity"]
122
+ assert detail["command_found"] is True
123
+ assert detail["version_known"] is True