wade-cli 1.1.0__tar.gz → 1.1.2__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 (421) hide show
  1. {wade_cli-1.1.0 → wade_cli-1.1.2}/AGENTS.md +7 -0
  2. {wade_cli-1.1.0 → wade_cli-1.1.2}/CHANGELOG.md +12 -0
  3. {wade_cli-1.1.0 → wade_cli-1.1.2}/KNOWLEDGE.ratings.jsonl +2 -0
  4. {wade_cli-1.1.0 → wade_cli-1.1.2}/PKG-INFO +25 -4
  5. {wade_cli-1.1.0 → wade_cli-1.1.2}/README.md +24 -3
  6. {wade_cli-1.1.0 → wade_cli-1.1.2}/docs/dev/architecture.md +26 -2
  7. {wade_cli-1.1.0 → wade_cli-1.1.2}/docs/dev/workflows-and-skills.md +27 -1
  8. {wade_cli-1.1.0 → wade_cli-1.1.2}/pyproject.toml +1 -1
  9. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/__init__.py +1 -1
  10. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/git/repo.py +12 -0
  11. wade_cli-1.1.2/src/wade/models/review_cycle.py +69 -0
  12. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/workflow.py +4 -4
  13. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/implementation_service/done.py +13 -0
  14. wade_cli-1.1.2/src/wade/services/review_cycle_service.py +192 -0
  15. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/review_delegation_service.py +382 -85
  16. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/review_record_service.py +70 -0
  17. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/review_service.py +108 -0
  18. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/skills/materializer.py +9 -3
  19. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/safe_state.py +46 -0
  20. wade_cli-1.1.2/templates/prompts/review-code.md +9 -0
  21. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/_partials/review-budget.md +7 -3
  22. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/implementation.md +3 -3
  23. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/review-pr-comments.md +3 -3
  24. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/test_implement_work_done_contract.py +6 -0
  25. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/integration/test_skill_install.py +18 -1
  26. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_done_gates.py +2 -2
  27. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_done_idempotent.py +26 -6
  28. wade_cli-1.1.2/tests/unit/test_services/test_review_cycle_service.py +235 -0
  29. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_review_delegation_service.py +173 -0
  30. wade_cli-1.1.2/tests/unit/test_services/test_review_receipts.py +999 -0
  31. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_review_records.py +82 -3
  32. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_review_status_block.py +2 -2
  33. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_skills/test_review_budget_partial.py +13 -0
  34. wade_cli-1.1.0/templates/prompts/review-code.md +0 -7
  35. wade_cli-1.1.0/tests/unit/test_services/test_review_receipts.py +0 -497
  36. {wade_cli-1.1.0 → wade_cli-1.1.2}/.gitattributes +0 -0
  37. {wade_cli-1.1.0 → wade_cli-1.1.2}/.github/workflows/auto-version.yml +0 -0
  38. {wade_cli-1.1.0 → wade_cli-1.1.2}/.github/workflows/ci.yml +0 -0
  39. {wade_cli-1.1.0 → wade_cli-1.1.2}/.github/workflows/pr-title-lint.yml +0 -0
  40. {wade_cli-1.1.0 → wade_cli-1.1.2}/.github/workflows/publish.yml +0 -0
  41. {wade_cli-1.1.0 → wade_cli-1.1.2}/.github/workflows/release.yml +0 -0
  42. {wade_cli-1.1.0 → wade_cli-1.1.2}/.gitignore +0 -0
  43. {wade_cli-1.1.0 → wade_cli-1.1.2}/.pre-commit-config.yaml +0 -0
  44. {wade_cli-1.1.0 → wade_cli-1.1.2}/.wade.yml +0 -0
  45. {wade_cli-1.1.0 → wade_cli-1.1.2}/CONTRIBUTING.md +0 -0
  46. {wade_cli-1.1.0 → wade_cli-1.1.2}/KNOWLEDGE.md +0 -0
  47. {wade_cli-1.1.0 → wade_cli-1.1.2}/assets/wade.png +0 -0
  48. {wade_cli-1.1.0 → wade_cli-1.1.2}/docker-compose.e2e.yml +0 -0
  49. {wade_cli-1.1.0 → wade_cli-1.1.2}/docs/dev/documentation-policies.md +0 -0
  50. {wade_cli-1.1.0 → wade_cli-1.1.2}/docs/dev/extending.md +0 -0
  51. {wade_cli-1.1.0 → wade_cli-1.1.2}/docs/dev/skills-system.md +0 -0
  52. {wade_cli-1.1.0 → wade_cli-1.1.2}/docs/dev/testing.md +0 -0
  53. {wade_cli-1.1.0 → wade_cli-1.1.2}/install.sh +0 -0
  54. {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/auto_version.py +0 -0
  55. {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/changelog.py +0 -0
  56. {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/check-all.sh +0 -0
  57. {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/check.sh +0 -0
  58. {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/fmt.sh +0 -0
  59. {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/hooks/pre-push +0 -0
  60. {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/install-hooks.sh +0 -0
  61. {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/release.sh +0 -0
  62. {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/setup-worktree.sh +0 -0
  63. {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/test-concurrency.sh +0 -0
  64. {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/test-e2e-docker.sh +0 -0
  65. {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/test-e2e.sh +0 -0
  66. {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/test-live-ai-taskr.sh +0 -0
  67. {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/test-live-ai.sh +0 -0
  68. {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/test-live-gh.sh +0 -0
  69. {wade_cli-1.1.0 → wade_cli-1.1.2}/scripts/test.sh +0 -0
  70. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/__main__.py +0 -0
  71. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/__init__.py +0 -0
  72. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/admin.py +0 -0
  73. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/autocomplete.py +0 -0
  74. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/deps_session.py +0 -0
  75. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/hook.py +0 -0
  76. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/implementation_session.py +0 -0
  77. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/knowledge.py +0 -0
  78. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/main.py +0 -0
  79. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/plan_session.py +0 -0
  80. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/review.py +0 -0
  81. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/review_pr_comments_session.py +0 -0
  82. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/session.py +0 -0
  83. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/session_shared.py +0 -0
  84. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/skills.py +0 -0
  85. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/task.py +0 -0
  86. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/cli/worktree.py +0 -0
  87. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/config/__init__.py +0 -0
  88. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/config/loader.py +0 -0
  89. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/config/migrations.py +0 -0
  90. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/config/schema.py +0 -0
  91. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/git/__init__.py +0 -0
  92. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/git/branch.py +0 -0
  93. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/git/hooks.py +0 -0
  94. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/git/pr.py +0 -0
  95. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/git/stash.py +0 -0
  96. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/git/sync.py +0 -0
  97. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/git/worktree.py +0 -0
  98. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/hooks/__init__.py +0 -0
  99. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/hooks/cli.py +0 -0
  100. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/hooks/policies.py +0 -0
  101. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/logging/__init__.py +0 -0
  102. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/logging/context.py +0 -0
  103. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/logging/setup.py +0 -0
  104. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/__init__.py +0 -0
  105. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/batch.py +0 -0
  106. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/config.py +0 -0
  107. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/delegation.py +0 -0
  108. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/deps.py +0 -0
  109. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/events.py +0 -0
  110. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/hooks.py +0 -0
  111. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/permission.py +0 -0
  112. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/readiness.py +0 -0
  113. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/review.py +0 -0
  114. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/session.py +0 -0
  115. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/session_manifest.py +0 -0
  116. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/skill.py +0 -0
  117. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/task.py +0 -0
  118. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/models/worktree.py +0 -0
  119. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/providers/__init__.py +0 -0
  120. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/providers/_pr_delegate.py +0 -0
  121. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/providers/base.py +0 -0
  122. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/providers/clickup.py +0 -0
  123. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/providers/github.py +0 -0
  124. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/providers/markdown.py +0 -0
  125. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/providers/registry.py +0 -0
  126. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/__init__.py +0 -0
  127. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/ai_resolution.py +0 -0
  128. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/batch_review_service.py +0 -0
  129. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/bot_trigger.py +0 -0
  130. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/check_service.py +0 -0
  131. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/delegation_service.py +0 -0
  132. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/deps_service.py +0 -0
  133. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/documentation_receipt_service.py +0 -0
  134. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/implementation_service/__init__.py +0 -0
  135. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/implementation_service/_shared.py +0 -0
  136. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/implementation_service/batch.py +0 -0
  137. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/implementation_service/bootstrap.py +0 -0
  138. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/implementation_service/cleanup.py +0 -0
  139. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/implementation_service/core.py +0 -0
  140. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/implementation_service/draft_pr.py +0 -0
  141. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/implementation_service/lifecycle.py +0 -0
  142. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/implementation_service/sync.py +0 -0
  143. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/implementation_service/usage_tracking.py +0 -0
  144. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/init_service/__init__.py +0 -0
  145. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/init_service/auth.py +0 -0
  146. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/init_service/commands.py +0 -0
  147. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/init_service/config_io.py +0 -0
  148. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/init_service/manifest.py +0 -0
  149. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/init_service/migrations.py +0 -0
  150. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/init_service/prompts_ai.py +0 -0
  151. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/init_service/prompts_setup.py +0 -0
  152. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/init_service/shell.py +0 -0
  153. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/knowledge_recovery.py +0 -0
  154. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/knowledge_search.py +0 -0
  155. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/knowledge_service.py +0 -0
  156. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/plan_service.py +0 -0
  157. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/prompt_delivery.py +0 -0
  158. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/review_settle.py +0 -0
  159. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/session_composition_service.py +0 -0
  160. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/skill_diagnostics_service.py +0 -0
  161. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/skill_invocation_service.py +0 -0
  162. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/smart_start.py +0 -0
  163. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/services/task_service.py +0 -0
  164. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/skills/__init__.py +0 -0
  165. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/skills/catalog.py +0 -0
  166. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/skills/discovery.py +0 -0
  167. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/skills/doc_targets.py +0 -0
  168. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/skills/installer.py +0 -0
  169. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/skills/pointer.py +0 -0
  170. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/skills/resolver.py +0 -0
  171. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/skills/validation.py +0 -0
  172. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/ui/__init__.py +0 -0
  173. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/ui/console.py +0 -0
  174. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/ui/prompts.py +0 -0
  175. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/__init__.py +0 -0
  176. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/body_markers.py +0 -0
  177. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/clipboard.py +0 -0
  178. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/conventional.py +0 -0
  179. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/filelock.py +0 -0
  180. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/gitref.py +0 -0
  181. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/http.py +0 -0
  182. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/install.py +0 -0
  183. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/knowledge_file.py +0 -0
  184. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/markdown.py +0 -0
  185. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/markers.py +0 -0
  186. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/paths.py +0 -0
  187. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/plan_validation.py +0 -0
  188. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/process.py +0 -0
  189. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/runtime_env.py +0 -0
  190. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/slug.py +0 -0
  191. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/stale_base.py +0 -0
  192. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/templates.py +0 -0
  193. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/terminal.py +0 -0
  194. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/token_usage_markdown.py +0 -0
  195. {wade_cli-1.1.0 → wade_cli-1.1.2}/src/wade/utils/update_check.py +0 -0
  196. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/agents-pointer.md +0 -0
  197. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/hooks/commit-msg +0 -0
  198. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/hooks/pre-commit +0 -0
  199. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/hooks/pre-push +0 -0
  200. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/prompts/deps-analysis.md +0 -0
  201. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/prompts/implement-context.md +0 -0
  202. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/prompts/plan-session.md +0 -0
  203. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/prompts/review-batch.md +0 -0
  204. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/prompts/review-plan.md +0 -0
  205. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/prompts/review-pr-comments.md +0 -0
  206. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/prompts/session-start-implement.md +0 -0
  207. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/prompts/session-start-plan.md +0 -0
  208. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/prompts/session-start-review.md +0 -0
  209. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/setup-worktree.sh.example +0 -0
  210. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/batch-review/SKILL.md +0 -0
  211. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/code-review/SKILL.md +0 -0
  212. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/dependency-analysis/SKILL.md +0 -0
  213. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/implementation/SKILL.md +0 -0
  214. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/knowledge/SKILL.md +0 -0
  215. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/plan-review/SKILL.md +0 -0
  216. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/planning/SKILL.md +0 -0
  217. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/review-comments/SKILL.md +0 -0
  218. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/task/SKILL.md +0 -0
  219. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/task/examples.md +0 -0
  220. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/task/plan-format.md +0 -0
  221. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/skills/task/reference/session-summary-format.md +0 -0
  222. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/statusline-command.sh +0 -0
  223. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/_partials/completion.md +0 -0
  224. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/_partials/documentation-step.md +0 -0
  225. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/_partials/interaction-policy.md +0 -0
  226. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/_partials/knowledge-step.md +0 -0
  227. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/plan.md +0 -0
  228. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/reference/documentation.md +0 -0
  229. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/reference/new-plan.md +0 -0
  230. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/reference/plan-output-contract.md +0 -0
  231. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/reference/pr-summary-format.md +0 -0
  232. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/reference/recovery.md +0 -0
  233. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/reference/session-summary-format.md +0 -0
  234. {wade_cli-1.1.0 → wade_cli-1.1.2}/templates/workflows/reference/tracking-issues.md +0 -0
  235. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/__init__.py +0 -0
  236. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/concurrency/__init__.py +0 -0
  237. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/concurrency/test_batch_multi_pr.py +0 -0
  238. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/concurrency/test_knowledge_merge.py +0 -0
  239. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/concurrency/test_lock_retry.py +0 -0
  240. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/concurrency/test_stash_race.py +0 -0
  241. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/conftest.py +0 -0
  242. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/__init__.py +0 -0
  243. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/_support.py +0 -0
  244. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/conftest.py +0 -0
  245. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/mock_gh_script.py +0 -0
  246. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/test_admin_contract.py +0 -0
  247. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/test_check_contract.py +0 -0
  248. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/test_knowledge_contract.py +0 -0
  249. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/test_mock_gh_contract.py +0 -0
  250. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/test_plan_contract.py +0 -0
  251. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/test_review_contract.py +0 -0
  252. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/test_smart_start_contract.py +0 -0
  253. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/test_task_contract.py +0 -0
  254. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/e2e/test_work_sync_list_contract.py +0 -0
  255. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/fixtures/live/taskr_header_hi_body.md +0 -0
  256. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/fixtures/live/taskr_header_howdy_body.md +0 -0
  257. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/fixtures/transcripts/claude_session.txt +0 -0
  258. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/fixtures/transcripts/codex_session.txt +0 -0
  259. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/fixtures/transcripts/copilot_session.txt +0 -0
  260. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/fixtures/transcripts/generic_session.txt +0 -0
  261. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/integration/__init__.py +0 -0
  262. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/integration/test_autostash.py +0 -0
  263. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/integration/test_check.py +0 -0
  264. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/integration/test_dynamic_skill_sessions.py +0 -0
  265. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/integration/test_git.py +0 -0
  266. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/integration/test_implementation_lifecycle.py +0 -0
  267. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/integration/test_init.py +0 -0
  268. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/integration/test_knowledge.py +0 -0
  269. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/integration/test_markdown_provider.py +0 -0
  270. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/integration/test_skill_context_budget.py +0 -0
  271. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/live/__init__.py +0 -0
  272. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/live/test_wade_live_ai.py +0 -0
  273. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/live/test_wade_live_ai_taskr.py +0 -0
  274. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/live/test_wade_live_gh.py +0 -0
  275. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/test_cli_basics.py +0 -0
  276. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/__init__.py +0 -0
  277. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/conftest.py +0 -0
  278. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/__init__.py +0 -0
  279. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/test_autocomplete.py +0 -0
  280. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/test_done_doc_pass_advisory.py +0 -0
  281. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/test_done_review_hint.py +0 -0
  282. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/test_knowledge_cli.py +0 -0
  283. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/test_review_cli.py +0 -0
  284. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/test_session_subapps.py +0 -0
  285. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/test_shell_init.py +0 -0
  286. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/test_skills_session_cli.py +0 -0
  287. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/test_task_cli.py +0 -0
  288. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/test_work_ai_flags.py +0 -0
  289. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_cli/test_work_cli.py +0 -0
  290. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_config/__init__.py +0 -0
  291. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_config/test_bot_review_config.py +0 -0
  292. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_config/test_dynamic_skill_config.py +0 -0
  293. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_config/test_loader.py +0 -0
  294. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_config/test_merge_strategy_migration.py +0 -0
  295. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_config/test_migrations.py +0 -0
  296. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_config/test_migrations_atomic.py +0 -0
  297. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_console.py +0 -0
  298. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_effort.py +0 -0
  299. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/__init__.py +0 -0
  300. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_all_patches_present.py +0 -0
  301. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_branch_list.py +0 -0
  302. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_branch_slugify.py +0 -0
  303. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_detached_worktree.py +0 -0
  304. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_is_merged_into.py +0 -0
  305. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_main_checkout_root.py +0 -0
  306. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_merge_count.py +0 -0
  307. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_pr_base.py +0 -0
  308. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_pr_lookup.py +0 -0
  309. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_pr_retry.py +0 -0
  310. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_pr_title.py +0 -0
  311. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_repo.py +0 -0
  312. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_reset_branch.py +0 -0
  313. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_scaffold_commit.py +0 -0
  314. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_stash_drop.py +0 -0
  315. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_sync_conflicted_files.py +0 -0
  316. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_tip_commit_is_empty.py +0 -0
  317. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_git/test_worktree_remove.py +0 -0
  318. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_hooks/__init__.py +0 -0
  319. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_hooks/test_hook_cli.py +0 -0
  320. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_hooks/test_policies.py +0 -0
  321. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_hooks/test_post_tool_use.py +0 -0
  322. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_hooks/test_session_start.py +0 -0
  323. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_models/__init__.py +0 -0
  324. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_models/test_batch.py +0 -0
  325. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_models/test_config_models.py +0 -0
  326. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_models/test_delegation.py +0 -0
  327. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_models/test_deps_models.py +0 -0
  328. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_models/test_deps_partition.py +0 -0
  329. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_models/test_review.py +0 -0
  330. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_models/test_review_expectation.py +0 -0
  331. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_models/test_task_models.py +0 -0
  332. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_models/test_workflow_models.py +0 -0
  333. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_permission_mode.py +0 -0
  334. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_providers/__init__.py +0 -0
  335. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_providers/test_clickup.py +0 -0
  336. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_providers/test_github.py +0 -0
  337. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_providers/test_github_pr.py +0 -0
  338. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_providers/test_markdown.py +0 -0
  339. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_scripts/__init__.py +0 -0
  340. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_scripts/test_changelog.py +0 -0
  341. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/__init__.py +0 -0
  342. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_ai_resolution.py +0 -0
  343. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_batch_build_cmd.py +0 -0
  344. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_batch_pr_index.py +0 -0
  345. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_batch_review_service.py +0 -0
  346. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_bootstrap_allowlist.py +0 -0
  347. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_bootstrap_guard_matcher.py +0 -0
  348. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_bootstrap_hook_timeout.py +0 -0
  349. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_bootstrap_post_tool_use.py +0 -0
  350. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_bootstrap_pre_push.py +0 -0
  351. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_bootstrap_session_start.py +0 -0
  352. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_bot_trigger.py +0 -0
  353. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_catchup.py +0 -0
  354. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_catchup_reconcile.py +0 -0
  355. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_check.py +0 -0
  356. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_cleanup_loss_guard.py +0 -0
  357. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_codex_worktree_launch_context.py +0 -0
  358. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_delegation_service.py +0 -0
  359. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_deps_service.py +0 -0
  360. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_documentation_receipt.py +0 -0
  361. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_done_plan_flag.py +0 -0
  362. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_done_pr_title_sync.py +0 -0
  363. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_draft_pr_base_fetch.py +0 -0
  364. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_draft_pr_retarget.py +0 -0
  365. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_implementation_done_sync.py +0 -0
  366. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_implementation_launch_context.py +0 -0
  367. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_implementation_service.py +0 -0
  368. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_implementation_service_resume.py +0 -0
  369. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_init.py +0 -0
  370. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_init_shell_integration.py +0 -0
  371. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_knowledge_recovery.py +0 -0
  372. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_knowledge_search.py +0 -0
  373. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_knowledge_service.py +0 -0
  374. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_managed_git_hooks.py +0 -0
  375. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_no_direct_git.py +0 -0
  376. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_plan_service.py +0 -0
  377. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_poll_for_reviews.py +0 -0
  378. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_post_implementation_lifecycle.py +0 -0
  379. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_pr_lookup_guards.py +0 -0
  380. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_pr_summary_path.py +0 -0
  381. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_pre_push_backstop.py +0 -0
  382. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_preserve_session_data.py +0 -0
  383. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_prompt_delivery.py +0 -0
  384. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_push_recovery.py +0 -0
  385. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_resolve_task_branch.py +0 -0
  386. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_resume_autonomy.py +0 -0
  387. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_review_service.py +0 -0
  388. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_skill_diagnostics_service.py +0 -0
  389. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_smart_start.py +0 -0
  390. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_smart_start_resume.py +0 -0
  391. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_stale_base_surface.py +0 -0
  392. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_sync_base_branch.py +0 -0
  393. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_task_service.py +0 -0
  394. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_tracked_managed_files.py +0 -0
  395. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_services/test_worktree_gitignore.py +0 -0
  396. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_skills/test_crossby_contract.py +0 -0
  397. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_skills/test_doc_targets.py +0 -0
  398. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_skills/test_dynamic_skills.py +0 -0
  399. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_skills/test_generic_builtin_boundaries.py +0 -0
  400. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_skills/test_knowledge_attributes.py +0 -0
  401. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_skills/test_managed_gitignore.py +0 -0
  402. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_skills/test_pointer.py +0 -0
  403. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/__init__.py +0 -0
  404. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_body_markers.py +0 -0
  405. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_conventional.py +0 -0
  406. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_filelock.py +0 -0
  407. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_gitref.py +0 -0
  408. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_http.py +0 -0
  409. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_install.py +0 -0
  410. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_markdown.py +0 -0
  411. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_markers.py +0 -0
  412. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_plan_validation.py +0 -0
  413. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_process.py +0 -0
  414. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_prompts.py +0 -0
  415. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_runtime_env.py +0 -0
  416. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_slug.py +0 -0
  417. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_stale_base.py +0 -0
  418. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_terminal.py +0 -0
  419. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_terminal_launch.py +0 -0
  420. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_utils/test_update_check.py +0 -0
  421. {wade_cli-1.1.0 → wade_cli-1.1.2}/tests/unit/test_yolo.py +0 -0
@@ -72,6 +72,13 @@ No circular dependencies. Models are pure data. Services orchestrate. **Never im
72
72
  > GitHub (PR/issue body markers, labels) and worktree files, **not** SQLite. The
73
73
  > code still exists but is inert; removal is tracked as #357 C5.
74
74
 
75
+ > **Closing-review state** lives in worktree files, not the immutable session
76
+ > bundle: binding-aware receipts are under `.wade/reviews/`, and an active
77
+ > PR-comment feedback cycle is under `.wade/review-cycles/`. The cycle preserves
78
+ > its pre-edit baseline and feedback history across refreshes and is cleared
79
+ > only after a successful PR-comment completion; unsafe state must fall back to
80
+ > a full review.
81
+
75
82
  > **Deterministic git-hook install/reconcile/build logic lives in `git/hooks.py`**
76
83
  > (git layer), not `skills/installer.py`. Packaged template-asset loaders
77
84
  > (prompt/skill/hook templates) live in `utils/templates.py`, a leaf module.
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Conventional Commits](https://conventionalcommits.org/).
6
6
 
7
+ ## [v1.1.2] — 2026-09-08
8
+
9
+ ### Bug Fixes
10
+
11
+ - make closing reviews converge on final incremental changes (#503) (5599fb1)
12
+
13
+ ## [v1.1.1] — 2026-09-08
14
+
15
+ ### Bug Fixes
16
+
17
+ - review limitation on reviews (#500) (3479794)
18
+
7
19
  ## [v1.1.0] — 2026-09-04
8
20
 
9
21
  ### Features
@@ -170,3 +170,5 @@
170
170
  {"dir": "stale", "event_id": "71f6ebba04cc4c86bc4e2d02ed19ea9d", "id": "65ee7eb64d81", "ts": "2026-09-04T22:14:03.707575+00:00"}
171
171
  {"id": "1f23c00660aa", "superseded_by": "4668ee29b835", "ts": "2026-09-04T22:14:10.938819+00:00"}
172
172
  {"id": "65ee7eb64d81", "superseded_by": "54236b4243bc", "ts": "2026-09-04T22:14:17.907524+00:00"}
173
+ {"dir": "up", "event_id": "bc0cace277f74e4490f28148eafb4070", "id": "4d419ec6629c", "ts": "2026-09-07T22:30:30.110983+00:00"}
174
+ {"dir": "up", "event_id": "0901f7e84f80422488ee976fb7f54934", "id": "fb5582aa12e0", "ts": "2026-09-08T10:26:25.322775+00:00"}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: wade-cli
3
- Version: 1.1.0
3
+ Version: 1.1.2
4
4
  Summary: WADE — Workflow for AI-Driven Engineering
5
5
  Project-URL: Homepage, https://github.com/ivanviragine/wade
6
6
  Project-URL: Repository, https://github.com/ivanviragine/wade
@@ -325,7 +325,7 @@ WADE splits review into an **AI review pass** you or the agent can invoke, and
325
325
  | Command | What it reviews |
326
326
  |---------|-----------------|
327
327
  | `wade review plan <file>` | A plan file, before it becomes tasks |
328
- | `wade review implementation` | The current implementation diff (mandatory gate before `done`) |
328
+ | `wade review implementation` | The current implementation change (mandatory gate before `done`) |
329
329
  | `wade review batch <N>` | Coherence across parallel implementation branches |
330
330
  | `wade review pr-comments <N>` | Starts a session to address human/bot PR comments |
331
331
  | `wade review trigger <N>` | Posts configured bot-review trigger comments on the task's PR |
@@ -335,12 +335,33 @@ printing it does not certify that a review happened. After performing that
335
335
  self-review and addressing its findings, run
336
336
  `wade review implementation --ack-self-review` to write the current commit and
337
337
  frozen-review-binding receipt. Successful headless or interactive reviews write
338
- the same receipt directly.
338
+ the same receipt directly. In an implementation session, once the active frozen
339
+ review binding has consumed `done.max_review_passes` (default 2), `wade review
340
+ implementation` exits successfully without launching another reviewer and directs
341
+ you to `wade implementation-session done`. The dispatch cap does not block a
342
+ valid `--ack-self-review`, which still writes its active-binding receipt.
343
+
344
+ The first implementation review for a frozen reviewer binding receives the
345
+ complete branch change. After a successful review and a new fix commit, a
346
+ re-review receives only the committed delta since that successful same-binding
347
+ review, plus any staged or unstaged changes and the local `PLAN.md` when safely
348
+ available. This narrows repeated review work without weakening `done`: the final
349
+ receipt is still written for the exact current commit. An unreadable record,
350
+ changed binding, unreachable baseline, or merge on the baseline-to-HEAD path
351
+ falls back to the complete branch change.
339
352
 
340
353
  To fetch the unresolved comments themselves during a review session, the agent
341
354
  runs `wade review-pr-comments-session fetch <N>`; it resolves individual threads with
342
355
  `wade review-pr-comments-session resolve`.
343
356
 
357
+ When a PR-comment session starts, WADE records its pre-edit commit and a feedback
358
+ snapshot under local `.wade/review-cycles/` state, outside the immutable session
359
+ bundle. Re-fetching retains prior feedback while adding new feedback, but never
360
+ advances that baseline. Its closing review receives the accumulated feedback and
361
+ the cycle delta; unsafe, mismatched, stale, or merged lineage uses the full
362
+ branch change instead. A successful `review-pr-comments-session done` clears
363
+ the cycle so later feedback starts from the then-current PR head.
364
+
344
365
  ### The auto-launched review session
345
366
 
346
367
  The auto-launched **review session** — the one started when you pick **"Wait
@@ -545,7 +566,7 @@ Decision and integrity gates deliberately require an explicit valid outcome:
545
566
  | PR-SUMMARY | `PR-SUMMARY.md` is missing, empty, or still a template placeholder (both change-producing session types) | `done.require_pr_summary: false` |
546
567
  | Documentation decision | neither an `--updated` nor reasoned `--not-needed` receipt exists for current HEAD | *no disable toggle; `docs --not-needed "<reason>"` records the explicit no-change outcome* |
547
568
  | Sync | the branch is behind main — auto-syncs first, refuses only on conflict (both change-producing session types) | `done.require_sync: false` |
548
- | Review ran | `wade review implementation` has no successful external-review or explicitly acknowledged self-review record for the current commit **and frozen REVIEW binding**, or the frozen workflow/skill bundle no longer matches its manifest. Bundle validation happens before receipts or pass counts are trusted. Prompt emission alone never satisfies this gate. **Implementation sessions bound this loop:** after `done.max_review_passes` (default 2) review→fix→re-review cycles for the active reviewer, `done` completes anyway with a notice instead of looping forever | `--skip-review`, `done.require_review: false` (auto-off when `ai.review_implementation.enabled: false`) |
569
+ | Review ran | `wade review implementation` has no successful external-review or explicitly acknowledged self-review record for the current commit **and frozen REVIEW binding**, or the frozen workflow/skill bundle no longer matches its manifest. Bundle validation happens before receipts or pass counts are trusted. Prompt emission alone never satisfies this gate. **Implementation sessions bound this loop:** at `done.max_review_passes` (default 2), `wade review implementation` stops before launching an excess reviewer, while a valid `--ack-self-review` may still record its receipt; `done` remains authoritative and completes an unreviewed later commit with a cap-reached status instead of looping forever | `--skip-review`, `done.require_review: false` (auto-off when `ai.review_implementation.enabled: false`) |
549
570
  | Resolved threads | unresolved PR review threads remain (review-comments only) | `done.require_resolved_threads: false` |
550
571
  | Conventional title | the task title is not a conventional-commit title (the PR title is derived from it, so it would fail `PR Title Lint`) — blocks; when valid but the open PR's title differs, syncs the PR title to match (both session types) — if that sync fails while the PR's current title is itself non-conventional (lint would fail), `done` fails so it can be retried | `done.require_conventional_title: false` |
551
572
  | Knowledge valid | the knowledge file is structurally corrupt — duplicate entry IDs or unresolved conflict markers (e.g. from a `merge=union` merge) | *none — gated by `knowledge.enabled`; no `done.*` hatch* |
@@ -289,7 +289,7 @@ WADE splits review into an **AI review pass** you or the agent can invoke, and
289
289
  | Command | What it reviews |
290
290
  |---------|-----------------|
291
291
  | `wade review plan <file>` | A plan file, before it becomes tasks |
292
- | `wade review implementation` | The current implementation diff (mandatory gate before `done`) |
292
+ | `wade review implementation` | The current implementation change (mandatory gate before `done`) |
293
293
  | `wade review batch <N>` | Coherence across parallel implementation branches |
294
294
  | `wade review pr-comments <N>` | Starts a session to address human/bot PR comments |
295
295
  | `wade review trigger <N>` | Posts configured bot-review trigger comments on the task's PR |
@@ -299,12 +299,33 @@ printing it does not certify that a review happened. After performing that
299
299
  self-review and addressing its findings, run
300
300
  `wade review implementation --ack-self-review` to write the current commit and
301
301
  frozen-review-binding receipt. Successful headless or interactive reviews write
302
- the same receipt directly.
302
+ the same receipt directly. In an implementation session, once the active frozen
303
+ review binding has consumed `done.max_review_passes` (default 2), `wade review
304
+ implementation` exits successfully without launching another reviewer and directs
305
+ you to `wade implementation-session done`. The dispatch cap does not block a
306
+ valid `--ack-self-review`, which still writes its active-binding receipt.
307
+
308
+ The first implementation review for a frozen reviewer binding receives the
309
+ complete branch change. After a successful review and a new fix commit, a
310
+ re-review receives only the committed delta since that successful same-binding
311
+ review, plus any staged or unstaged changes and the local `PLAN.md` when safely
312
+ available. This narrows repeated review work without weakening `done`: the final
313
+ receipt is still written for the exact current commit. An unreadable record,
314
+ changed binding, unreachable baseline, or merge on the baseline-to-HEAD path
315
+ falls back to the complete branch change.
303
316
 
304
317
  To fetch the unresolved comments themselves during a review session, the agent
305
318
  runs `wade review-pr-comments-session fetch <N>`; it resolves individual threads with
306
319
  `wade review-pr-comments-session resolve`.
307
320
 
321
+ When a PR-comment session starts, WADE records its pre-edit commit and a feedback
322
+ snapshot under local `.wade/review-cycles/` state, outside the immutable session
323
+ bundle. Re-fetching retains prior feedback while adding new feedback, but never
324
+ advances that baseline. Its closing review receives the accumulated feedback and
325
+ the cycle delta; unsafe, mismatched, stale, or merged lineage uses the full
326
+ branch change instead. A successful `review-pr-comments-session done` clears
327
+ the cycle so later feedback starts from the then-current PR head.
328
+
308
329
  ### The auto-launched review session
309
330
 
310
331
  The auto-launched **review session** — the one started when you pick **"Wait
@@ -509,7 +530,7 @@ Decision and integrity gates deliberately require an explicit valid outcome:
509
530
  | PR-SUMMARY | `PR-SUMMARY.md` is missing, empty, or still a template placeholder (both change-producing session types) | `done.require_pr_summary: false` |
510
531
  | Documentation decision | neither an `--updated` nor reasoned `--not-needed` receipt exists for current HEAD | *no disable toggle; `docs --not-needed "<reason>"` records the explicit no-change outcome* |
511
532
  | Sync | the branch is behind main — auto-syncs first, refuses only on conflict (both change-producing session types) | `done.require_sync: false` |
512
- | Review ran | `wade review implementation` has no successful external-review or explicitly acknowledged self-review record for the current commit **and frozen REVIEW binding**, or the frozen workflow/skill bundle no longer matches its manifest. Bundle validation happens before receipts or pass counts are trusted. Prompt emission alone never satisfies this gate. **Implementation sessions bound this loop:** after `done.max_review_passes` (default 2) review→fix→re-review cycles for the active reviewer, `done` completes anyway with a notice instead of looping forever | `--skip-review`, `done.require_review: false` (auto-off when `ai.review_implementation.enabled: false`) |
533
+ | Review ran | `wade review implementation` has no successful external-review or explicitly acknowledged self-review record for the current commit **and frozen REVIEW binding**, or the frozen workflow/skill bundle no longer matches its manifest. Bundle validation happens before receipts or pass counts are trusted. Prompt emission alone never satisfies this gate. **Implementation sessions bound this loop:** at `done.max_review_passes` (default 2), `wade review implementation` stops before launching an excess reviewer, while a valid `--ack-self-review` may still record its receipt; `done` remains authoritative and completes an unreviewed later commit with a cap-reached status instead of looping forever | `--skip-review`, `done.require_review: false` (auto-off when `ai.review_implementation.enabled: false`) |
513
534
  | Resolved threads | unresolved PR review threads remain (review-comments only) | `done.require_resolved_threads: false` |
514
535
  | Conventional title | the task title is not a conventional-commit title (the PR title is derived from it, so it would fail `PR Title Lint`) — blocks; when valid but the open PR's title differs, syncs the PR title to match (both session types) — if that sync fails while the PR's current title is itself non-conventional (lint would fail), `done` fails so it can be retried | `done.require_conventional_title: false` |
515
536
  | Knowledge valid | the knowledge file is structurally corrupt — duplicate entry IDs or unresolved conflict markers (e.g. from a `merge=union` merge) | *none — gated by `knowledge.enabled`; no `done.*` hatch* |
@@ -87,6 +87,7 @@ src/wade/
87
87
  │ ├── skill_invocation_service.py # Bind methods to bounded operation contracts
88
88
  │ ├── skill_diagnostics_service.py # Discovery and precedence diagnostics
89
89
  │ ├── review_record_service.py # Binding-aware durable review outcomes
90
+ │ ├── review_cycle_service.py # Safe PR-comment feedback-cycle context
90
91
  │ ├── documentation_receipt_service.py # Current-commit documentation decision
91
92
  │ ├── batch_review_service.py # Batch issue review
92
93
  │ ├── deps_service.py # Dependency analysis
@@ -800,7 +801,30 @@ The implementation-review pass cap distinguishes an attempted, bounded review
800
801
  from a reviewer that never launched: success and a true headless timeout count
801
802
  toward `done.max_review_passes`, but missing credentials/PATH or a sandbox exec
802
803
  denial do not. Otherwise an unavailable reviewer could exhaust the cap and let
803
- `done` bypass its review gate without any review attempt.
804
+ `done` bypass its review gate without any review attempt. For an implementation
805
+ session, `wade review implementation` consults that same active frozen-binding
806
+ count before dispatch and skips an excess launch; `implementation-session done`
807
+ remains the authoritative classifier for accepting an unreviewed later commit
808
+ and projecting its cap-reached PR review status. A valid `--ack-self-review`
809
+ still records the current binding receipt because it acknowledges work already
810
+ performed rather than dispatching a reviewer. Plan and PR-comment reviews are
811
+ uncapped.
812
+
813
+ Review input converges without weakening completion evidence. The first mapped
814
+ implementation review uses the full branch diff; later reviews may select the
815
+ nearest satisfying record for the same frozen REVIEW binding that is an ancestor
816
+ of `HEAD`, then inspect only the delta from that commit. A Git error, malformed
817
+ or foreign record, non-ancestor, or merge on that ancestry path forces the full
818
+ branch diff. Staged and unstaged changes remain separate inputs in either case,
819
+ and every review writes its ordinary exact-HEAD receipt.
820
+
821
+ `review_cycle_service.py` stores the PR-comment session's issue/PR identity,
822
+ pre-edit baseline, and normalized feedback in `.wade/review-cycles/`, outside
823
+ the immutable session bundle. Re-fetching retains prior feedback and adds new
824
+ feedback while preserving the baseline; a successful PR-comment `done` removes
825
+ it. The closing review receives the accumulated feedback and cycle delta only
826
+ after safe state, identity, ancestry, and merge-path checks. Any failed check
827
+ emits a diagnostic and reviews the complete branch diff instead.
804
828
 
805
829
  The capability remediation is intentionally tool-neutral: retain the sandbox
806
830
  and grant only the worktree Git metadata paths, GitHub credential/API route, or
@@ -911,7 +935,7 @@ includes the active binding's pass count. The structured worktree records under
911
935
  `.wade/reviews/` provide deterministic gate evidence; the PR-body projection is
912
936
  the durable human-readable outcome.
913
937
 
914
- Binding-aware review records and documentation receipts use the descriptor-safe
938
+ Binding-aware review records, review-cycle contexts, and documentation receipts use the descriptor-safe
915
939
  state primitives in `utils/safe_state.py`: `.wade` and intermediate directories
916
940
  must be real directories, final files are opened relative to trusted directory
917
941
  handles, and malformed/symlinked/unreadable state is treated as absent. Record
@@ -175,6 +175,14 @@ An implementation-to-PR-comment transition is a new session and replaces the
175
175
  manifest/workflow. Durable review records survive outside the session bundle,
176
176
  but apply only if the new session's review binding is identical.
177
177
 
178
+ The implementation and PR-comment definitions currently use workflow revision
179
+ 2. Their authoritative order is verification, documentation decision, knowledge,
180
+ then closing method review; an old frozen revision is rejected until an explicit
181
+ refresh replaces it. Expected tracked documentation and knowledge changes are
182
+ therefore normally present in the final review. If a review finding creates a
183
+ new tracked commit, the session repeats affected verification/documentation work
184
+ and reviews that new final commit.
185
+
178
186
  ## Bounded invocation contract
179
187
 
180
188
  `skill_invocation_service.py` composes bounded prompts structurally in this
@@ -228,7 +236,25 @@ active binding. Switching A to B preserves A's history but makes it inapplicable
228
236
  switching back to byte-identical A at the same commit reuses its record. Refresh
229
237
  of only WORK does not invalidate a REVIEW record.
230
238
 
231
- Manifest, review, and documentation-gate state uses descriptor-relative,
239
+ The first implementation review for a REVIEW binding uses the full branch diff.
240
+ A later one may use the nearest same-binding satisfying record that is a safe
241
+ ancestor of `HEAD`, reviewing only its committed delta while retaining staged and
242
+ unstaged sections. Git errors, malformed or other-binding records, non-ancestors,
243
+ and any merge on the selected ancestry path fail open to the full branch diff.
244
+ The record is only an input baseline: every completed review still writes the
245
+ normal exact-`HEAD`, binding-aware receipt.
246
+
247
+ PR-comment cycles persist separately at
248
+ `.wade/review-cycles/review-cycle@<issue>.json`, rather than in immutable
249
+ `.wade/session/` methodology snapshots. A cycle captures the open PR identity,
250
+ the pre-edit baseline, and normalized fetched feedback. Starting or resuming a
251
+ cycle preserves its baseline and retains prior feedback while adding newly
252
+ fetched feedback; successful `review-pr-comments-session done` removes the
253
+ context. The closing review uses that accumulated feedback plus the cycle delta
254
+ only after identity, ancestry, and merge-path validation; unreadable, mismatched,
255
+ malformed, or unsafe state falls back to a full diff.
256
+
257
+ Manifest, review, review-cycle, and documentation-gate state uses descriptor-relative,
232
258
  no-follow filesystem operations. Unsafe/malformed/unreadable state is absent for
233
259
  gate purposes and fails toward re-review, never toward completion. SHA marker
234
260
  files are never accepted as review evidence. The completion classifier also
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "wade-cli"
7
- version = "1.1.0"
7
+ version = "1.1.2"
8
8
  description = "WADE — Workflow for AI-Driven Engineering"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -1,3 +1,3 @@
1
1
  """WADE (Workflow for AI-Driven Engineering) — AI-agent-driven git workflow management CLI."""
2
2
 
3
- __version__ = "1.1.0"
3
+ __version__ = "1.1.2"
@@ -686,6 +686,18 @@ def diff_between_checked(repo_root: Path, base: str, head: str) -> str:
686
686
  return _run_git("diff", f"{base}...{head}", cwd=repo_root).stdout
687
687
 
688
688
 
689
+ def has_merge_commit_between(repo_root: Path, base: str, head: str) -> bool:
690
+ """Return whether the ancestry path from *base* to *head* contains a merge.
691
+
692
+ Callers first establish that ``base`` is an ancestor of ``head``. Keeping
693
+ this query in the git layer lets review-input selection fail open when git
694
+ cannot establish a trustworthy incremental lineage.
695
+ """
696
+
697
+ result = _run_git("rev-list", "--merges", f"{base}..{head}", cwd=repo_root)
698
+ return bool(result.stdout.strip())
699
+
700
+
689
701
  def diff_worktree(repo_root: Path, *, staged: bool = False) -> str:
690
702
  """Return ``git diff`` output for the working tree (or the staged index).
691
703
 
@@ -0,0 +1,69 @@
1
+ """Persisted PR-comment review-cycle context."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import re
7
+
8
+ from pydantic import BaseModel, Field, field_validator, model_validator
9
+
10
+ REVIEW_CYCLE_SCHEMA_VERSION = 1
11
+ # A persisted context includes JSON syntax and other fields, so its actual
12
+ # serialized byte length remains the authoritative limit.
13
+ MAX_REVIEW_CYCLE_PAYLOAD_BYTES = 256 * 1024
14
+ MAX_REVIEW_FEEDBACK_CHARS = MAX_REVIEW_CYCLE_PAYLOAD_BYTES
15
+ _COMMIT_RE = re.compile(r"^[0-9a-f]{7,64}$")
16
+
17
+
18
+ class ReviewCycleContext(BaseModel, frozen=True):
19
+ """The immutable baseline and mutable feedback snapshot for one PR cycle."""
20
+
21
+ schema_version: int = Field(default=REVIEW_CYCLE_SCHEMA_VERSION)
22
+ issue_number: str
23
+ pr_number: int = Field(gt=0)
24
+ baseline_commit: str
25
+ feedback: str = Field(min_length=1, max_length=MAX_REVIEW_FEEDBACK_CHARS)
26
+
27
+ @field_validator("schema_version")
28
+ @classmethod
29
+ def _schema(cls, value: int) -> int:
30
+ if value != REVIEW_CYCLE_SCHEMA_VERSION:
31
+ raise ValueError(f"Unsupported review-cycle schema {value}")
32
+ return value
33
+
34
+ @field_validator("issue_number")
35
+ @classmethod
36
+ def _issue_number(cls, value: str) -> str:
37
+ normalized = value.strip().lstrip("#")
38
+ if not normalized.isdigit() or int(normalized) <= 0:
39
+ raise ValueError("Review-cycle issue number must be positive")
40
+ return str(int(normalized))
41
+
42
+ @field_validator("baseline_commit")
43
+ @classmethod
44
+ def _commit(cls, value: str) -> str:
45
+ if not _COMMIT_RE.fullmatch(value):
46
+ raise ValueError("Review-cycle baseline must be a lowercase hexadecimal git object id")
47
+ return value
48
+
49
+ @field_validator("feedback")
50
+ @classmethod
51
+ def _feedback(cls, value: str) -> str:
52
+ normalized = value.replace("\r\n", "\n").replace("\r", "\n").strip()
53
+ if not normalized:
54
+ raise ValueError("Review-cycle feedback cannot be empty")
55
+ return normalized
56
+
57
+ def serialized_payload(self) -> bytes:
58
+ """Return the exact UTF-8 state-file representation for this context."""
59
+
60
+ return (
61
+ json.dumps(self.model_dump(mode="json"), indent=2, sort_keys=True, ensure_ascii=True)
62
+ + "\n"
63
+ ).encode("utf-8")
64
+
65
+ @model_validator(mode="after")
66
+ def _serialized_payload_fits_state_file(self) -> ReviewCycleContext:
67
+ if len(self.serialized_payload()) > MAX_REVIEW_CYCLE_PAYLOAD_BYTES:
68
+ raise ValueError("Review-cycle serialized payload exceeds the 256 KiB limit")
69
+ return self
@@ -178,7 +178,7 @@ SESSION_DEFINITIONS: dict[SessionKind, SessionDefinition] = {
178
178
  kind=SessionKind.IMPLEMENTATION,
179
179
  ai_command=AICommandKey.IMPLEMENT,
180
180
  workflow_template="implementation.md",
181
- workflow_revision=1,
181
+ workflow_revision=2,
182
182
  launch_prompt="implement-context.md",
183
183
  session_phase=SessionPhase.IMPLEMENT,
184
184
  steps=(
@@ -187,9 +187,9 @@ SESSION_DEFINITIONS: dict[SessionKind, SessionDefinition] = {
187
187
  "understand-plan",
188
188
  "implement",
189
189
  "verify",
190
- "method-review",
191
190
  "documentation",
192
191
  "knowledge",
192
+ "method-review",
193
193
  "pr-summary",
194
194
  "sync",
195
195
  "done",
@@ -214,7 +214,7 @@ SESSION_DEFINITIONS: dict[SessionKind, SessionDefinition] = {
214
214
  kind=SessionKind.REVIEW_PR_COMMENTS,
215
215
  ai_command=AICommandKey.REVIEW_PR_COMMENTS,
216
216
  workflow_template="review-pr-comments.md",
217
- workflow_revision=1,
217
+ workflow_revision=2,
218
218
  launch_prompt="review-pr-comments.md",
219
219
  session_phase=SessionPhase.REVIEW,
220
220
  steps=(
@@ -223,9 +223,9 @@ SESSION_DEFINITIONS: dict[SessionKind, SessionDefinition] = {
223
223
  "verify-feedback",
224
224
  "address-feedback",
225
225
  "verify",
226
- "method-review",
227
226
  "documentation",
228
227
  "knowledge",
228
+ "method-review",
229
229
  "pr-summary",
230
230
  "sync",
231
231
  "resolve-threads",
@@ -46,6 +46,7 @@ from wade.services.implementation_service.lifecycle import (
46
46
  _strip_summary_section,
47
47
  )
48
48
  from wade.services.implementation_service.usage_tracking import IMPL_USAGE_MARKER_START
49
+ from wade.services.review_cycle_service import clear_review_cycle
49
50
  from wade.services.review_record_service import (
50
51
  count_binding_passes,
51
52
  has_other_satisfying_binding,
@@ -387,6 +388,18 @@ def done(
387
388
  # block + skip-worktree still in place) so the user can fix and re-run.
388
389
  return False
389
390
 
391
+ if session_type == "review-pr-comments" and not clear_review_cycle(
392
+ worktree_root, issue_number=issue_number
393
+ ):
394
+ # The PR may already be finalized, but completion remains retryable
395
+ # until the active review-cycle state is gone or durably invalidated.
396
+ # Otherwise a later feedback cycle could inherit an obsolete baseline.
397
+ console.error(
398
+ "Could not clear the completed PR-comment review-cycle state safely; "
399
+ "restore filesystem access and re-run done."
400
+ )
401
+ return False
402
+
390
403
  # Success only: strip the worktree gitignore block and restore .gitignore
391
404
  # visibility now that there is nothing left to retry. The PR is already
392
405
  # updated at this point, so a filesystem cleanup failure (read-only file,
@@ -0,0 +1,192 @@
1
+ """Safe PR-comment review-cycle state outside immutable session bundles."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+
9
+ from pydantic import ValidationError
10
+
11
+ from wade.models.review_cycle import ReviewCycleContext
12
+ from wade.utils.safe_state import (
13
+ MAX_STATE_FILE_BYTES,
14
+ atomic_write_state_file,
15
+ delete_state_file,
16
+ read_state_file,
17
+ state_file_present,
18
+ )
19
+
20
+ _DIRECTORIES = ("review-cycles",)
21
+ _INVALIDATION_DIRECTORIES = ("review-cycle-invalidations",)
22
+ _PREFIX = "review-cycle@"
23
+ _FEEDBACK_SECTION = re.compile(r"(?m)(?=^### )")
24
+
25
+
26
+ @dataclass(frozen=True)
27
+ class ReviewCycleLookup:
28
+ """A context lookup that preserves an unsafe-state diagnostic."""
29
+
30
+ context: ReviewCycleContext | None
31
+ diagnostic: str | None = None
32
+
33
+
34
+ def review_cycle_filename(issue_number: str) -> str:
35
+ """Return the one canonical context filename for an issue's active cycle."""
36
+
37
+ normalized = issue_number.strip().lstrip("#")
38
+ if not normalized.isdigit() or int(normalized) <= 0:
39
+ raise ValueError("Review-cycle issue number must be positive")
40
+ return f"{_PREFIX}{int(normalized)}.json"
41
+
42
+
43
+ def read_review_cycle(
44
+ root: Path,
45
+ *,
46
+ issue_number: str,
47
+ pr_number: int,
48
+ ) -> ReviewCycleLookup:
49
+ """Read a current identity-matched cycle, never trusting malformed state."""
50
+
51
+ try:
52
+ filename = review_cycle_filename(issue_number)
53
+ except ValueError as exc:
54
+ return ReviewCycleLookup(None, str(exc))
55
+ if state_file_present(root, _INVALIDATION_DIRECTORIES, filename):
56
+ return ReviewCycleLookup(
57
+ None,
58
+ "PR-comment review-cycle state was invalidated after an unsafe update",
59
+ )
60
+ raw = read_state_file(root, _DIRECTORIES, filename)
61
+ if raw is None:
62
+ if state_file_present(root, _DIRECTORIES, filename):
63
+ return ReviewCycleLookup(
64
+ None,
65
+ "PR-comment review-cycle state is unreadable or unsafe",
66
+ )
67
+ return ReviewCycleLookup(None)
68
+ try:
69
+ context = ReviewCycleContext.model_validate_json(raw)
70
+ except (ValidationError, ValueError):
71
+ return ReviewCycleLookup(None, "PR-comment review-cycle state is malformed")
72
+ if context.issue_number != str(int(issue_number.lstrip("#"))):
73
+ return ReviewCycleLookup(None, "PR-comment review-cycle issue identity does not match")
74
+ if context.pr_number != pr_number:
75
+ return ReviewCycleLookup(None, "PR-comment review-cycle PR identity does not match")
76
+ return ReviewCycleLookup(context)
77
+
78
+
79
+ def _feedback_sections(feedback: str) -> tuple[str, ...]:
80
+ """Return individually rendered review entries from a feedback snapshot."""
81
+
82
+ return tuple(
83
+ section.strip() for section in _FEEDBACK_SECTION.split(feedback)[1:] if section.strip()
84
+ )
85
+
86
+
87
+ def _merge_feedback(existing_feedback: str, refreshed_feedback: str) -> str:
88
+ """Retain feedback already addressed while adding new feedback from a refresh."""
89
+
90
+ if refreshed_feedback in existing_feedback:
91
+ return existing_feedback
92
+
93
+ refreshed_sections = _feedback_sections(refreshed_feedback)
94
+ if refreshed_sections:
95
+ existing_sections = set(_feedback_sections(existing_feedback))
96
+ additions = [section for section in refreshed_sections if section not in existing_sections]
97
+ if not additions:
98
+ return existing_feedback
99
+ return f"{existing_feedback}\n\n## Feedback added during refresh\n\n" + "\n\n".join(
100
+ additions
101
+ )
102
+
103
+ return f"{existing_feedback}\n\n{refreshed_feedback}"
104
+
105
+
106
+ def initialize_or_refresh_review_cycle(
107
+ root: Path,
108
+ *,
109
+ issue_number: str,
110
+ pr_number: int,
111
+ baseline_commit: str,
112
+ feedback: str,
113
+ ) -> ReviewCycleLookup:
114
+ """Create a cycle or retain its baseline and all feedback across refreshes."""
115
+
116
+ try:
117
+ filename = review_cycle_filename(issue_number)
118
+ except ValueError as exc:
119
+ return ReviewCycleLookup(None, f"Could not validate PR-comment review-cycle state: {exc}")
120
+ invalidation = _discard_invalidated_review_cycle(root, filename)
121
+ if invalidation is not None:
122
+ return ReviewCycleLookup(None, invalidation)
123
+
124
+ existing = read_review_cycle(root, issue_number=issue_number, pr_number=pr_number)
125
+ if existing.diagnostic is not None:
126
+ return existing
127
+ try:
128
+ context = (
129
+ ReviewCycleContext(
130
+ **(
131
+ existing.context.model_dump()
132
+ | {"feedback": _merge_feedback(existing.context.feedback, feedback)}
133
+ )
134
+ )
135
+ if existing.context is not None
136
+ else ReviewCycleContext(
137
+ issue_number=issue_number,
138
+ pr_number=pr_number,
139
+ baseline_commit=baseline_commit,
140
+ feedback=feedback,
141
+ )
142
+ )
143
+ except (ValidationError, ValueError) as exc:
144
+ return ReviewCycleLookup(None, f"Could not validate PR-comment review-cycle state: {exc}")
145
+ payload = context.serialized_payload()
146
+ if len(payload) > MAX_STATE_FILE_BYTES:
147
+ return ReviewCycleLookup(
148
+ None,
149
+ "Could not persist PR-comment review-cycle state: serialized payload exceeds "
150
+ "the 256 KiB limit",
151
+ )
152
+ if not atomic_write_state_file(root, _DIRECTORIES, filename, payload):
153
+ if clear_review_cycle(root, issue_number=issue_number):
154
+ return ReviewCycleLookup(
155
+ None,
156
+ "Could not persist PR-comment review-cycle state safely; the prior state was "
157
+ "invalidated",
158
+ )
159
+ return ReviewCycleLookup(None, "Could not persist PR-comment review-cycle state safely")
160
+ return ReviewCycleLookup(context)
161
+
162
+
163
+ def clear_review_cycle(root: Path, *, issue_number: str) -> bool:
164
+ """Remove an active cycle, or durably invalidate it when removal is unsafe."""
165
+
166
+ try:
167
+ filename = review_cycle_filename(issue_number)
168
+ except ValueError:
169
+ return False
170
+ if not state_file_present(root, _DIRECTORIES, filename):
171
+ return True
172
+ if delete_state_file(root, _DIRECTORIES, filename):
173
+ return True
174
+ # A successful marker makes every later read fail closed, even if the old
175
+ # regular JSON file remains readable after a failed deletion. It lives in
176
+ # a sibling directory so a read-only review-cycles directory can still be
177
+ # invalidated through its writable .wade parent.
178
+ return atomic_write_state_file(root, _INVALIDATION_DIRECTORIES, filename, b"")
179
+
180
+
181
+ def _discard_invalidated_review_cycle(root: Path, filename: str) -> str | None:
182
+ """Clear a previous invalidation before beginning a fresh feedback cycle."""
183
+
184
+ if not state_file_present(root, _INVALIDATION_DIRECTORIES, filename):
185
+ return None
186
+ if state_file_present(root, _DIRECTORIES, filename) and not delete_state_file(
187
+ root, _DIRECTORIES, filename
188
+ ):
189
+ return "Could not discard invalidated PR-comment review-cycle state safely"
190
+ if not delete_state_file(root, _INVALIDATION_DIRECTORIES, filename):
191
+ return "Could not clear PR-comment review-cycle invalidation safely"
192
+ return None