code-mower 0.5.0b40__tar.gz → 0.5.0b42__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 (300) hide show
  1. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/PKG-INFO +31 -12
  2. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/README.md +30 -11
  3. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/pyproject.toml +1 -1
  4. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/__init__.py +1 -1
  5. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/audit_labeler_lib.py +227 -6
  6. code_mower-0.5.0b42/src/code_mower/builder_runs.py +752 -0
  7. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/evidence.py +1 -0
  8. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/evidence_report.py +66 -0
  9. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/value_report.py +17 -5
  10. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/checks.py +339 -3
  11. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/claude_audit_pr.py +212 -6
  12. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cli.py +4 -1
  13. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud.py +59 -1
  14. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/bundle.py +1 -0
  15. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/operations.py +23 -1
  16. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/code_mower_telemetry.py +26 -1
  17. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/codex_audit_pr.py +248 -12
  18. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/config.py +22 -0
  19. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/__init__.py +2 -0
  20. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/common.py +1 -0
  21. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github.py +13 -1
  22. code_mower-0.5.0b42/src/code_mower/doctor_checks/github_branch.py +90 -0
  23. code_mower-0.5.0b42/src/code_mower/doctor_checks/github_config.py +57 -0
  24. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_repo.py +31 -0
  25. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/runner.py +2 -0
  26. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/init.py +514 -5
  27. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/lane_configs/claude.py +1 -1
  28. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/lane_configs/codex.py +1 -1
  29. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/migration.py +11 -1
  30. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/migration_mirror.py +1 -0
  31. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/package_content.py +1 -1
  32. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/package_manifest.py +23 -0
  33. code_mower-0.5.0b42/src/code_mower/plan_context.py +443 -0
  34. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_runners/__init__.py +6 -0
  35. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_runners/pr_worktree.py +36 -1
  36. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/reviewer_metrics.py +78 -8
  37. code_mower-0.5.0b42/src/code_mower/reviewer_spend.py +283 -0
  38. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/saas_reviewer_labeler.py +72 -1
  39. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/code-mower.example.yml +32 -0
  40. code_mower-0.5.0b42/src/code_mower/templates/lane_prompts/plan-conformance.md +12 -0
  41. code_mower-0.5.0b42/src/code_mower/templates/product-support/status_report.py +232 -0
  42. {code_mower-0.5.0b40 → code_mower-0.5.0b42/src/code_mower}/templates/reviewer-spend.example.json +3 -1
  43. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/reviewer-value-report.example.md +3 -3
  44. {code_mower-0.5.0b40 → code_mower-0.5.0b42/src/code_mower}/templates/workflows/audit-label-cleanup.yml.j2 +1 -0
  45. code_mower-0.5.0b42/src/code_mower/templates/workflows/builder-provenance.yml.j2 +64 -0
  46. code_mower-0.5.0b42/src/code_mower/templates/workflows/code-mower-gate-health.yml.j2 +321 -0
  47. code_mower-0.5.0b42/src/code_mower/templates/workflows/code-mower-gate.yml.j2 +237 -0
  48. {code_mower-0.5.0b40 → code_mower-0.5.0b42/src/code_mower}/templates/workflows/hosted-bridge.yml.j2 +1 -1
  49. code_mower-0.5.0b42/src/code_mower/templates/workflows/needs-owner-notify.yml.j2 +54 -0
  50. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/workflows/review-clear-stale.yml.j2 +1 -1
  51. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/workflows/saas-reviewer-labeler.yml.j2 +3 -1
  52. code_mower-0.5.0b42/src/code_mower/templates/workflows/self-hosted-local-audit.yml.j2 +214 -0
  53. code_mower-0.5.0b42/src/code_mower/templates/workflows/trailer-comment-labeler.yml.j2 +63 -0
  54. code_mower-0.5.0b42/src/code_mower/templates/workflows/weekly-status.yml.j2 +54 -0
  55. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/trailer_comment_labeler.py +73 -3
  56. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/work_orders.py +83 -0
  57. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower.egg-info/PKG-INFO +31 -12
  58. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower.egg-info/SOURCES.txt +21 -0
  59. {code_mower-0.5.0b40/src/code_mower → code_mower-0.5.0b42}/templates/reviewer-spend.example.json +3 -1
  60. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/reviewer-value-report.example.md +3 -3
  61. {code_mower-0.5.0b40/src/code_mower → code_mower-0.5.0b42}/templates/workflows/audit-label-cleanup.yml.j2 +1 -0
  62. code_mower-0.5.0b42/templates/workflows/builder-provenance.yml.j2 +64 -0
  63. code_mower-0.5.0b42/templates/workflows/code-mower-gate-health.yml.j2 +321 -0
  64. code_mower-0.5.0b42/templates/workflows/code-mower-gate.yml.j2 +237 -0
  65. {code_mower-0.5.0b40/src/code_mower → code_mower-0.5.0b42}/templates/workflows/hosted-bridge.yml.j2 +1 -1
  66. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/workflows/review-clear-stale.yml.j2 +1 -1
  67. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/workflows/saas-reviewer-labeler.yml.j2 +3 -1
  68. code_mower-0.5.0b42/templates/workflows/self-hosted-local-audit.yml.j2 +214 -0
  69. code_mower-0.5.0b42/templates/workflows/trailer-comment-labeler.yml.j2 +63 -0
  70. code_mower-0.5.0b42/tests/test_audit_comment_posture.py +71 -0
  71. code_mower-0.5.0b42/tests/test_builder_identity.py +144 -0
  72. code_mower-0.5.0b42/tests/test_builder_runs.py +794 -0
  73. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_calibration_truth.py +52 -0
  74. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_checks.py +96 -0
  75. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_cloud_client.py +87 -0
  76. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_doctor_github_checks.py +78 -1
  77. code_mower-0.5.0b42/tests/test_plan_context.py +241 -0
  78. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_provider_runners_pr_worktree.py +58 -0
  79. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_release_hygiene.py +1007 -38
  80. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_reviewer_metrics_core.py +73 -2
  81. code_mower-0.5.0b42/tests/test_reviewer_spend.py +234 -0
  82. code_mower-0.5.0b42/tests/test_trailer_comment_labeler.py +151 -0
  83. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_work_orders.py +22 -0
  84. code_mower-0.5.0b40/src/code_mower/doctor_checks/github_branch.py +0 -56
  85. code_mower-0.5.0b40/src/code_mower/doctor_checks/github_config.py +0 -25
  86. code_mower-0.5.0b40/src/code_mower/templates/workflows/trailer-comment-labeler.yml.j2 +0 -51
  87. code_mower-0.5.0b40/templates/workflows/trailer-comment-labeler.yml.j2 +0 -51
  88. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/CODE_OF_CONDUCT.md +0 -0
  89. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/LICENSE +0 -0
  90. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/MANIFEST.in +0 -0
  91. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/NOTICE +0 -0
  92. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/SUPPORT.md +0 -0
  93. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/requirements/requirements.txt +0 -0
  94. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/setup.cfg +0 -0
  95. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/adapters/__init__.py +0 -0
  96. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/adapters/_base.py +0 -0
  97. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/adapters/cursor_bugbot.py +0 -0
  98. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/adapters/gitar.py +0 -0
  99. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/adapters/greptile.py +0 -0
  100. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/adapters/qodo.py +0 -0
  101. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/antigravity_cli_audit_pr.py +0 -0
  102. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/audit_handoff_log.py +0 -0
  103. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/audit_progress.py +0 -0
  104. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/blind_review_artifacts.py +0 -0
  105. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/blind_review_coordinator.py +0 -0
  106. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/bootstrap.py +0 -0
  107. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/builder_experiment.py +0 -0
  108. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/__init__.py +0 -0
  109. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/arms.py +0 -0
  110. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/auto_discovery.py +0 -0
  111. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/commands.py +0 -0
  112. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/context_inputs.py +0 -0
  113. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/corpus.py +0 -0
  114. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/effect_report.py +0 -0
  115. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/identity.py +0 -0
  116. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/metrics.py +0 -0
  117. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/overlap.py +0 -0
  118. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/planning.py +0 -0
  119. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/policy.py +0 -0
  120. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/results.py +0 -0
  121. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/run_results.py +0 -0
  122. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/run_status.py +0 -0
  123. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/runner.py +0 -0
  124. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/truth.py +0 -0
  125. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/claude_cli_bounce.py +0 -0
  126. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/claude_cli_environment.py +0 -0
  127. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/clear_stale.py +0 -0
  128. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/__init__.py +0 -0
  129. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/doctor.py +0 -0
  130. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/dogfood.py +0 -0
  131. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/endpoints.py +0 -0
  132. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/errors.py +0 -0
  133. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/events.py +0 -0
  134. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/export.py +0 -0
  135. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/git_metadata.py +0 -0
  136. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/manifest.py +0 -0
  137. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/reports.py +0 -0
  138. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/setup.py +0 -0
  139. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/upload.py +0 -0
  140. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/code_mower_calibration.py +0 -0
  141. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/code_mower_context_packs.py +0 -0
  142. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/code_mower_merge.py +0 -0
  143. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/coderabbit_cli_audit_pr.py +0 -0
  144. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/codex_audit_env_preflight.py +0 -0
  145. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/codex_audit_schema_smoke.py +0 -0
  146. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/codex_audit_verdict.schema.json +0 -0
  147. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor.py +0 -0
  148. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/cloud.py +0 -0
  149. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions.py +0 -0
  150. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_cost.py +0 -0
  151. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_cost_summary.py +0 -0
  152. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_failure_annotations.py +0 -0
  153. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_failure_models.py +0 -0
  154. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_failure_scan.py +0 -0
  155. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_failure_selection.py +0 -0
  156. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_failures.py +0 -0
  157. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_permissions.py +0 -0
  158. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_api.py +0 -0
  159. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_provider.py +0 -0
  160. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/groups.py +0 -0
  161. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/models.py +0 -0
  162. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/output.py +0 -0
  163. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/presets.py +0 -0
  164. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/privacy.py +0 -0
  165. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_api_model.py +0 -0
  166. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_api_model_openai.py +0 -0
  167. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_api_model_profiles.py +0 -0
  168. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_env.py +0 -0
  169. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_env_required.py +0 -0
  170. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_env_tokens.py +0 -0
  171. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_local_cli.py +0 -0
  172. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_local_cli_commands.py +0 -0
  173. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_local_cli_probe_config.py +0 -0
  174. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_probe.py +0 -0
  175. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_probe_auth.py +0 -0
  176. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_probe_evaluation.py +0 -0
  177. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_probe_json.py +0 -0
  178. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_probe_remediation.py +0 -0
  179. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_review_hygiene.py +0 -0
  180. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/providers.py +0 -0
  181. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/registry.py +0 -0
  182. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/runtime.py +0 -0
  183. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/runtime_github_auth.py +0 -0
  184. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/gemini_cli_audit_pr.py +0 -0
  185. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/grok_build_audit_pr.py +0 -0
  186. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/hermes_cli_audit_pr.py +0 -0
  187. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/lane_configs/__init__.py +0 -0
  188. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/lane_configs/aider.py +0 -0
  189. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/lane_configs/antigravity_cli.py +0 -0
  190. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/lane_configs/devin.py +0 -0
  191. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/lane_configs/gemini_cli.py +0 -0
  192. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/lane_configs/grok_build.py +0 -0
  193. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/lane_configs/hermes_cli.py +0 -0
  194. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/lane_configs/local_llm.py +0 -0
  195. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/local_llm_audit_pr.py +0 -0
  196. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/local_llm_bakeoff.py +0 -0
  197. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/local_llm_calibration.py +0 -0
  198. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/local_llm_profiles.py +0 -0
  199. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/migration_install.py +0 -0
  200. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/migration_readiness.py +0 -0
  201. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/migration_rehearsal.py +0 -0
  202. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/next_steps.py +0 -0
  203. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/package.py +0 -0
  204. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/package_paths.py +0 -0
  205. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/package_rendering.py +0 -0
  206. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/package_static.py +0 -0
  207. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/prompts.py +0 -0
  208. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_registry.py +0 -0
  209. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_runners/comments.py +0 -0
  210. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_runners/git.py +0 -0
  211. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_runners/github_auth.py +0 -0
  212. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_runners/github_pr.py +0 -0
  213. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_runners/process.py +0 -0
  214. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_runners/repo_paths.py +0 -0
  215. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_runners/text_schema.py +0 -0
  216. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_runners/verdict_artifacts.py +0 -0
  217. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_runners/workspace.py +0 -0
  218. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/providers/__init__.py +0 -0
  219. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/providers/local_cli.py +0 -0
  220. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/providers/provenance.py +0 -0
  221. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/release_readiness.py +0 -0
  222. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/secrets.py +0 -0
  223. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/builder-experiment.example.json +0 -0
  224. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/calibration-corpus.example.json +0 -0
  225. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/calibration-corpus.json +0 -0
  226. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/context-packs.example.json +0 -0
  227. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/base-audit.md +0 -0
  228. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/calibration-policy.md +0 -0
  229. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/context-driven-quality.md +0 -0
  230. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/docs-design.md +0 -0
  231. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/generic-programming.md +0 -0
  232. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/operability.md +0 -0
  233. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/package-runtime.md +0 -0
  234. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/security-threat-model.md +0 -0
  235. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/product-support/code_mower +0 -0
  236. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/product-support/code_mower_standalone_pin.env +0 -0
  237. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/product-support/code_mower_standalone_shadow.sh +0 -0
  238. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/product-support/run_claude_audit_pr.sh +0 -0
  239. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/product-support/run_codex_audit_pr.sh +0 -0
  240. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/product-support/safe_gh_comment.py +0 -0
  241. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/providers.yml +0 -0
  242. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/workflows/local-cli-audit.yml.j2 +0 -0
  243. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/workflows/private-standalone-shadow.yml.j2 +0 -0
  244. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/versioning.py +0 -0
  245. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower.egg-info/dependency_links.txt +0 -0
  246. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower.egg-info/entry_points.txt +0 -0
  247. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower.egg-info/requires.txt +0 -0
  248. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower.egg-info/top_level.txt +0 -0
  249. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/builder-experiment.example.json +0 -0
  250. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/calibration-corpus.example.json +0 -0
  251. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/calibration-corpus.json +0 -0
  252. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/code-mower.yml.j2 +0 -0
  253. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/context-packs.example.json +0 -0
  254. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/cursor/BUGBOT.md +0 -0
  255. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/acp_bridge.yml +0 -0
  256. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/aider.yml +0 -0
  257. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/antigravity_cli.yml +0 -0
  258. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/claude_audit.yml +0 -0
  259. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/claude_review.yml +0 -0
  260. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/coderabbit_cli.yml +0 -0
  261. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/codex.yml +0 -0
  262. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/cursor_bugbot.yml +0 -0
  263. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/devin.yml +0 -0
  264. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/gemini_cli.yml +0 -0
  265. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/gitar.yml +0 -0
  266. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/greptile.yml +0 -0
  267. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/hermes_cli.yml +0 -0
  268. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/local_llm.yml +0 -0
  269. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/qodo.yml +0 -0
  270. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers.yml +0 -0
  271. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/workflows/blind-review-artifacts-dry-run.yml.j2 +0 -0
  272. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/workflows/local-cli-audit.yml.j2 +0 -0
  273. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/workflows/private-standalone-shadow.yml.j2 +0 -0
  274. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_calibration_effect_report.py +0 -0
  275. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_claude_cli_bounce.py +0 -0
  276. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_clear_stale.py +0 -0
  277. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_cloud_git_metadata.py +0 -0
  278. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_cloud_manifest.py +0 -0
  279. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_cloud_reports.py +0 -0
  280. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_coderabbit_cli_audit_pr.py +0 -0
  281. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_codex_audit_env_preflight.py +0 -0
  282. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_doctor_actions_cost_summary.py +0 -0
  283. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_doctor_actions_failure_selection.py +0 -0
  284. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_doctor_boundaries.py +0 -0
  285. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_doctor_output.py +0 -0
  286. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_doctor_privacy.py +0 -0
  287. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_doctor_provider_env_required.py +0 -0
  288. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_doctor_provider_env_tokens.py +0 -0
  289. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_doctor_provider_probe.py +0 -0
  290. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_doctor_provider_review_hygiene.py +0 -0
  291. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_doctor_registry.py +0 -0
  292. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_local_llm_github_helpers.py +0 -0
  293. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_provider_local_cli.py +0 -0
  294. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_provider_runners_comments.py +0 -0
  295. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_provider_runners_git.py +0 -0
  296. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_provider_runners_github_pr.py +0 -0
  297. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_provider_runners_process.py +0 -0
  298. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_provider_runners_repo_paths.py +0 -0
  299. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_provider_runners_text_schema.py +0 -0
  300. {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_provider_runners_workspace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-mower
3
- Version: 0.5.0b40
3
+ Version: 0.5.0b42
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.0b40
106
+ pipx install --python python3.12 code-mower==0.5.0b42
107
107
  code-mower --version
108
108
  ```
109
109
 
110
- `0.5.0b40` is a beta release. Until Code Mower publishes a stable `1.0`
110
+ `0.5.0b42` 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
@@ -122,6 +122,10 @@ code-mower doctor --preflight
122
122
  code-mower checks detect --json
123
123
  ```
124
124
 
125
+ In Git worktrees, `checks detect` also offers `code-mower.pr-size`, a local
126
+ PR-size lint that defaults to 300 changed lines and flags large batches of
127
+ near-identical files.
128
+
125
129
  When those look sane, write the generated setup to a reviewable folder and
126
130
  produce the starter local report:
127
131
 
@@ -131,8 +135,9 @@ code-mower calibration value-report .code-mower.generated/calibration-corpus.jso
131
135
  --output .code-mower/reviewer-value-report.md
132
136
  ```
133
137
 
134
- The generated starter corpus proves the command path. To bootstrap a draft from
135
- your repository history:
138
+ The generated setup also includes the configurable owner escalation and weekly
139
+ status digest templates. The generated starter corpus proves the command path.
140
+ To bootstrap a draft from your repository history:
136
141
 
137
142
  ```bash
138
143
  code-mower calibration auto-discover \
@@ -150,7 +155,7 @@ First-time command map: [docs/launch-command-surface.md](docs/launch-command-sur
150
155
 
151
156
  The current PyPI beta has been rehearsed end-to-end from a clean install:
152
157
  [First-User Install Rehearsal](docs/first-user-install-rehearsal.md) records
153
- the latest 10/10 public-package readiness proof for `code-mower==0.5.0b40`.
158
+ the latest 10/10 public-package readiness proof for `code-mower==0.5.0b42`.
154
159
 
155
160
  ## Optional: Plan Before Coding
156
161
 
@@ -184,9 +189,21 @@ code-mower work-order attach-delivery \
184
189
  --from-github
185
190
  ```
186
191
 
187
- That lets CodeMower.com show `issue -> plan -> work order -> PR -> reviewer
188
- checks -> merge` lineage without receiving source, diffs, transcripts, or issue
189
- body text.
192
+ If a hosted builder such as Grok Bot or Cursor Cloud Agents produced the PR,
193
+ record source-free builder provenance too:
194
+
195
+ ```bash
196
+ code-mower builder record \
197
+ --provider grok_bot \
198
+ --executor cursor_cloud_agent \
199
+ --work-order .code-mower/work-orders/billing-settings.md \
200
+ --pr OWNER/REPO#124
201
+ ```
202
+
203
+ That lets CodeMower.com show `issue -> plan -> work order -> builder run -> PR
204
+ -> reviewer checks -> merge` lineage without receiving source, diffs,
205
+ transcripts, prompts, or issue body text. See
206
+ [docs/builders-grok-cursor.md](docs/builders-grok-cursor.md).
190
207
 
191
208
  ## Why Not Just Run Codex Or Claude Yourself?
192
209
 
@@ -287,13 +304,15 @@ engineer can:
287
304
  Future builder/orchestrator experiments extend the same loop from "who reviews
288
305
  best?" to "which AI builder plus reviewer loop ships best on this product?" See
289
306
  [docs/builder-experiments.md](docs/builder-experiments.md) and
290
- [docs/authoring-intelligence.md](docs/authoring-intelligence.md).
307
+ [docs/authoring-intelligence.md](docs/authoring-intelligence.md). For hosted
308
+ builder provenance, start with
309
+ [docs/builders-grok-cursor.md](docs/builders-grok-cursor.md).
291
310
 
292
311
  ## Installation Status
293
312
 
294
- The current public beta is `v0.5.0-beta.40` from
313
+ The current public beta is `v0.5.0-beta.42` from
295
314
  [codemower-ai/code-mower](https://github.com/codemower-ai/code-mower), published
296
- as `code-mower==0.5.0b40` on [PyPI](https://pypi.org/project/code-mower/).
315
+ as `code-mower==0.5.0b42` on [PyPI](https://pypi.org/project/code-mower/).
297
316
  GitHub releases remain the auditable source for tags, build artifacts, and
298
317
  release notes.
299
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.0b40
91
+ pipx install --python python3.12 code-mower==0.5.0b42
92
92
  code-mower --version
93
93
  ```
94
94
 
95
- `0.5.0b40` is a beta release. Until Code Mower publishes a stable `1.0`
95
+ `0.5.0b42` 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
@@ -107,6 +107,10 @@ code-mower doctor --preflight
107
107
  code-mower checks detect --json
108
108
  ```
109
109
 
110
+ In Git worktrees, `checks detect` also offers `code-mower.pr-size`, a local
111
+ PR-size lint that defaults to 300 changed lines and flags large batches of
112
+ near-identical files.
113
+
110
114
  When those look sane, write the generated setup to a reviewable folder and
111
115
  produce the starter local report:
112
116
 
@@ -116,8 +120,9 @@ code-mower calibration value-report .code-mower.generated/calibration-corpus.jso
116
120
  --output .code-mower/reviewer-value-report.md
117
121
  ```
118
122
 
119
- The generated starter corpus proves the command path. To bootstrap a draft from
120
- your repository history:
123
+ The generated setup also includes the configurable owner escalation and weekly
124
+ status digest templates. The generated starter corpus proves the command path.
125
+ To bootstrap a draft from your repository history:
121
126
 
122
127
  ```bash
123
128
  code-mower calibration auto-discover \
@@ -135,7 +140,7 @@ First-time command map: [docs/launch-command-surface.md](docs/launch-command-sur
135
140
 
136
141
  The current PyPI beta has been rehearsed end-to-end from a clean install:
137
142
  [First-User Install Rehearsal](docs/first-user-install-rehearsal.md) records
138
- the latest 10/10 public-package readiness proof for `code-mower==0.5.0b40`.
143
+ the latest 10/10 public-package readiness proof for `code-mower==0.5.0b42`.
139
144
 
140
145
  ## Optional: Plan Before Coding
141
146
 
@@ -169,9 +174,21 @@ code-mower work-order attach-delivery \
169
174
  --from-github
170
175
  ```
171
176
 
172
- That lets CodeMower.com show `issue -> plan -> work order -> PR -> reviewer
173
- checks -> merge` lineage without receiving source, diffs, transcripts, or issue
174
- body text.
177
+ If a hosted builder such as Grok Bot or Cursor Cloud Agents produced the PR,
178
+ record source-free builder provenance too:
179
+
180
+ ```bash
181
+ code-mower builder record \
182
+ --provider grok_bot \
183
+ --executor cursor_cloud_agent \
184
+ --work-order .code-mower/work-orders/billing-settings.md \
185
+ --pr OWNER/REPO#124
186
+ ```
187
+
188
+ That lets CodeMower.com show `issue -> plan -> work order -> builder run -> PR
189
+ -> reviewer checks -> merge` lineage without receiving source, diffs,
190
+ transcripts, prompts, or issue body text. See
191
+ [docs/builders-grok-cursor.md](docs/builders-grok-cursor.md).
175
192
 
176
193
  ## Why Not Just Run Codex Or Claude Yourself?
177
194
 
@@ -272,13 +289,15 @@ engineer can:
272
289
  Future builder/orchestrator experiments extend the same loop from "who reviews
273
290
  best?" to "which AI builder plus reviewer loop ships best on this product?" See
274
291
  [docs/builder-experiments.md](docs/builder-experiments.md) and
275
- [docs/authoring-intelligence.md](docs/authoring-intelligence.md).
292
+ [docs/authoring-intelligence.md](docs/authoring-intelligence.md). For hosted
293
+ builder provenance, start with
294
+ [docs/builders-grok-cursor.md](docs/builders-grok-cursor.md).
276
295
 
277
296
  ## Installation Status
278
297
 
279
- The current public beta is `v0.5.0-beta.40` from
298
+ The current public beta is `v0.5.0-beta.42` from
280
299
  [codemower-ai/code-mower](https://github.com/codemower-ai/code-mower), published
281
- as `code-mower==0.5.0b40` on [PyPI](https://pypi.org/project/code-mower/).
300
+ as `code-mower==0.5.0b42` on [PyPI](https://pypi.org/project/code-mower/).
282
301
  GitHub releases remain the auditable source for tags, build artifacts, and
283
302
  release notes.
284
303
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "code-mower"
7
- version = "0.5.0b40"
7
+ version = "0.5.0b42"
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.0b40"
3
+ __version__ = "0.5.0b42"
@@ -17,11 +17,16 @@ import urllib.error
17
17
  import urllib.request
18
18
  from dataclasses import dataclass
19
19
  from pathlib import Path
20
- from typing import Any, Dict, Optional, Pattern, Sequence
20
+ from typing import Any, Callable, Dict, Mapping, Optional, Pattern, Sequence
21
21
  from urllib.parse import quote
22
22
  import re
23
23
 
24
24
  MIN_ABBREVIATED_SHA_LENGTH = 7
25
+ AUTHOR_EXCLUSION_ENV = "CODE_MOWER_AUTHOR_EXCLUSION_JSON"
26
+ ACTIONS_RUN_MARKER_RE = re.compile(
27
+ r"<!--\s*CODE_MOWER_AUDIT_RUN:\s*run_id=([0-9]+)\s*-->"
28
+ )
29
+ TRUSTED_ACTIONS_AUDIT_EVENTS = frozenset({"pull_request_target"})
25
30
 
26
31
 
27
32
  @dataclass(frozen=True)
@@ -65,12 +70,22 @@ class LaneConfig:
65
70
  flags=re.IGNORECASE,
66
71
  )
67
72
 
73
+ def default_comment_authors(self) -> frozenset[str]:
74
+ return _author_csv_set(",".join(self.default_authors))
75
+
76
+ def configured_comment_authors(self) -> frozenset[str]:
77
+ if not self.authors_env_var:
78
+ return frozenset()
79
+ return _author_csv_set(os.environ.get(self.authors_env_var) or "")
80
+
68
81
  def comment_authors(self) -> frozenset[str]:
69
- if self.authors_env_var:
70
- raw_authors = os.environ.get(self.authors_env_var) or ",".join(self.default_authors)
71
- else:
72
- raw_authors = ",".join(self.default_authors)
73
- return frozenset(author.strip().lower() for author in raw_authors.split(",") if author.strip())
82
+ return self.default_comment_authors() | self.configured_comment_authors()
83
+
84
+ def is_default_comment_author(self, login: str) -> bool:
85
+ return login.strip().lower() in self.default_comment_authors()
86
+
87
+ def is_configured_comment_author(self, login: str) -> bool:
88
+ return login.strip().lower() in self.configured_comment_authors()
74
89
 
75
90
  def github_tokens_from_env(self) -> tuple[GitHubToken, ...]:
76
91
  tokens = []
@@ -84,6 +99,14 @@ class LaneConfig:
84
99
  return tuple(tokens)
85
100
 
86
101
 
102
+ def _author_csv_set(raw_authors: str) -> frozenset[str]:
103
+ return frozenset(
104
+ author.strip().lower()
105
+ for author in raw_authors.split(",")
106
+ if author.strip()
107
+ )
108
+
109
+
87
110
  class GitHubRequestError(RuntimeError):
88
111
  def __init__(self, method: str, path: str, code: int, response_body: str) -> None:
89
112
  self.method = method
@@ -97,6 +120,80 @@ class IssueCommentPaginationLimitExceeded(RuntimeError):
97
120
  """Raised when issue comments exceed the configured safe pagination cap."""
98
121
 
99
122
 
123
+ def _string_mapping(value: Any) -> dict[str, str]:
124
+ if not isinstance(value, Mapping):
125
+ return {}
126
+ return {
127
+ str(key): str(item)
128
+ for key, item in value.items()
129
+ if str(key) and str(item)
130
+ }
131
+
132
+
133
+ def load_author_exclusion_config(raw: str | None = None) -> Mapping[str, Any]:
134
+ text = raw if raw is not None else os.environ.get(AUTHOR_EXCLUSION_ENV, "")
135
+ if not text:
136
+ return {"enabled": False}
137
+ try:
138
+ parsed = json.loads(text)
139
+ except json.JSONDecodeError:
140
+ return {"enabled": False}
141
+ return parsed if isinstance(parsed, Mapping) else {"enabled": False}
142
+
143
+
144
+ def builder_identity_matches(
145
+ *,
146
+ labels: Sequence[str],
147
+ author: str,
148
+ text: str,
149
+ config: Mapping[str, Any],
150
+ ) -> tuple[str, ...]:
151
+ if not bool(config.get("enabled")):
152
+ return ()
153
+ label_map = _string_mapping(config.get("labels"))
154
+ author_map = {
155
+ key.lower(): value
156
+ for key, value in _string_mapping(config.get("authors")).items()
157
+ }
158
+ matches: list[str] = []
159
+
160
+ for label in labels:
161
+ lane = label_map.get(str(label))
162
+ if lane:
163
+ matches.append(lane)
164
+
165
+ lane = author_map.get(author.lower())
166
+ if lane:
167
+ matches.append(lane)
168
+
169
+ return tuple(dict.fromkeys(matches))
170
+
171
+
172
+ def author_exclusion_reason(
173
+ *,
174
+ lane_name: str,
175
+ labels: Sequence[str],
176
+ author: str,
177
+ text: str,
178
+ config: Mapping[str, Any] | None = None,
179
+ ) -> str | None:
180
+ exclusion_config = config or load_author_exclusion_config()
181
+ matches = builder_identity_matches(
182
+ labels=labels,
183
+ author=author,
184
+ text=text,
185
+ config=exclusion_config,
186
+ )
187
+ if not matches:
188
+ return None
189
+ if len(set(matches)) > 1:
190
+ return "conflicting builder identity; skipping author-excluded label update"
191
+ builder_lane = matches[0]
192
+ if builder_lane == lane_name:
193
+ return f"{lane_name} lane excluded for builder-authored PR"
194
+ return None
195
+
196
+
100
197
  def load_json(path: Path) -> Dict[str, Any]:
101
198
  return json.loads(path.read_text(encoding="utf-8"))
102
199
 
@@ -198,6 +295,130 @@ def github_request_with_fallback(
198
295
  raise RuntimeError(f"no GitHub tokens available for {method} {path}")
199
296
 
200
297
 
298
+ def parse_csv_set(raw: str) -> frozenset[str]:
299
+ return frozenset(item.strip() for item in raw.split(",") if item.strip())
300
+
301
+
302
+ def parse_audit_run_id(body: str) -> str:
303
+ match = ACTIONS_RUN_MARKER_RE.search(body)
304
+ return match.group(1) if match else ""
305
+
306
+
307
+ def workflow_path_matches(path: str, trusted_workflows: Sequence[str]) -> bool:
308
+ if not path:
309
+ return False
310
+ return any(path == item or path.endswith("/" + item) for item in trusted_workflows)
311
+
312
+
313
+ def _pr_item_matches_head(
314
+ item: Mapping[str, Any],
315
+ *,
316
+ issue_number: int,
317
+ head_sha: str,
318
+ fallback_head_sha: str = "",
319
+ ) -> bool:
320
+ if str(item.get("number") or "") != str(issue_number):
321
+ return False
322
+ pr_head = str(((item.get("head") or {}).get("sha")) or "")
323
+ if pr_head:
324
+ return sha_matches(pr_head, head_sha)
325
+ return bool(fallback_head_sha) and sha_matches(fallback_head_sha, head_sha)
326
+
327
+
328
+ def fetch_actions_run(
329
+ repo: str,
330
+ run_id: str,
331
+ *,
332
+ tokens: Sequence[GitHubToken],
333
+ ) -> dict[str, Any]:
334
+ response = github_request_with_fallback(
335
+ "GET",
336
+ f"/repos/{repo}/actions/runs/{run_id}",
337
+ tokens=tokens,
338
+ )
339
+ return response if isinstance(response, dict) else {}
340
+
341
+
342
+ def fetch_pull_requests_for_commit(
343
+ repo: str,
344
+ head_sha: str,
345
+ *,
346
+ tokens: Sequence[GitHubToken],
347
+ ) -> list[dict[str, Any]]:
348
+ response = github_request_with_fallback(
349
+ "GET",
350
+ f"/repos/{repo}/commits/{head_sha}/pulls?per_page=100",
351
+ tokens=tokens,
352
+ )
353
+ return response if isinstance(response, list) else []
354
+
355
+
356
+ def github_actions_comment_attested(
357
+ *,
358
+ repo: str,
359
+ body: str,
360
+ issue_number: int,
361
+ head_sha: str,
362
+ workflow_paths: Sequence[str],
363
+ tokens: Sequence[GitHubToken],
364
+ actions_run_lookup: Optional[Callable[[str], Mapping[str, Any]]] = None,
365
+ commit_pull_requests_lookup: Optional[Callable[[str], Sequence[Mapping[str, Any]]]] = None,
366
+ ) -> bool:
367
+ """Return whether a github-actions[bot] audit comment is tied to this PR head."""
368
+ trusted_workflows = tuple(parse_csv_set(",".join(workflow_paths)))
369
+ token_list = tuple(token for token in tokens if token.value)
370
+ run_id = parse_audit_run_id(body)
371
+ if not trusted_workflows or not token_list or not run_id or not head_sha:
372
+ return False
373
+ try:
374
+ if actions_run_lookup is None:
375
+ run = fetch_actions_run(repo, run_id, tokens=token_list)
376
+ else:
377
+ run = dict(actions_run_lookup(run_id))
378
+ except Exception:
379
+ return False
380
+
381
+ path = str(run.get("path") or "")
382
+ if not workflow_path_matches(path, trusted_workflows):
383
+ return False
384
+ if str(run.get("event") or "") not in TRUSTED_ACTIONS_AUDIT_EVENTS:
385
+ return False
386
+
387
+ run_head_sha = str(run.get("head_sha") or "")
388
+ pull_requests = run.get("pull_requests")
389
+ if isinstance(pull_requests, list) and pull_requests:
390
+ return any(
391
+ isinstance(item, Mapping)
392
+ and _pr_item_matches_head(
393
+ item,
394
+ issue_number=issue_number,
395
+ head_sha=head_sha,
396
+ fallback_head_sha=run_head_sha,
397
+ )
398
+ for item in pull_requests
399
+ )
400
+ if not run_head_sha or not sha_matches(run_head_sha, head_sha):
401
+ return False
402
+
403
+ try:
404
+ if commit_pull_requests_lookup is None:
405
+ associated_prs = fetch_pull_requests_for_commit(repo, run_head_sha, tokens=token_list)
406
+ else:
407
+ associated_prs = commit_pull_requests_lookup(run_head_sha)
408
+ except Exception:
409
+ return False
410
+ return any(
411
+ isinstance(item, Mapping)
412
+ and _pr_item_matches_head(
413
+ item,
414
+ issue_number=issue_number,
415
+ head_sha=head_sha,
416
+ fallback_head_sha=run_head_sha,
417
+ )
418
+ for item in associated_prs
419
+ )
420
+
421
+
201
422
  def fetch_pull_request(
202
423
  repo: str,
203
424
  issue_number: int,