code-mower 0.5.0b49__tar.gz → 0.5.0b51__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 (308) hide show
  1. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/PKG-INFO +6 -6
  2. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/README.md +5 -5
  3. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/pyproject.toml +1 -1
  4. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/__init__.py +1 -1
  5. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/cli.py +1 -1
  6. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/cloud.py +10 -0
  7. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/cloud_client/operations.py +122 -11
  8. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/config.py +39 -2
  9. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/__init__.py +8 -0
  10. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/github.py +10 -0
  11. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/github_branch.py +40 -2
  12. code_mower-0.5.0b51/src/code_mower/doctor_checks/github_human_token.py +199 -0
  13. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/gate_health.py +228 -4
  14. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/init.py +324 -4
  15. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/lane_configs/claude.py +1 -1
  16. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/lane_configs/codex.py +1 -1
  17. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/package_manifest.py +9 -0
  18. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/provider_registry.py +2 -2
  19. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/code-mower.example.yml +11 -2
  20. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/lane_prompts/operability.md +1 -0
  21. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/lane_prompts/security-threat-model.md +1 -0
  22. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/product-support/status_report.py +30 -4
  23. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/providers.yml +2 -2
  24. {code_mower-0.5.0b49 → code_mower-0.5.0b51/src/code_mower}/templates/workflows/builder-provenance.yml.j2 +1 -1
  25. code_mower-0.5.0b51/src/code_mower/templates/workflows/code-mower-agent-pr-labeler.yml.j2 +138 -0
  26. code_mower-0.5.0b51/src/code_mower/templates/workflows/code-mower-fix-round-dispatch.yml.j2 +153 -0
  27. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/workflows/code-mower-gate-health.yml.j2 +5 -0
  28. {code_mower-0.5.0b49 → code_mower-0.5.0b51/src/code_mower}/templates/workflows/code-mower-gate.yml.j2 +7 -1
  29. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/workflows/needs-owner-notify.yml.j2 +9 -1
  30. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/workflows/weekly-status.yml.j2 +2 -0
  31. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower.egg-info/PKG-INFO +6 -6
  32. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower.egg-info/SOURCES.txt +5 -0
  33. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/providers/claude_audit.yml +1 -1
  34. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/providers/codex.yml +1 -1
  35. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/providers.yml +2 -2
  36. {code_mower-0.5.0b49/src/code_mower → code_mower-0.5.0b51}/templates/workflows/builder-provenance.yml.j2 +1 -1
  37. code_mower-0.5.0b51/templates/workflows/code-mower-agent-pr-labeler.yml.j2 +138 -0
  38. code_mower-0.5.0b51/templates/workflows/code-mower-fix-round-dispatch.yml.j2 +153 -0
  39. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/workflows/code-mower-gate-health.yml.j2 +5 -0
  40. {code_mower-0.5.0b49/src/code_mower → code_mower-0.5.0b51}/templates/workflows/code-mower-gate.yml.j2 +7 -1
  41. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_builder_identity.py +38 -0
  42. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_doctor_github_checks.py +143 -2
  43. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_gate_health.py +214 -4
  44. code_mower-0.5.0b51/tests/test_prompts.py +27 -0
  45. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_release_hygiene.py +215 -42
  46. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_reviewer_metrics_core.py +190 -0
  47. code_mower-0.5.0b49/tests/test_prompts.py +0 -18
  48. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/CODE_OF_CONDUCT.md +0 -0
  49. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/LICENSE +0 -0
  50. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/MANIFEST.in +0 -0
  51. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/NOTICE +0 -0
  52. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/SUPPORT.md +0 -0
  53. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/requirements/requirements.txt +0 -0
  54. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/setup.cfg +0 -0
  55. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/adapters/__init__.py +0 -0
  56. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/adapters/_base.py +0 -0
  57. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/adapters/cursor_bugbot.py +0 -0
  58. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/adapters/gitar.py +0 -0
  59. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/adapters/greptile.py +0 -0
  60. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/adapters/qodo.py +0 -0
  61. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/antigravity_cli_audit_pr.py +0 -0
  62. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/audit_handoff_log.py +0 -0
  63. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/audit_labeler_lib.py +0 -0
  64. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/audit_progress.py +0 -0
  65. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/blind_review_artifacts.py +0 -0
  66. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/blind_review_coordinator.py +0 -0
  67. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/bootstrap.py +0 -0
  68. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/builder_experiment.py +0 -0
  69. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/builder_runs.py +0 -0
  70. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/calibration/__init__.py +0 -0
  71. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/calibration/arms.py +0 -0
  72. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/calibration/auto_discovery.py +0 -0
  73. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/calibration/commands.py +0 -0
  74. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/calibration/context_inputs.py +0 -0
  75. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/calibration/corpus.py +0 -0
  76. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/calibration/effect_report.py +0 -0
  77. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/calibration/evidence.py +0 -0
  78. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/calibration/evidence_report.py +0 -0
  79. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/calibration/identity.py +0 -0
  80. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/calibration/metrics.py +0 -0
  81. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/calibration/overlap.py +0 -0
  82. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/calibration/planning.py +0 -0
  83. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/calibration/policy.py +0 -0
  84. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/calibration/results.py +0 -0
  85. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/calibration/run_results.py +0 -0
  86. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/calibration/run_status.py +0 -0
  87. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/calibration/runner.py +0 -0
  88. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/calibration/truth.py +0 -0
  89. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/calibration/value_report.py +0 -0
  90. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/checks.py +0 -0
  91. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/claude_audit_pr.py +0 -0
  92. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/claude_cli_bounce.py +0 -0
  93. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/claude_cli_environment.py +0 -0
  94. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/clear_stale.py +0 -0
  95. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/cloud_client/__init__.py +0 -0
  96. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/cloud_client/bundle.py +0 -0
  97. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/cloud_client/doctor.py +0 -0
  98. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/cloud_client/dogfood.py +0 -0
  99. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/cloud_client/endpoints.py +0 -0
  100. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/cloud_client/errors.py +0 -0
  101. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/cloud_client/events.py +0 -0
  102. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/cloud_client/export.py +0 -0
  103. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/cloud_client/git_metadata.py +0 -0
  104. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/cloud_client/manifest.py +0 -0
  105. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/cloud_client/reports.py +0 -0
  106. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/cloud_client/setup.py +0 -0
  107. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/cloud_client/upload.py +0 -0
  108. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/code_mower_calibration.py +0 -0
  109. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/code_mower_context_packs.py +0 -0
  110. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/code_mower_merge.py +0 -0
  111. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/code_mower_telemetry.py +0 -0
  112. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/coderabbit_cli_audit_pr.py +0 -0
  113. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/codex_audit_env_preflight.py +0 -0
  114. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/codex_audit_pr.py +0 -0
  115. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/codex_audit_schema_smoke.py +0 -0
  116. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/codex_audit_verdict.schema.json +0 -0
  117. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor.py +0 -0
  118. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/cloud.py +0 -0
  119. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/common.py +0 -0
  120. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/github_actions.py +0 -0
  121. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/github_actions_cost.py +0 -0
  122. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/github_actions_cost_summary.py +0 -0
  123. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/github_actions_failure_annotations.py +0 -0
  124. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/github_actions_failure_models.py +0 -0
  125. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/github_actions_failure_scan.py +0 -0
  126. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/github_actions_failure_selection.py +0 -0
  127. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/github_actions_failures.py +0 -0
  128. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/github_actions_permissions.py +0 -0
  129. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/github_api.py +0 -0
  130. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/github_config.py +0 -0
  131. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/github_provider.py +0 -0
  132. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/github_repo.py +0 -0
  133. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/groups.py +0 -0
  134. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/models.py +0 -0
  135. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/output.py +0 -0
  136. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/presets.py +0 -0
  137. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/privacy.py +0 -0
  138. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/provider_api_model.py +0 -0
  139. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/provider_api_model_openai.py +0 -0
  140. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/provider_api_model_profiles.py +0 -0
  141. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/provider_env.py +0 -0
  142. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/provider_env_required.py +0 -0
  143. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/provider_env_tokens.py +0 -0
  144. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/provider_local_cli.py +0 -0
  145. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/provider_local_cli_commands.py +0 -0
  146. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/provider_local_cli_probe_config.py +0 -0
  147. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/provider_probe.py +0 -0
  148. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/provider_probe_auth.py +0 -0
  149. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/provider_probe_evaluation.py +0 -0
  150. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/provider_probe_json.py +0 -0
  151. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/provider_probe_remediation.py +0 -0
  152. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/provider_review_hygiene.py +0 -0
  153. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/providers.py +0 -0
  154. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/registry.py +0 -0
  155. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/runner.py +0 -0
  156. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/runtime.py +0 -0
  157. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/doctor_checks/runtime_github_auth.py +0 -0
  158. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/gemini_cli_audit_pr.py +0 -0
  159. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/grok_build_audit_pr.py +0 -0
  160. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/hermes_cli_audit_pr.py +0 -0
  161. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/lane_configs/__init__.py +0 -0
  162. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/lane_configs/aider.py +0 -0
  163. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/lane_configs/antigravity_cli.py +0 -0
  164. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/lane_configs/devin.py +0 -0
  165. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/lane_configs/gemini_cli.py +0 -0
  166. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/lane_configs/grok_build.py +0 -0
  167. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/lane_configs/hermes_cli.py +0 -0
  168. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/lane_configs/local_llm.py +0 -0
  169. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/local_llm_audit_pr.py +0 -0
  170. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/local_llm_bakeoff.py +0 -0
  171. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/local_llm_calibration.py +0 -0
  172. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/local_llm_profiles.py +0 -0
  173. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/migration.py +0 -0
  174. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/migration_install.py +0 -0
  175. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/migration_mirror.py +0 -0
  176. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/migration_readiness.py +0 -0
  177. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/migration_rehearsal.py +0 -0
  178. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/next_steps.py +0 -0
  179. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/package.py +0 -0
  180. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/package_content.py +0 -0
  181. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/package_paths.py +0 -0
  182. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/package_rendering.py +0 -0
  183. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/package_static.py +0 -0
  184. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/plan_context.py +0 -0
  185. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/prompts.py +0 -0
  186. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/provider_runners/__init__.py +0 -0
  187. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/provider_runners/comments.py +0 -0
  188. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/provider_runners/git.py +0 -0
  189. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/provider_runners/github_auth.py +0 -0
  190. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/provider_runners/github_pr.py +0 -0
  191. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/provider_runners/pr_worktree.py +0 -0
  192. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/provider_runners/process.py +0 -0
  193. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/provider_runners/repo_paths.py +0 -0
  194. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/provider_runners/text_schema.py +0 -0
  195. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/provider_runners/verdict_artifacts.py +0 -0
  196. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/provider_runners/workspace.py +0 -0
  197. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/providers/__init__.py +0 -0
  198. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/providers/local_cli.py +0 -0
  199. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/providers/provenance.py +0 -0
  200. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/release_readiness.py +0 -0
  201. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/reviewer_metrics.py +0 -0
  202. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/reviewer_spend.py +0 -0
  203. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/saas_reviewer_labeler.py +0 -0
  204. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/secrets.py +0 -0
  205. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/builder-experiment.example.json +0 -0
  206. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/calibration-corpus.example.json +0 -0
  207. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/calibration-corpus.json +0 -0
  208. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/context-packs.example.json +0 -0
  209. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/lane_prompts/base-audit.md +0 -0
  210. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/lane_prompts/calibration-policy.md +0 -0
  211. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/lane_prompts/context-driven-quality.md +0 -0
  212. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/lane_prompts/docs-design.md +0 -0
  213. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/lane_prompts/generic-programming.md +0 -0
  214. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/lane_prompts/package-runtime.md +0 -0
  215. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/lane_prompts/plan-conformance.md +0 -0
  216. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/product-support/code_mower +0 -0
  217. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/product-support/code_mower_standalone_pin.env +0 -0
  218. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/product-support/code_mower_standalone_shadow.sh +0 -0
  219. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/product-support/run_claude_audit_pr.sh +0 -0
  220. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/product-support/run_codex_audit_pr.sh +0 -0
  221. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/product-support/safe_gh_comment.py +0 -0
  222. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/reviewer-spend.example.json +0 -0
  223. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/reviewer-value-report.example.md +0 -0
  224. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/workflows/audit-label-cleanup.yml.j2 +0 -0
  225. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/workflows/hosted-bridge.yml.j2 +0 -0
  226. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/workflows/local-cli-audit.yml.j2 +0 -0
  227. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/workflows/private-standalone-shadow.yml.j2 +0 -0
  228. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/workflows/review-clear-stale.yml.j2 +0 -0
  229. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/workflows/saas-reviewer-labeler.yml.j2 +0 -0
  230. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/workflows/self-hosted-local-audit.yml.j2 +1 -1
  231. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/templates/workflows/trailer-comment-labeler.yml.j2 +0 -0
  232. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/trailer_comment_labeler.py +0 -0
  233. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/versioning.py +0 -0
  234. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower/work_orders.py +0 -0
  235. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower.egg-info/dependency_links.txt +0 -0
  236. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower.egg-info/entry_points.txt +0 -0
  237. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower.egg-info/requires.txt +0 -0
  238. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/src/code_mower.egg-info/top_level.txt +0 -0
  239. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/builder-experiment.example.json +0 -0
  240. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/calibration-corpus.example.json +0 -0
  241. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/calibration-corpus.json +0 -0
  242. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/code-mower.yml.j2 +0 -0
  243. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/context-packs.example.json +0 -0
  244. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/cursor/BUGBOT.md +0 -0
  245. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/providers/acp_bridge.yml +0 -0
  246. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/providers/aider.yml +0 -0
  247. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/providers/antigravity_cli.yml +0 -0
  248. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/providers/claude_review.yml +0 -0
  249. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/providers/coderabbit_cli.yml +0 -0
  250. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/providers/cursor_bugbot.yml +0 -0
  251. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/providers/devin.yml +0 -0
  252. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/providers/gemini_cli.yml +0 -0
  253. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/providers/gitar.yml +0 -0
  254. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/providers/greptile.yml +0 -0
  255. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/providers/hermes_cli.yml +0 -0
  256. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/providers/local_llm.yml +0 -0
  257. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/providers/qodo.yml +0 -0
  258. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/reviewer-spend.example.json +0 -0
  259. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/reviewer-value-report.example.md +0 -0
  260. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/workflows/audit-label-cleanup.yml.j2 +0 -0
  261. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/workflows/blind-review-artifacts-dry-run.yml.j2 +0 -0
  262. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/workflows/hosted-bridge.yml.j2 +0 -0
  263. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/workflows/local-cli-audit.yml.j2 +0 -0
  264. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/workflows/private-standalone-shadow.yml.j2 +0 -0
  265. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/workflows/review-clear-stale.yml.j2 +0 -0
  266. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/workflows/saas-reviewer-labeler.yml.j2 +0 -0
  267. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/workflows/self-hosted-local-audit.yml.j2 +1 -1
  268. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/templates/workflows/trailer-comment-labeler.yml.j2 +0 -0
  269. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_audit_comment_posture.py +0 -0
  270. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_builder_runs.py +0 -0
  271. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_calibration_effect_report.py +0 -0
  272. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_calibration_truth.py +0 -0
  273. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_checks.py +0 -0
  274. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_claude_audit_pr.py +0 -0
  275. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_claude_cli_bounce.py +0 -0
  276. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_clear_stale.py +0 -0
  277. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_cloud_client.py +0 -0
  278. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_cloud_git_metadata.py +0 -0
  279. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_cloud_manifest.py +0 -0
  280. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_cloud_reports.py +0 -0
  281. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_coderabbit_cli_audit_pr.py +0 -0
  282. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_codex_audit_env_preflight.py +0 -0
  283. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_codex_audit_pr.py +0 -0
  284. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_doctor_actions_cost_summary.py +0 -0
  285. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_doctor_actions_failure_selection.py +0 -0
  286. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_doctor_boundaries.py +0 -0
  287. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_doctor_output.py +0 -0
  288. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_doctor_privacy.py +0 -0
  289. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_doctor_provider_env_required.py +0 -0
  290. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_doctor_provider_env_tokens.py +0 -0
  291. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_doctor_provider_probe.py +0 -0
  292. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_doctor_provider_review_hygiene.py +0 -0
  293. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_doctor_registry.py +0 -0
  294. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_doctor_runtime.py +0 -0
  295. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_local_llm_github_helpers.py +0 -0
  296. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_plan_context.py +0 -0
  297. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_provider_local_cli.py +0 -0
  298. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_provider_runners_comments.py +0 -0
  299. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_provider_runners_git.py +0 -0
  300. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_provider_runners_github_pr.py +0 -0
  301. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_provider_runners_pr_worktree.py +0 -0
  302. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_provider_runners_process.py +0 -0
  303. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_provider_runners_repo_paths.py +0 -0
  304. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_provider_runners_text_schema.py +0 -0
  305. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_provider_runners_workspace.py +0 -0
  306. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_reviewer_spend.py +0 -0
  307. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/tests/test_trailer_comment_labeler.py +0 -0
  308. {code_mower-0.5.0b49 → code_mower-0.5.0b51}/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.0b49
3
+ Version: 0.5.0b51
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.0b49
106
+ pipx install --python python3.12 code-mower==0.5.0b51
107
107
  code-mower --version
108
108
  ```
109
109
 
110
- `0.5.0b49` is a beta release. Until Code Mower publishes a stable `1.0`
110
+ `0.5.0b51` 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.0b49`.
158
+ the latest 10/10 public-package readiness proof for `code-mower==0.5.0b51`.
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.49` from
313
+ The current public beta is `v0.5.0-beta.51` from
314
314
  [codemower-ai/code-mower](https://github.com/codemower-ai/code-mower), published
315
- as `code-mower==0.5.0b49` on [PyPI](https://pypi.org/project/code-mower/).
315
+ as `code-mower==0.5.0b51` 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.0b49
91
+ pipx install --python python3.12 code-mower==0.5.0b51
92
92
  code-mower --version
93
93
  ```
94
94
 
95
- `0.5.0b49` is a beta release. Until Code Mower publishes a stable `1.0`
95
+ `0.5.0b51` 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.0b49`.
143
+ the latest 10/10 public-package readiness proof for `code-mower==0.5.0b51`.
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.49` from
298
+ The current public beta is `v0.5.0-beta.51` from
299
299
  [codemower-ai/code-mower](https://github.com/codemower-ai/code-mower), published
300
- as `code-mower==0.5.0b49` on [PyPI](https://pypi.org/project/code-mower/).
300
+ as `code-mower==0.5.0b51` 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.0b49"
7
+ version = "0.5.0b51"
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.0b49"
3
+ __version__ = "0.5.0b51"
@@ -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.0b49`, or run source checkouts with "
17
+ "`pipx install code-mower==0.5.0b51`, or run source checkouts with "
18
18
  "`PYTHONPATH=src python -m code_mower.cli`."
19
19
  )
20
20
 
@@ -429,6 +429,15 @@ def main(argv: list[str] | None = None) -> int:
429
429
  action="store_true",
430
430
  help="include verdict head SHA metadata",
431
431
  )
432
+ reviewer_runs.add_argument(
433
+ "--spend",
434
+ type=Path,
435
+ default=None,
436
+ help=(
437
+ "reviewer spend ledger to merge into reviewer_run events; defaults "
438
+ "to .code-mower/reviewer-spend.json when present"
439
+ ),
440
+ )
432
441
  reviewer_runs.add_argument(
433
442
  "--endpoint",
434
443
  default=os.environ.get("CODE_MOWER_CLOUD_ENDPOINT", DEFAULT_UPLOAD_ENDPOINT),
@@ -686,6 +695,7 @@ def main(argv: list[str] | None = None) -> int:
686
695
  yes=args.yes,
687
696
  timeout=args.timeout,
688
697
  include_git_ref=args.include_git_ref,
698
+ spend_path=args.spend,
689
699
  )
690
700
  if args.json:
691
701
  print(json.dumps(result, indent=2, sort_keys=True))
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import os
6
6
  from pathlib import Path
7
- from typing import Any
7
+ from typing import Any, Mapping
8
8
 
9
9
  from .. import code_mower_telemetry
10
10
  from .. import reviewer_spend
@@ -86,6 +86,108 @@ def build_catch_up_summary(
86
86
  }
87
87
 
88
88
 
89
+ def _reviewer_spend_events(
90
+ *,
91
+ repo_path: Path,
92
+ spend_path: Path | None,
93
+ repo_slug: str,
94
+ team_id: str,
95
+ install_id: str,
96
+ source: str,
97
+ ) -> list[dict[str, Any]]:
98
+ resolved_spend_path = spend_path or repo_path / reviewer_spend.DEFAULT_SPEND_PATH
99
+ if not resolved_spend_path.is_file():
100
+ return []
101
+ return reviewer_spend.spend_runs_to_events(
102
+ reviewer_spend.load_spend_file(resolved_spend_path),
103
+ repo_slug=repo_slug,
104
+ team_id=team_id,
105
+ install_id=install_id,
106
+ source=source,
107
+ )
108
+
109
+
110
+ def _mapping(value: object) -> Mapping[str, Any]:
111
+ return value if isinstance(value, Mapping) else {}
112
+
113
+
114
+ def _event_match_values(event: Mapping[str, Any]) -> tuple[str, str, str, str]:
115
+ dimensions = _mapping(event.get("dimensions"))
116
+ lane = str(
117
+ dimensions.get("audit_comment_lane_id")
118
+ or dimensions.get("lane_id")
119
+ or dimensions.get("lane")
120
+ or event.get("lens")
121
+ or event.get("provider")
122
+ or ""
123
+ ).strip()
124
+ return (
125
+ str(event.get("repo_slug") or "").strip(),
126
+ str(dimensions.get("pr_number") or "").strip(),
127
+ lane,
128
+ str(dimensions.get("head_sha") or "").strip(),
129
+ )
130
+
131
+
132
+ def _merge_spend_metrics(
133
+ event: dict[str, Any],
134
+ spend_event: Mapping[str, Any],
135
+ ) -> dict[str, Any]:
136
+ merged = dict(event)
137
+ metrics = dict(_mapping(event.get("metrics")))
138
+ for key, value in _mapping(spend_event.get("metrics")).items():
139
+ metrics.setdefault(key, value)
140
+ dimensions = dict(_mapping(event.get("dimensions")))
141
+ spend_dimensions = _mapping(spend_event.get("dimensions"))
142
+ if spend_dimensions.get("spend_run_id"):
143
+ dimensions["spend_run_id"] = spend_dimensions["spend_run_id"]
144
+ if spend_event.get("source"):
145
+ dimensions["spend_source"] = spend_event["source"]
146
+ merged["metrics"] = metrics
147
+ merged["dimensions"] = dimensions
148
+ if not _mapping(event.get("tool")) and isinstance(spend_event.get("tool"), Mapping):
149
+ merged["tool"] = dict(spend_event["tool"])
150
+ return merged
151
+
152
+
153
+ def _merge_reviewer_spend_events(
154
+ events: list[dict[str, Any]],
155
+ spend_events: list[dict[str, Any]],
156
+ ) -> list[dict[str, Any]]:
157
+ if not spend_events:
158
+ return events
159
+ merged = list(events)
160
+ exact_matches: dict[tuple[str, str, str, str], int] = {}
161
+ fallback_matches: dict[tuple[str, str, str], int | None] = {}
162
+ for index, event in enumerate(merged):
163
+ repo_slug, pr_number, lane, head_sha = _event_match_values(event)
164
+ if not repo_slug or not pr_number or not lane:
165
+ continue
166
+ if head_sha:
167
+ exact_matches[(repo_slug, pr_number, lane, head_sha)] = index
168
+ fallback_key = (repo_slug, pr_number, lane)
169
+ fallback_matches[fallback_key] = (
170
+ None if fallback_key in fallback_matches else index
171
+ )
172
+
173
+ unmatched: list[dict[str, Any]] = []
174
+ merged_targets: set[int] = set()
175
+ for spend_event in spend_events:
176
+ repo_slug, pr_number, lane, head_sha = _event_match_values(spend_event)
177
+ target_index = None
178
+ if head_sha:
179
+ target_index = exact_matches.get((repo_slug, pr_number, lane, head_sha))
180
+ if target_index is None:
181
+ target_index = fallback_matches.get((repo_slug, pr_number, lane))
182
+ if target_index is None or target_index in merged_targets:
183
+ unmatched.append(spend_event)
184
+ continue
185
+ merged_targets.add(target_index)
186
+ merged[target_index] = _merge_spend_metrics(merged[target_index], spend_event)
187
+ remaining = max(0, MAX_EVENT_COUNT - len(merged))
188
+ return [*merged, *unmatched[:remaining]]
189
+
190
+
89
191
  def dogfood_upload(
90
192
  *,
91
193
  repo_path: Path,
@@ -112,16 +214,14 @@ def dogfood_upload(
112
214
  resolved_team_id = team_id or os.environ.get(DEFAULT_TEAM_ID_ENV, "")
113
215
  resolved_install_id = install_id or os.environ.get(DEFAULT_INSTALL_ID_ENV, "")
114
216
  resolved_reports = reports or default_dogfood_reports(repo_path)
115
- resolved_spend_path = spend_path or repo_path / reviewer_spend.DEFAULT_SPEND_PATH
116
- spend_events: list[dict[str, Any]] = []
117
- if resolved_spend_path.is_file():
118
- spend_events = reviewer_spend.spend_runs_to_events(
119
- reviewer_spend.load_spend_file(resolved_spend_path),
120
- repo_slug=detected_repo_slug,
121
- team_id=resolved_team_id,
122
- install_id=resolved_install_id,
123
- source="code-mower cloud dogfood spend",
124
- )
217
+ spend_events = _reviewer_spend_events(
218
+ repo_path=repo_path,
219
+ spend_path=spend_path,
220
+ repo_slug=detected_repo_slug,
221
+ team_id=resolved_team_id,
222
+ install_id=resolved_install_id,
223
+ source="code-mower cloud dogfood spend",
224
+ )
125
225
  dogfood_plan = build_dogfood_plan(
126
226
  repo_slug=detected_repo_slug,
127
227
  team_id=resolved_team_id,
@@ -321,6 +421,7 @@ def reviewer_runs_upload(
321
421
  yes: bool,
322
422
  timeout: float,
323
423
  include_git_ref: bool,
424
+ spend_path: Path | None = None,
324
425
  ) -> dict[str, Any]:
325
426
  if limit < 1 or limit > MAX_EVENT_COUNT:
326
427
  raise CloudBundleError(f"--limit must be between 1 and {MAX_EVENT_COUNT}")
@@ -344,6 +445,15 @@ def reviewer_runs_upload(
344
445
  )
345
446
  except ValueError as exc:
346
447
  raise CloudBundleError(str(exc)) from exc
448
+ spend_events = _reviewer_spend_events(
449
+ repo_path=repo_path,
450
+ spend_path=spend_path,
451
+ repo_slug=detected_repo_slug,
452
+ team_id=resolved_team_id,
453
+ install_id=resolved_install_id,
454
+ source="code-mower cloud reviewer-runs spend",
455
+ )
456
+ events = _merge_reviewer_spend_events(events, spend_events)
347
457
  if not events:
348
458
  return {
349
459
  "mode": "cloud-reviewer-runs",
@@ -566,6 +676,7 @@ def repo_sync_upload(
566
676
  yes=yes,
567
677
  timeout=timeout,
568
678
  include_git_ref=include_git_ref,
679
+ spend_path=None,
569
680
  )
570
681
  else: # pragma: no cover - argparse constrains modes.
571
682
  raise CloudBundleError(f"unsupported repo-sync mode: {mode}")
@@ -21,8 +21,15 @@ ALLOWED_LANE_TYPES = {"audit", "review"}
21
21
  ALLOWED_DRIVERS = {"local_cli", "manual", "hosted_bridge", "saas_event", "api_model"}
22
22
  ALLOWED_EVENTS = {"issue_comment", "pull_request_review", "check_run"}
23
23
  ALLOWED_SPEND_POLICIES = {"none", "local", "included", "paid"}
24
- BUILDER_IDENTITY_SECTIONS = {"labels", "authors", "trailers"}
24
+ BUILDER_IDENTITY_SECTIONS = {
25
+ "labels",
26
+ "authors",
27
+ "trailers",
28
+ "branch_prefixes",
29
+ "fix_round_mentions",
30
+ }
25
31
  SAFE_IDENTIFIER_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_-]*$")
32
+ SAFE_ENV_NAME_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
26
33
  SAFE_WORKFLOW_FILENAME_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_-]*\.ya?ml$")
27
34
  GENERATED_WORKFLOW_BY_DRIVER = {
28
35
  "api_model": "trailer-comment labeler + API/model runner",
@@ -305,6 +312,21 @@ def _require_identifier(value: Any, path: str, issues: list[ConfigIssue]) -> str
305
312
  return text
306
313
 
307
314
 
315
+ def _require_env_name(value: Any, path: str, issues: list[ConfigIssue]) -> str | None:
316
+ text = _require_string(value, path, issues)
317
+ if text is None:
318
+ return None
319
+ if not SAFE_ENV_NAME_RE.fullmatch(text):
320
+ issues.append(
321
+ ConfigIssue(
322
+ path,
323
+ "must match [A-Za-z_][A-Za-z0-9_]* for generated workflow safety",
324
+ )
325
+ )
326
+ return None
327
+ return text
328
+
329
+
308
330
  def _require_workflow_path(value: Any, path: str, issues: list[ConfigIssue]) -> str | None:
309
331
  text = _require_string(value, path, issues)
310
332
  if text is None:
@@ -403,13 +425,28 @@ def validate_config(config: Mapping[str, Any]) -> list[ConfigIssue]:
403
425
  section_path = f"builder_identity.{section}"
404
426
  if section not in BUILDER_IDENTITY_SECTIONS:
405
427
  issues.append(
406
- ConfigIssue(section_path, "must be labels, authors, or trailers")
428
+ ConfigIssue(
429
+ section_path,
430
+ "must be labels, authors, trailers, branch_prefixes, or fix_round_mentions",
431
+ )
407
432
  )
408
433
  continue
434
+ if section == "fix_round_mentions":
435
+ for lane_name, mention in _as_mapping(values, section_path, issues).items():
436
+ _require_identifier(lane_name, f"{section_path}.{lane_name}", issues)
437
+ _require_string(mention, f"{section_path}.{lane_name}", issues)
438
+ continue
409
439
  for key, lane_name in _as_mapping(values, section_path, issues).items():
410
440
  _require_string(str(key), f"{section_path}.{key}", issues)
411
441
  _require_identifier(lane_name, f"{section_path}.{key}", issues)
412
442
 
443
+ owner_surface = config.get("owner_surface")
444
+ if owner_surface is not None:
445
+ owner_surface_map = _as_mapping(owner_surface, "owner_surface", issues)
446
+ for key in ("dispatch_token_env", "dispatch_token_expires_var"):
447
+ if key in owner_surface_map:
448
+ _require_env_name(owner_surface_map.get(key), f"owner_surface.{key}", issues)
449
+
413
450
  all_labels: dict[str, str] = {}
414
451
  for lane_id, lane in lanes.items():
415
452
  path = f"lanes.{lane_id}"
@@ -19,6 +19,11 @@ from .common import (
19
19
  )
20
20
  from .github import check_github_setup
21
21
  from .github_config import check_repository_posture
22
+ from .github_human_token import (
23
+ check_human_automation_token,
24
+ human_automation_token_config,
25
+ human_automation_token_required,
26
+ )
22
27
  from .groups import GROUP_LABELS, doctor_check_group_id
23
28
  from .models import (
24
29
  STATUS_FAIL,
@@ -86,6 +91,7 @@ __all__ = [
86
91
  "check_github_auth_surface",
87
92
  "check_macos_runner_launchagent",
88
93
  "check_github_setup",
94
+ "check_human_automation_token",
89
95
  "check_repository_posture",
90
96
  "check_lane_runtime",
91
97
  "check_review_hygiene",
@@ -97,6 +103,8 @@ __all__ = [
97
103
  "doctor_output_group",
98
104
  "effective_lane",
99
105
  "evaluate_json_probe",
106
+ "human_automation_token_config",
107
+ "human_automation_token_required",
100
108
  "load_inputs",
101
109
  "local_cli_probe_remediation",
102
110
  "provider_template_coverage",
@@ -18,6 +18,7 @@ from .github_actions import (
18
18
  from .github_actions_permissions import check_actions_permissions
19
19
  from .github_branch import check_branch_protection
20
20
  from .github_config import configured_repositories, selected_saas_or_hosted_lanes
21
+ from .github_human_token import check_human_automation_token
21
22
  from .github_provider import check_private_repo_provider_surface
22
23
  from .github_repo import (
23
24
  check_repo_auto_merge,
@@ -124,6 +125,15 @@ def check_github_setup(
124
125
  ),
125
126
  ]
126
127
  )
128
+ checks.append(
129
+ check_human_automation_token(
130
+ gh_path=gh_path,
131
+ slug=slug,
132
+ config=config,
133
+ lanes=lanes,
134
+ http_timeout=http_timeout,
135
+ )
136
+ )
127
137
  if has_merge_authority:
128
138
  checks.append(
129
139
  check_repo_auto_merge(slug=slug, repo_payload=metadata.payload)
@@ -8,6 +8,8 @@ from typing import Mapping
8
8
  from .common import DoctorCheck, STATUS_PASS, STATUS_WARN
9
9
  from .github_api import _github_api_json
10
10
 
11
+ GITHUB_ACTIONS_APP_ID = 15368
12
+
11
13
 
12
14
  def check_branch_protection(
13
15
  *,
@@ -41,6 +43,7 @@ def check_branch_protection(
41
43
 
42
44
  required_checks = protection_payload.get("required_status_checks")
43
45
  contexts: list[str] = []
46
+ check_bindings: list[dict[str, object]] = []
44
47
  if isinstance(required_checks, Mapping):
45
48
  raw_contexts = required_checks.get("contexts")
46
49
  if isinstance(raw_contexts, list):
@@ -49,8 +52,42 @@ def check_branch_protection(
49
52
  if isinstance(raw_checks, list):
50
53
  for check in raw_checks:
51
54
  if isinstance(check, Mapping) and check.get("context"):
52
- contexts.append(str(check["context"]))
55
+ context = str(check["context"])
56
+ contexts.append(context)
57
+ check_bindings.append(
58
+ {"context": context, "app_id": check.get("app_id")}
59
+ )
53
60
  contexts = list(dict.fromkeys(contexts))
61
+ wrong_gate_bindings = [
62
+ binding
63
+ for binding in check_bindings
64
+ if binding.get("context") == required_status_context
65
+ and binding.get("app_id") == GITHUB_ACTIONS_APP_ID
66
+ ]
67
+ if required_status_context and wrong_gate_bindings:
68
+ return DoctorCheck(
69
+ name="github.branch_protection",
70
+ status=STATUS_WARN,
71
+ message=(
72
+ f"{slug}@{default_branch} requires {required_status_context} "
73
+ "from GitHub Actions instead of Any source"
74
+ ),
75
+ detail={
76
+ "repo": slug,
77
+ "default_branch": default_branch,
78
+ "required_status_context": required_status_context,
79
+ "required_status_contexts": contexts,
80
+ "required_status_check_count": len(contexts),
81
+ "required_status_check_bindings": check_bindings,
82
+ },
83
+ remediation=(
84
+ f"Rebind `{required_status_context}` in branch protection to "
85
+ "Any source, not GitHub Actions. In the API response, the "
86
+ f"`checks[]` entry for `{required_status_context}` should have "
87
+ "`app_id: null`; `app_id: 15368` means GitHub is evaluating "
88
+ "the Actions job check-run instead of the Code Mower commit status."
89
+ ),
90
+ )
54
91
  if required_status_context and required_status_context not in contexts:
55
92
  return DoctorCheck(
56
93
  name="github.branch_protection",
@@ -70,7 +107,7 @@ def check_branch_protection(
70
107
  "unattended merge. Inspect existing checks with "
71
108
  f"`gh api repos/{slug}/branches/{encoded_branch}/protection/required_status_checks`, "
72
109
  "then PATCH that endpoint with all existing contexts plus "
73
- f"`{required_status_context}`."
110
+ f"`{required_status_context}` from Any source."
74
111
  ),
75
112
  )
76
113
  return DoctorCheck(
@@ -86,5 +123,6 @@ def check_branch_protection(
86
123
  "default_branch": default_branch,
87
124
  "required_status_check_count": len(contexts),
88
125
  "required_status_contexts": contexts,
126
+ "required_status_check_bindings": check_bindings,
89
127
  },
90
128
  )