wade-cli 0.53.0__tar.gz → 0.53.1__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 (411) hide show
  1. {wade_cli-0.53.0 → wade_cli-0.53.1}/CHANGELOG.md +6 -0
  2. {wade_cli-0.53.0 → wade_cli-0.53.1}/PKG-INFO +10 -1
  3. {wade_cli-0.53.0 → wade_cli-0.53.1}/README.md +9 -0
  4. {wade_cli-0.53.0 → wade_cli-0.53.1}/docs/dev/architecture.md +11 -0
  5. {wade_cli-0.53.0 → wade_cli-0.53.1}/pyproject.toml +1 -1
  6. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/__init__.py +1 -1
  7. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/implementation_service/core.py +51 -12
  8. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/plan_service.py +4 -1
  9. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/terminal.py +97 -1
  10. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_implementation_launch_context.py +148 -7
  11. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_plan_service.py +1 -1
  12. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_terminal_launch.py +40 -0
  13. {wade_cli-0.53.0 → wade_cli-0.53.1}/.gitattributes +0 -0
  14. {wade_cli-0.53.0 → wade_cli-0.53.1}/.github/workflows/auto-version.yml +0 -0
  15. {wade_cli-0.53.0 → wade_cli-0.53.1}/.github/workflows/ci.yml +0 -0
  16. {wade_cli-0.53.0 → wade_cli-0.53.1}/.github/workflows/pr-title-lint.yml +0 -0
  17. {wade_cli-0.53.0 → wade_cli-0.53.1}/.github/workflows/publish.yml +0 -0
  18. {wade_cli-0.53.0 → wade_cli-0.53.1}/.github/workflows/release.yml +0 -0
  19. {wade_cli-0.53.0 → wade_cli-0.53.1}/.gitignore +0 -0
  20. {wade_cli-0.53.0 → wade_cli-0.53.1}/.pre-commit-config.yaml +0 -0
  21. {wade_cli-0.53.0 → wade_cli-0.53.1}/.wade.yml +0 -0
  22. {wade_cli-0.53.0 → wade_cli-0.53.1}/AGENTS.md +0 -0
  23. {wade_cli-0.53.0 → wade_cli-0.53.1}/CONTRIBUTING.md +0 -0
  24. {wade_cli-0.53.0 → wade_cli-0.53.1}/KNOWLEDGE.md +0 -0
  25. {wade_cli-0.53.0 → wade_cli-0.53.1}/KNOWLEDGE.ratings.jsonl +0 -0
  26. {wade_cli-0.53.0 → wade_cli-0.53.1}/assets/wade.png +0 -0
  27. {wade_cli-0.53.0 → wade_cli-0.53.1}/docker-compose.e2e.yml +0 -0
  28. {wade_cli-0.53.0 → wade_cli-0.53.1}/docs/dev/documentation-policies.md +0 -0
  29. {wade_cli-0.53.0 → wade_cli-0.53.1}/docs/dev/extending.md +0 -0
  30. {wade_cli-0.53.0 → wade_cli-0.53.1}/docs/dev/skills-system.md +0 -0
  31. {wade_cli-0.53.0 → wade_cli-0.53.1}/docs/dev/testing.md +0 -0
  32. {wade_cli-0.53.0 → wade_cli-0.53.1}/docs/dev/workflows-and-skills.md +0 -0
  33. {wade_cli-0.53.0 → wade_cli-0.53.1}/install.sh +0 -0
  34. {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/auto_version.py +0 -0
  35. {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/changelog.py +0 -0
  36. {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/check-all.sh +0 -0
  37. {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/check.sh +0 -0
  38. {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/fmt.sh +0 -0
  39. {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/hooks/pre-push +0 -0
  40. {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/install-hooks.sh +0 -0
  41. {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/release.sh +0 -0
  42. {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/setup-worktree.sh +0 -0
  43. {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/test-concurrency.sh +0 -0
  44. {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/test-e2e-docker.sh +0 -0
  45. {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/test-e2e.sh +0 -0
  46. {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/test-live-ai-taskr.sh +0 -0
  47. {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/test-live-ai.sh +0 -0
  48. {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/test-live-gh.sh +0 -0
  49. {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/test.sh +0 -0
  50. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/__main__.py +0 -0
  51. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/__init__.py +0 -0
  52. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/admin.py +0 -0
  53. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/autocomplete.py +0 -0
  54. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/deps_session.py +0 -0
  55. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/hook.py +0 -0
  56. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/implementation_session.py +0 -0
  57. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/knowledge.py +0 -0
  58. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/main.py +0 -0
  59. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/plan_session.py +0 -0
  60. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/review.py +0 -0
  61. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/review_pr_comments_session.py +0 -0
  62. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/session.py +0 -0
  63. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/session_shared.py +0 -0
  64. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/skills.py +0 -0
  65. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/task.py +0 -0
  66. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/worktree.py +0 -0
  67. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/config/__init__.py +0 -0
  68. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/config/loader.py +0 -0
  69. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/config/migrations.py +0 -0
  70. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/config/schema.py +0 -0
  71. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/git/__init__.py +0 -0
  72. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/git/branch.py +0 -0
  73. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/git/hooks.py +0 -0
  74. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/git/pr.py +0 -0
  75. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/git/repo.py +0 -0
  76. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/git/stash.py +0 -0
  77. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/git/sync.py +0 -0
  78. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/git/worktree.py +0 -0
  79. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/hooks/__init__.py +0 -0
  80. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/hooks/cli.py +0 -0
  81. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/hooks/policies.py +0 -0
  82. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/logging/__init__.py +0 -0
  83. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/logging/context.py +0 -0
  84. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/logging/setup.py +0 -0
  85. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/__init__.py +0 -0
  86. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/batch.py +0 -0
  87. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/config.py +0 -0
  88. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/delegation.py +0 -0
  89. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/deps.py +0 -0
  90. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/events.py +0 -0
  91. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/hooks.py +0 -0
  92. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/permission.py +0 -0
  93. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/readiness.py +0 -0
  94. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/review.py +0 -0
  95. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/session.py +0 -0
  96. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/session_manifest.py +0 -0
  97. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/skill.py +0 -0
  98. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/task.py +0 -0
  99. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/workflow.py +0 -0
  100. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/worktree.py +0 -0
  101. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/providers/__init__.py +0 -0
  102. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/providers/_pr_delegate.py +0 -0
  103. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/providers/base.py +0 -0
  104. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/providers/clickup.py +0 -0
  105. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/providers/github.py +0 -0
  106. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/providers/markdown.py +0 -0
  107. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/providers/registry.py +0 -0
  108. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/__init__.py +0 -0
  109. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/ai_resolution.py +0 -0
  110. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/batch_review_service.py +0 -0
  111. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/bot_trigger.py +0 -0
  112. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/check_service.py +0 -0
  113. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/delegation_service.py +0 -0
  114. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/deps_service.py +0 -0
  115. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/documentation_receipt_service.py +0 -0
  116. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/implementation_service/__init__.py +0 -0
  117. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/implementation_service/_shared.py +0 -0
  118. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/implementation_service/batch.py +0 -0
  119. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/implementation_service/bootstrap.py +0 -0
  120. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/implementation_service/cleanup.py +0 -0
  121. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/implementation_service/done.py +0 -0
  122. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/implementation_service/draft_pr.py +0 -0
  123. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/implementation_service/lifecycle.py +0 -0
  124. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/implementation_service/sync.py +0 -0
  125. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/implementation_service/usage_tracking.py +0 -0
  126. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/init_service/__init__.py +0 -0
  127. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/init_service/auth.py +0 -0
  128. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/init_service/commands.py +0 -0
  129. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/init_service/config_io.py +0 -0
  130. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/init_service/manifest.py +0 -0
  131. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/init_service/migrations.py +0 -0
  132. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/init_service/prompts_ai.py +0 -0
  133. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/init_service/prompts_setup.py +0 -0
  134. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/init_service/shell.py +0 -0
  135. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/knowledge_recovery.py +0 -0
  136. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/knowledge_search.py +0 -0
  137. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/knowledge_service.py +0 -0
  138. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/prompt_delivery.py +0 -0
  139. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/review_delegation_service.py +0 -0
  140. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/review_record_service.py +0 -0
  141. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/review_service.py +0 -0
  142. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/review_settle.py +0 -0
  143. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/session_composition_service.py +0 -0
  144. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/skill_diagnostics_service.py +0 -0
  145. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/skill_invocation_service.py +0 -0
  146. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/smart_start.py +0 -0
  147. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/task_service.py +0 -0
  148. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/skills/__init__.py +0 -0
  149. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/skills/catalog.py +0 -0
  150. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/skills/discovery.py +0 -0
  151. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/skills/doc_targets.py +0 -0
  152. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/skills/installer.py +0 -0
  153. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/skills/materializer.py +0 -0
  154. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/skills/pointer.py +0 -0
  155. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/skills/resolver.py +0 -0
  156. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/skills/validation.py +0 -0
  157. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/ui/__init__.py +0 -0
  158. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/ui/console.py +0 -0
  159. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/ui/prompts.py +0 -0
  160. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/__init__.py +0 -0
  161. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/body_markers.py +0 -0
  162. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/clipboard.py +0 -0
  163. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/conventional.py +0 -0
  164. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/filelock.py +0 -0
  165. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/gitref.py +0 -0
  166. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/http.py +0 -0
  167. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/install.py +0 -0
  168. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/knowledge_file.py +0 -0
  169. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/markdown.py +0 -0
  170. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/markers.py +0 -0
  171. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/paths.py +0 -0
  172. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/plan_validation.py +0 -0
  173. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/process.py +0 -0
  174. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/safe_state.py +0 -0
  175. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/slug.py +0 -0
  176. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/stale_base.py +0 -0
  177. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/templates.py +0 -0
  178. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/token_usage_markdown.py +0 -0
  179. {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/update_check.py +0 -0
  180. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/agents-pointer.md +0 -0
  181. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/hooks/commit-msg +0 -0
  182. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/hooks/pre-commit +0 -0
  183. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/hooks/pre-push +0 -0
  184. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/prompts/deps-analysis.md +0 -0
  185. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/prompts/implement-context.md +0 -0
  186. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/prompts/plan-session.md +0 -0
  187. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/prompts/review-batch.md +0 -0
  188. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/prompts/review-code.md +0 -0
  189. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/prompts/review-plan.md +0 -0
  190. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/prompts/review-pr-comments.md +0 -0
  191. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/prompts/session-start-implement.md +0 -0
  192. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/prompts/session-start-plan.md +0 -0
  193. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/prompts/session-start-review.md +0 -0
  194. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/setup-worktree.sh.example +0 -0
  195. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/batch-review/SKILL.md +0 -0
  196. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/code-review/SKILL.md +0 -0
  197. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/dependency-analysis/SKILL.md +0 -0
  198. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/implementation/SKILL.md +0 -0
  199. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/knowledge/SKILL.md +0 -0
  200. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/plan-review/SKILL.md +0 -0
  201. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/planning/SKILL.md +0 -0
  202. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/review-comments/SKILL.md +0 -0
  203. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/task/SKILL.md +0 -0
  204. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/task/examples.md +0 -0
  205. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/task/plan-format.md +0 -0
  206. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/task/reference/session-summary-format.md +0 -0
  207. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/statusline-command.sh +0 -0
  208. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/_partials/completion.md +0 -0
  209. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/_partials/documentation-step.md +0 -0
  210. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/_partials/interaction-policy.md +0 -0
  211. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/_partials/knowledge-step.md +0 -0
  212. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/_partials/review-budget.md +0 -0
  213. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/implementation.md +0 -0
  214. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/plan.md +0 -0
  215. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/reference/documentation.md +0 -0
  216. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/reference/new-plan.md +0 -0
  217. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/reference/plan-output-contract.md +0 -0
  218. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/reference/pr-summary-format.md +0 -0
  219. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/reference/recovery.md +0 -0
  220. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/reference/session-summary-format.md +0 -0
  221. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/reference/tracking-issues.md +0 -0
  222. {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/review-pr-comments.md +0 -0
  223. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/__init__.py +0 -0
  224. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/concurrency/__init__.py +0 -0
  225. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/concurrency/test_batch_multi_pr.py +0 -0
  226. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/concurrency/test_knowledge_merge.py +0 -0
  227. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/concurrency/test_lock_retry.py +0 -0
  228. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/concurrency/test_stash_race.py +0 -0
  229. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/conftest.py +0 -0
  230. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/__init__.py +0 -0
  231. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/_support.py +0 -0
  232. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/conftest.py +0 -0
  233. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/mock_gh_script.py +0 -0
  234. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/test_admin_contract.py +0 -0
  235. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/test_check_contract.py +0 -0
  236. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/test_implement_work_done_contract.py +0 -0
  237. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/test_knowledge_contract.py +0 -0
  238. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/test_mock_gh_contract.py +0 -0
  239. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/test_plan_contract.py +0 -0
  240. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/test_review_contract.py +0 -0
  241. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/test_smart_start_contract.py +0 -0
  242. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/test_task_contract.py +0 -0
  243. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/test_work_sync_list_contract.py +0 -0
  244. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/fixtures/live/taskr_header_hi_body.md +0 -0
  245. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/fixtures/live/taskr_header_howdy_body.md +0 -0
  246. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/fixtures/transcripts/claude_session.txt +0 -0
  247. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/fixtures/transcripts/codex_session.txt +0 -0
  248. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/fixtures/transcripts/copilot_session.txt +0 -0
  249. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/fixtures/transcripts/generic_session.txt +0 -0
  250. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/integration/__init__.py +0 -0
  251. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/integration/test_autostash.py +0 -0
  252. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/integration/test_check.py +0 -0
  253. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/integration/test_dynamic_skill_sessions.py +0 -0
  254. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/integration/test_git.py +0 -0
  255. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/integration/test_implementation_lifecycle.py +0 -0
  256. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/integration/test_init.py +0 -0
  257. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/integration/test_knowledge.py +0 -0
  258. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/integration/test_markdown_provider.py +0 -0
  259. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/integration/test_skill_context_budget.py +0 -0
  260. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/integration/test_skill_install.py +0 -0
  261. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/live/__init__.py +0 -0
  262. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/live/test_wade_live_ai.py +0 -0
  263. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/live/test_wade_live_ai_taskr.py +0 -0
  264. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/live/test_wade_live_gh.py +0 -0
  265. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/test_cli_basics.py +0 -0
  266. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/__init__.py +0 -0
  267. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/conftest.py +0 -0
  268. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_cli/__init__.py +0 -0
  269. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_cli/test_done_doc_pass_advisory.py +0 -0
  270. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_cli/test_done_review_hint.py +0 -0
  271. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_cli/test_knowledge_cli.py +0 -0
  272. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_cli/test_review_cli.py +0 -0
  273. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_cli/test_session_subapps.py +0 -0
  274. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_cli/test_shell_init.py +0 -0
  275. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_cli/test_skills_session_cli.py +0 -0
  276. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_cli/test_task_cli.py +0 -0
  277. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_cli/test_work_ai_flags.py +0 -0
  278. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_cli/test_work_cli.py +0 -0
  279. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_config/__init__.py +0 -0
  280. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_config/test_bot_review_config.py +0 -0
  281. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_config/test_dynamic_skill_config.py +0 -0
  282. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_config/test_loader.py +0 -0
  283. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_config/test_merge_strategy_migration.py +0 -0
  284. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_config/test_migrations.py +0 -0
  285. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_config/test_migrations_atomic.py +0 -0
  286. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_console.py +0 -0
  287. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_effort.py +0 -0
  288. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/__init__.py +0 -0
  289. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_all_patches_present.py +0 -0
  290. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_branch_list.py +0 -0
  291. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_branch_slugify.py +0 -0
  292. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_detached_worktree.py +0 -0
  293. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_is_merged_into.py +0 -0
  294. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_main_checkout_root.py +0 -0
  295. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_merge_count.py +0 -0
  296. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_pr_base.py +0 -0
  297. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_pr_lookup.py +0 -0
  298. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_pr_retry.py +0 -0
  299. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_pr_title.py +0 -0
  300. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_repo.py +0 -0
  301. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_reset_branch.py +0 -0
  302. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_scaffold_commit.py +0 -0
  303. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_stash_drop.py +0 -0
  304. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_sync_conflicted_files.py +0 -0
  305. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_tip_commit_is_empty.py +0 -0
  306. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_worktree_remove.py +0 -0
  307. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_hooks/__init__.py +0 -0
  308. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_hooks/test_hook_cli.py +0 -0
  309. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_hooks/test_policies.py +0 -0
  310. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_hooks/test_post_tool_use.py +0 -0
  311. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_hooks/test_session_start.py +0 -0
  312. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_models/__init__.py +0 -0
  313. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_models/test_batch.py +0 -0
  314. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_models/test_config_models.py +0 -0
  315. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_models/test_delegation.py +0 -0
  316. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_models/test_deps_models.py +0 -0
  317. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_models/test_deps_partition.py +0 -0
  318. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_models/test_review.py +0 -0
  319. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_models/test_review_expectation.py +0 -0
  320. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_models/test_task_models.py +0 -0
  321. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_models/test_workflow_models.py +0 -0
  322. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_permission_mode.py +0 -0
  323. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_providers/__init__.py +0 -0
  324. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_providers/test_clickup.py +0 -0
  325. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_providers/test_github.py +0 -0
  326. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_providers/test_github_pr.py +0 -0
  327. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_providers/test_markdown.py +0 -0
  328. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/__init__.py +0 -0
  329. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_ai_resolution.py +0 -0
  330. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_batch_build_cmd.py +0 -0
  331. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_batch_pr_index.py +0 -0
  332. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_batch_review_service.py +0 -0
  333. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_bootstrap_allowlist.py +0 -0
  334. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_bootstrap_guard_matcher.py +0 -0
  335. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_bootstrap_hook_timeout.py +0 -0
  336. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_bootstrap_post_tool_use.py +0 -0
  337. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_bootstrap_pre_push.py +0 -0
  338. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_bootstrap_session_start.py +0 -0
  339. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_bot_trigger.py +0 -0
  340. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_catchup.py +0 -0
  341. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_catchup_reconcile.py +0 -0
  342. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_check.py +0 -0
  343. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_cleanup_loss_guard.py +0 -0
  344. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_codex_worktree_launch_context.py +0 -0
  345. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_delegation_service.py +0 -0
  346. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_deps_service.py +0 -0
  347. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_documentation_receipt.py +0 -0
  348. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_done_gates.py +0 -0
  349. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_done_idempotent.py +0 -0
  350. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_done_plan_flag.py +0 -0
  351. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_done_pr_title_sync.py +0 -0
  352. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_draft_pr_base_fetch.py +0 -0
  353. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_draft_pr_retarget.py +0 -0
  354. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_implementation_done_sync.py +0 -0
  355. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_implementation_service.py +0 -0
  356. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_implementation_service_resume.py +0 -0
  357. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_init.py +0 -0
  358. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_init_shell_integration.py +0 -0
  359. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_knowledge_recovery.py +0 -0
  360. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_knowledge_search.py +0 -0
  361. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_knowledge_service.py +0 -0
  362. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_managed_git_hooks.py +0 -0
  363. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_no_direct_git.py +0 -0
  364. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_poll_for_reviews.py +0 -0
  365. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_post_implementation_lifecycle.py +0 -0
  366. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_pr_lookup_guards.py +0 -0
  367. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_pr_summary_path.py +0 -0
  368. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_pre_push_backstop.py +0 -0
  369. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_preserve_session_data.py +0 -0
  370. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_prompt_delivery.py +0 -0
  371. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_push_recovery.py +0 -0
  372. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_resolve_task_branch.py +0 -0
  373. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_resume_autonomy.py +0 -0
  374. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_review_delegation_service.py +0 -0
  375. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_review_receipts.py +0 -0
  376. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_review_records.py +0 -0
  377. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_review_service.py +0 -0
  378. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_review_status_block.py +0 -0
  379. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_skill_diagnostics_service.py +0 -0
  380. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_smart_start.py +0 -0
  381. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_smart_start_resume.py +0 -0
  382. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_stale_base_surface.py +0 -0
  383. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_sync_base_branch.py +0 -0
  384. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_task_service.py +0 -0
  385. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_tracked_managed_files.py +0 -0
  386. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_worktree_gitignore.py +0 -0
  387. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_skills/test_crossby_contract.py +0 -0
  388. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_skills/test_doc_targets.py +0 -0
  389. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_skills/test_dynamic_skills.py +0 -0
  390. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_skills/test_generic_builtin_boundaries.py +0 -0
  391. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_skills/test_knowledge_attributes.py +0 -0
  392. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_skills/test_managed_gitignore.py +0 -0
  393. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_skills/test_pointer.py +0 -0
  394. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_skills/test_review_budget_partial.py +0 -0
  395. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/__init__.py +0 -0
  396. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_body_markers.py +0 -0
  397. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_conventional.py +0 -0
  398. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_filelock.py +0 -0
  399. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_gitref.py +0 -0
  400. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_http.py +0 -0
  401. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_install.py +0 -0
  402. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_markdown.py +0 -0
  403. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_markers.py +0 -0
  404. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_plan_validation.py +0 -0
  405. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_process.py +0 -0
  406. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_prompts.py +0 -0
  407. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_slug.py +0 -0
  408. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_stale_base.py +0 -0
  409. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_terminal.py +0 -0
  410. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_update_check.py +0 -0
  411. {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_yolo.py +0 -0
@@ -4,6 +4,12 @@ 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
+ ## [v0.53.1] — 2026-08-31
8
+
9
+ ### Bug Fixes
10
+
11
+ - relaunch Codex with implementation network policy after plan handoff (#473) (202018b)
12
+
7
13
  ## [v0.53.0] — 2026-08-31
8
14
 
9
15
  ### Features
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: wade-cli
3
- Version: 0.53.0
3
+ Version: 0.53.1
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
@@ -730,6 +730,15 @@ flag enables it there. Precedence for the commands that honor it is
730
730
  enable network for a WADE-managed sandbox. Enabling network never disables the
731
731
  sandbox and never changes approval-policy semantics.
732
732
 
733
+ An accepted single-issue plan handoff starts implementation from a planning
734
+ session that is always network-off. When both the planning tool and resolved
735
+ implementation tool are Codex and the implementation policy resolves to enabled,
736
+ WADE starts a fresh detached Codex context with network explicitly enabled;
737
+ reusing the planner process would retain its immutable network-off sandbox. If
738
+ WADE cannot open that detached context, the handoff fails rather than reporting
739
+ an implementation session ready. Open a new host terminal and run the displayed
740
+ `wade implement <issue> --network` command to continue with the required policy.
741
+
733
742
  ### Session readiness and least-privilege remediation
734
743
 
735
744
  Every `wade` command an agent runs is a child of the AI tool. It inherits that
@@ -694,6 +694,15 @@ flag enables it there. Precedence for the commands that honor it is
694
694
  enable network for a WADE-managed sandbox. Enabling network never disables the
695
695
  sandbox and never changes approval-policy semantics.
696
696
 
697
+ An accepted single-issue plan handoff starts implementation from a planning
698
+ session that is always network-off. When both the planning tool and resolved
699
+ implementation tool are Codex and the implementation policy resolves to enabled,
700
+ WADE starts a fresh detached Codex context with network explicitly enabled;
701
+ reusing the planner process would retain its immutable network-off sandbox. If
702
+ WADE cannot open that detached context, the handoff fails rather than reporting
703
+ an implementation session ready. Open a new host terminal and run the displayed
704
+ `wade implement <issue> --network` command to continue with the required policy.
705
+
697
706
  ### Session readiness and least-privilege remediation
698
707
 
699
708
  Every `wade` command an agent runs is a child of the AI tool. It inherits that
@@ -499,6 +499,17 @@ picks (implement / batch / review). `None` (unset) still re-resolves per the
499
499
  routed command's own config, matching how a non-explicit `permission_mode` is
500
500
  threaded.
501
501
 
502
+ Planning itself is always network-off, including when it later offers an
503
+ accepted single-issue implementation handoff. The internal `plan_handoff`
504
+ intent keeps implementation-network resolution at `implementation_service.start`.
505
+ If that handoff originates in Codex, resolves the implementation tool to Codex,
506
+ and resolves network access to enabled, implementation must create a fresh
507
+ detached Codex launch with an explicit `network_access=True` pin. It cannot reuse
508
+ the planner's immutable network-off sandbox. If the detached launch cannot be
509
+ created, the handoff fails closed and prints `wade implement <issue> --network`
510
+ for the user to run from a new host terminal; it must not fall back to an inline
511
+ or nested agent. All other nested-agent starts retain the ordinary launch guard.
512
+
502
513
  ### Phase-aware session readiness (#462)
503
514
 
504
515
  `check_service.check_session_readiness()` applies the declarative
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "wade-cli"
7
- version = "0.53.0"
7
+ version = "0.53.1"
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__ = "0.53.0"
3
+ __version__ = "0.53.1"
@@ -434,6 +434,7 @@ def start(
434
434
  work_skills: list[str] | None = None,
435
435
  review_skills: list[str] | None = None,
436
436
  refresh_skills: bool = False,
437
+ plan_handoff: bool = False,
437
438
  ) -> ImplementResult:
438
439
  """Start an implementation session on an issue.
439
440
 
@@ -453,6 +454,10 @@ def start(
453
454
  project_root: Repository root (defaults to CWD).
454
455
  detach: If True, launch AI in a new terminal tab.
455
456
  cd_only: If True, create worktree and print path only (no AI launch).
457
+ plan_handoff: Internal marker for an accepted ``wade plan`` handoff.
458
+ A network-enabled Codex implementation must launch in a fresh
459
+ detached context rather than inherit the planner's network-off
460
+ sandbox.
456
461
 
457
462
  Returns:
458
463
  ImplementResult with success/merged status.
@@ -931,9 +936,19 @@ def start(
931
936
  return ImplementResult(success=True)
932
937
 
933
938
  # AI-initiated start guard: if we're inside an AI CLI session,
934
- # don't launch another AI tool — just print the worktree path.
939
+ # don't launch another AI tool — just print the worktree path. The
940
+ # one exception is a plan handoff from Codex to a network-enabled Codex
941
+ # implementation: network is a launch-time sandbox property, and the
942
+ # planner always ran with it disabled, so that handoff needs a fresh
943
+ # detached process instead of reusing this agent context.
935
944
  detected_env = _detect_ai_cli_env()
936
- if detected_env:
945
+ requires_fresh_codex_handoff = (
946
+ plan_handoff
947
+ and detected_env == "CODEX_CLI"
948
+ and resolved_tool == AIToolID.CODEX.value
949
+ and resolved_network_access
950
+ )
951
+ if detected_env and not requires_fresh_codex_handoff:
937
952
  logger.info(
938
953
  "implementation.ai_launch_skipped",
939
954
  reason="inside_ai_cli",
@@ -960,8 +975,10 @@ def start(
960
975
  except OSError:
961
976
  logger.warning("implementation.transcript_dir_failed")
962
977
 
963
- # Detach mode: launch AI tool in a new terminal, don't block
964
- if detach and resolved_tool:
978
+ # Detach mode: launch AI tool in a new terminal, don't block. A
979
+ # network-enabled Codex plan handoff is forced through this path so it
980
+ # cannot inherit the planner's immutable network-off sandbox.
981
+ if (detach or requires_fresh_codex_handoff) and resolved_tool:
965
982
  cmd: list[str] | None = None
966
983
  try:
967
984
  detach_adapter = AbstractAITool.get(AIToolID(resolved_tool))
@@ -995,18 +1012,40 @@ def start(
995
1012
  effort=resolved_effort,
996
1013
  allowed_commands=config.permissions.allowed_commands,
997
1014
  working_dir=worktree_path,
998
- network_access=resolved_network_access,
1015
+ # ``requires_fresh_codex_handoff`` already established
1016
+ # the resolved value is true; pin it explicitly for the
1017
+ # new Codex process rather than relying on ambient config.
1018
+ network_access=(
1019
+ True if requires_fresh_codex_handoff else resolved_network_access
1020
+ ),
999
1021
  **permission_mode_launch_kwargs(resolved_permission_mode),
1000
1022
  )
1001
1023
  except (ValueError, KeyError):
1002
- cmd = [resolved_tool]
1003
-
1004
- console.step(f"Launching {resolved_tool} in new terminal...")
1005
- assert cmd is not None # guaranteed by the two branches above
1006
- if launch_in_new_terminal(cmd, cwd=str(worktree_path), title=work_title):
1007
- console.success(f"Detached AI session for #{task.id}")
1024
+ # A fresh Codex handoff must retain its explicit sandbox,
1025
+ # network, permission, and prompt arguments. Do not replace a
1026
+ # failed build with bare ``codex``, which would bypass them.
1027
+ if not requires_fresh_codex_handoff:
1028
+ cmd = [resolved_tool]
1029
+
1030
+ if cmd is not None:
1031
+ console.step(f"Launching {resolved_tool} in new terminal...")
1032
+ if launch_in_new_terminal(
1033
+ cmd,
1034
+ cwd=str(worktree_path),
1035
+ title=work_title,
1036
+ wait_for_ready=requires_fresh_codex_handoff,
1037
+ ):
1038
+ console.success(f"Detached AI session for #{task.id}")
1039
+ stop_title_keeper()
1040
+ return ImplementResult(success=True)
1041
+ if requires_fresh_codex_handoff:
1042
+ console.error(
1043
+ "Could not launch a fresh Codex implementation session with network access."
1044
+ )
1045
+ console.hint("Open a new host terminal, then run:")
1046
+ console.detail(f"wade implement {task.id} --network")
1008
1047
  stop_title_keeper()
1009
- return ImplementResult(success=True)
1048
+ return ImplementResult(success=False)
1010
1049
  console.warn("Could not launch in new terminal — falling back to inline")
1011
1050
  detach = False
1012
1051
  # Fall through to inline launch below
@@ -1539,7 +1539,10 @@ def _offer_to_implement(
1539
1539
  return False
1540
1540
 
1541
1541
  try:
1542
- result = start_implementation_session(target=issue_number)
1542
+ # Planning always launches with network disabled. Tell implementation
1543
+ # startup this is an accepted handoff so it can establish a fresh Codex
1544
+ # context when its independently resolved policy needs network access.
1545
+ result = start_implementation_session(target=issue_number, plan_handoff=True)
1543
1546
  return result.success
1544
1547
  except Exception:
1545
1548
  logger.exception("plan.work_session_start_failed", issue=issue_number)
@@ -11,6 +11,7 @@ import sys
11
11
  import threading
12
12
  import time
13
13
  from collections.abc import Sequence
14
+ from pathlib import Path
14
15
 
15
16
  import structlog
16
17
 
@@ -20,6 +21,9 @@ _title_keeper_thread: threading.Thread | None = None
20
21
  _title_keeper_running = False
21
22
 
22
23
  _TERMINAL_TITLE_MAX_LEN = 50
24
+ _LAUNCH_READY_TIMEOUT_SECONDS = 5.0
25
+ _LAUNCH_READY_POLL_SECONDS = 0.05
26
+ _LAUNCH_STABILITY_SECONDS = 0.5
23
27
 
24
28
 
25
29
  def _truncate_terminal_title(text: str) -> str:
@@ -126,6 +130,74 @@ def _create_temp_script(command: list[str], cwd: str | None = None) -> str:
126
130
  return tmp_path
127
131
 
128
132
 
133
+ def _create_readiness_file() -> str:
134
+ """Create an empty, private file used for a detached-launch acknowledgement."""
135
+ import tempfile
136
+
137
+ fd, path = tempfile.mkstemp(prefix="wade-launch-", suffix=".ready")
138
+ os.close(fd)
139
+ return path
140
+
141
+
142
+ def _create_readiness_script(
143
+ command: list[str],
144
+ cwd: str | None,
145
+ readiness_file: str,
146
+ ) -> str:
147
+ """Create a launcher which acknowledges only after its child stays alive.
148
+
149
+ Terminal brokers can confirm that they accepted a tab/window request but
150
+ cannot observe a command executed inside it. This wrapper reports
151
+ ``ready`` only after the command has remained alive briefly, which catches
152
+ an unavailable executable and launch-time failures before the caller
153
+ reports a successful handoff.
154
+ """
155
+ import tempfile
156
+
157
+ cmd_str = " ".join(shlex.quote(str(c)) for c in command)
158
+ quoted_cwd = shlex.quote(cwd or ".")
159
+ quoted_readiness_file = shlex.quote(readiness_file)
160
+ with tempfile.NamedTemporaryFile(prefix="wade-", suffix=".sh", delete=False, mode="w") as f:
161
+ tmp_path = f.name
162
+ f.write(
163
+ "#!/usr/bin/env bash\n"
164
+ f"cd {quoted_cwd} || {{ printf 'failed\\n' > {quoted_readiness_file}; exit 1; }}\n"
165
+ f"{cmd_str} &\n"
166
+ "child_pid=$!\n"
167
+ f"sleep {_LAUNCH_STABILITY_SECONDS}\n"
168
+ 'if kill -0 "$child_pid" 2>/dev/null; then\n'
169
+ f" printf 'ready\\n' > {quoted_readiness_file}\n"
170
+ "else\n"
171
+ ' wait "$child_pid"\n'
172
+ f" printf 'failed\\n' > {quoted_readiness_file}\n"
173
+ " exit 1\n"
174
+ "fi\n"
175
+ 'wait "$child_pid"\n'
176
+ )
177
+ os.chmod(tmp_path, 0o700)
178
+ return tmp_path
179
+
180
+
181
+ def _wait_for_launch_readiness(
182
+ readiness_file: str,
183
+ timeout: float = _LAUNCH_READY_TIMEOUT_SECONDS,
184
+ ) -> bool:
185
+ """Wait for the detached launch wrapper to acknowledge a live child."""
186
+ deadline = time.monotonic() + timeout
187
+ path = Path(readiness_file)
188
+ while time.monotonic() < deadline:
189
+ try:
190
+ status = path.read_text()
191
+ except OSError:
192
+ return False
193
+ if status == "ready\n":
194
+ return True
195
+ if status == "failed\n":
196
+ return False
197
+ time.sleep(_LAUNCH_READY_POLL_SECONDS)
198
+ return False
199
+
200
+
129
201
  def detect_terminal() -> str | None:
130
202
  """Detect the current terminal emulator.
131
203
 
@@ -154,12 +226,36 @@ def launch_in_new_terminal(
154
226
  command: list[str],
155
227
  cwd: str | None = None,
156
228
  title: str | None = None,
229
+ *,
230
+ wait_for_ready: bool = False,
157
231
  ) -> bool:
158
232
  """Launch a command in a new terminal window/tab.
159
233
 
160
234
  Tries Ghostty, iTerm2, Terminal.app, tmux in order.
161
- Returns True if launched successfully, False otherwise.
235
+ Returns True if launched successfully, False otherwise. When
236
+ ``wait_for_ready`` is set, require a brief child-process acknowledgement
237
+ rather than treating terminal-broker acceptance as success.
162
238
  """
239
+ if not wait_for_ready:
240
+ return _launch_in_new_terminal(command, cwd=cwd, title=title)
241
+
242
+ readiness_file = _create_readiness_file()
243
+ launcher_script = _create_readiness_script(command, cwd, readiness_file)
244
+ try:
245
+ if not _launch_in_new_terminal([launcher_script], title=title):
246
+ return False
247
+ return _wait_for_launch_readiness(readiness_file)
248
+ finally:
249
+ _safe_unlink(launcher_script)
250
+ _safe_unlink(readiness_file)
251
+
252
+
253
+ def _launch_in_new_terminal(
254
+ command: list[str],
255
+ cwd: str | None = None,
256
+ title: str | None = None,
257
+ ) -> bool:
258
+ """Launch a command in a new terminal window/tab without confirmation."""
163
259
  terminal = detect_terminal()
164
260
 
165
261
  if terminal == "ghostty":
@@ -22,7 +22,7 @@ from unittest.mock import MagicMock, patch
22
22
  import pytest
23
23
 
24
24
  from wade.git.pr import PRLookup, PRRef
25
- from wade.models.config import AIConfig, ProjectConfig, ProjectSettings
25
+ from wade.models.config import AICommandConfig, AIConfig, ProjectConfig, ProjectSettings
26
26
  from wade.models.permission import PermissionMode
27
27
  from wade.models.task import Task, TaskState
28
28
  from wade.models.worktree import Worktree
@@ -32,7 +32,15 @@ _CORE = "wade.services.implementation_service.core"
32
32
 
33
33
 
34
34
  @contextlib.contextmanager
35
- def _driven_start(worktree_path: Path, *, network_access_config: bool) -> Iterator[MagicMock]:
35
+ def _driven_start(
36
+ worktree_path: Path,
37
+ *,
38
+ network_access_config: bool,
39
+ implement_network_access_config: bool | None = None,
40
+ detected_env: str | None = None,
41
+ resolved_tool: str = "codex",
42
+ terminal_launch_succeeds: bool = True,
43
+ ) -> Iterator[MagicMock]:
36
44
  """Patch impl start()'s prerequisites and yield the spy AI adapter.
37
45
 
38
46
  Every heavy step between ``start()`` entry and the AI launch is stubbed so the
@@ -40,7 +48,10 @@ def _driven_start(worktree_path: Path, *, network_access_config: bool) -> Iterat
40
48
  worktree at *worktree_path*, and ``network_access`` resolved from config.
41
49
  """
42
50
  config = ProjectConfig(
43
- ai=AIConfig(network_access=network_access_config),
51
+ ai=AIConfig(
52
+ network_access=network_access_config,
53
+ implement=AICommandConfig(network_access=implement_network_access_config),
54
+ ),
44
55
  project=ProjectSettings(main_branch="main", branch_prefix="feat"),
45
56
  )
46
57
  task = Task(id="42", title="Fix the widget", body="broken", state=TaskState.OPEN)
@@ -80,23 +91,26 @@ def _driven_start(worktree_path: Path, *, network_access_config: bool) -> Iterat
80
91
  p(patch(f"{_CORE}.bootstrap_worktree"))
81
92
  p(patch(f"{_CORE}._catchup_and_surface_staleness", return_value=None))
82
93
  p(patch(f"{_CORE}.build_implementation_prompt", return_value="PROMPT"))
83
- p(patch(f"{_CORE}.resolve_ai_tool", return_value="codex"))
94
+ p(patch(f"{_CORE}.resolve_ai_tool", return_value=resolved_tool))
84
95
  p(patch(f"{_CORE}.resolve_model", return_value=None))
85
96
  p(patch(f"{_CORE}.resolve_effort", return_value=None))
86
97
  p(
87
98
  patch(
88
99
  f"{_CORE}.confirm_ai_selection",
89
- return_value=("codex", None, None, PermissionMode.DEFAULT),
100
+ return_value=(resolved_tool, None, None, PermissionMode.DEFAULT),
90
101
  )
91
102
  )
92
- p(patch(f"{_CORE}._detect_ai_cli_env", return_value=None))
103
+ p(patch(f"{_CORE}._detect_ai_cli_env", return_value=detected_env))
93
104
  p(patch(f"{_CORE}.set_terminal_title"))
94
105
  p(patch(f"{_CORE}.start_title_keeper"))
95
106
  p(patch(f"{_CORE}.stop_title_keeper"))
96
107
  p(patch(f"{_CORE}.deliver_prompt_if_needed"))
97
- p(patch(f"{_CORE}.launch_in_new_terminal", return_value=True))
108
+ terminal_launch = p(
109
+ patch(f"{_CORE}.launch_in_new_terminal", return_value=terminal_launch_succeeds)
110
+ )
98
111
  p(patch("crossby.ai_tools.AbstractAITool.get", return_value=spy))
99
112
  with stack:
113
+ spy.terminal_launch = terminal_launch
100
114
  yield spy
101
115
 
102
116
 
@@ -164,3 +178,130 @@ class TestImplementationLaunchContext:
164
178
  kwargs = spy.build_resume_command.call_args.kwargs
165
179
  assert kwargs["working_dir"] == worktree
166
180
  assert kwargs["network_access"] is True
181
+
182
+ @pytest.mark.parametrize(
183
+ ("global_network_access", "implement_network_access"),
184
+ [(True, None), (False, True)],
185
+ ids=["global-policy", "implement-policy"],
186
+ )
187
+ def test_network_enabled_codex_plan_handoff_launches_fresh_detached_context(
188
+ self,
189
+ worktree: Path,
190
+ global_network_access: bool,
191
+ implement_network_access: bool | None,
192
+ ) -> None:
193
+ """A Codex plan handoff cannot reuse the planner's network-off sandbox."""
194
+ with _driven_start(
195
+ worktree,
196
+ network_access_config=global_network_access,
197
+ implement_network_access_config=implement_network_access,
198
+ detected_env="CODEX_CLI",
199
+ ) as spy:
200
+ spy.build_launch_command.return_value = ["codex"]
201
+
202
+ result = start(target="42", plan_handoff=True)
203
+
204
+ assert result.success is True
205
+ assert spy.build_launch_command.call_args.kwargs["working_dir"] == worktree
206
+ assert spy.build_launch_command.call_args.kwargs["network_access"] is True
207
+ assert spy.terminal_launch.call_args.kwargs["wait_for_ready"] is True
208
+ spy.launch.assert_not_called()
209
+
210
+ def test_network_disabled_codex_plan_handoff_keeps_nested_launch_guard(
211
+ self, worktree: Path
212
+ ) -> None:
213
+ """A false policy must not gain network access merely to leave planning."""
214
+ with _driven_start(
215
+ worktree,
216
+ network_access_config=False,
217
+ detected_env="CODEX_CLI",
218
+ ) as spy:
219
+ result = start(target="42", plan_handoff=True)
220
+
221
+ assert result.success is True
222
+ spy.build_launch_command.assert_not_called()
223
+ spy.launch.assert_not_called()
224
+
225
+ def test_failed_fresh_codex_plan_handoff_fails_closed_with_restart_command(
226
+ self, worktree: Path
227
+ ) -> None:
228
+ """The handoff must never fall through to an inline planner context."""
229
+ with (
230
+ _driven_start(
231
+ worktree,
232
+ network_access_config=True,
233
+ detected_env="CODEX_CLI",
234
+ terminal_launch_succeeds=False,
235
+ ) as spy,
236
+ patch(f"{_CORE}.console") as mock_console,
237
+ ):
238
+ spy.build_launch_command.return_value = ["codex"]
239
+
240
+ result = start(target="42", plan_handoff=True)
241
+
242
+ assert result.success is False
243
+ spy.launch.assert_not_called()
244
+ mock_console.detail.assert_any_call("wade implement 42 --network")
245
+
246
+ @pytest.mark.parametrize("command_error", [ValueError, KeyError])
247
+ def test_fresh_codex_plan_handoff_fails_closed_when_command_build_fails(
248
+ self, worktree: Path, command_error: type[Exception]
249
+ ) -> None:
250
+ """A builder failure must not degrade to an unconfigured ``codex`` command."""
251
+ with (
252
+ _driven_start(
253
+ worktree,
254
+ network_access_config=True,
255
+ detected_env="CODEX_CLI",
256
+ ) as spy,
257
+ patch(f"{_CORE}.console") as mock_console,
258
+ patch(f"{_CORE}.launch_in_new_terminal") as mock_launch,
259
+ ):
260
+ spy.build_launch_command.side_effect = command_error("build failed")
261
+
262
+ result = start(target="42", plan_handoff=True)
263
+
264
+ assert result.success is False
265
+ mock_launch.assert_not_called()
266
+ spy.launch.assert_not_called()
267
+ mock_console.detail.assert_any_call("wade implement 42 --network")
268
+
269
+ def test_non_handoff_codex_session_keeps_nested_launch_guard(self, worktree: Path) -> None:
270
+ """Ordinary ``wade implement`` calls still do not recursively launch Codex."""
271
+ with _driven_start(
272
+ worktree,
273
+ network_access_config=True,
274
+ detected_env="CODEX_CLI",
275
+ ) as spy:
276
+ result = start(target="42")
277
+
278
+ assert result.success is True
279
+ spy.build_launch_command.assert_not_called()
280
+ spy.launch.assert_not_called()
281
+
282
+ def test_non_codex_plan_handoff_keeps_nested_launch_guard(self, worktree: Path) -> None:
283
+ """A plan handoff to another implementation tool cannot escape the guard."""
284
+ with _driven_start(
285
+ worktree,
286
+ network_access_config=True,
287
+ detected_env="CODEX_CLI",
288
+ resolved_tool="claude",
289
+ ) as spy:
290
+ result = start(target="42", plan_handoff=True)
291
+
292
+ assert result.success is True
293
+ spy.build_launch_command.assert_not_called()
294
+ spy.launch.assert_not_called()
295
+
296
+ def test_other_ai_session_marker_keeps_nested_launch_guard(self, worktree: Path) -> None:
297
+ """Only a Codex-originated handoff gets the fresh-context exception."""
298
+ with _driven_start(
299
+ worktree,
300
+ network_access_config=True,
301
+ detected_env="CLAUDE_CODE",
302
+ ) as spy:
303
+ result = start(target="42", plan_handoff=True)
304
+
305
+ assert result.success is True
306
+ spy.build_launch_command.assert_not_called()
307
+ spy.launch.assert_not_called()
@@ -1170,7 +1170,7 @@ class TestOfferToImplement:
1170
1170
  result = _offer_to_implement("42")
1171
1171
 
1172
1172
  assert result is True
1173
- mock_start.assert_called_once_with(target="42")
1173
+ mock_start.assert_called_once_with(target="42", plan_handoff=True)
1174
1174
 
1175
1175
  def test_user_declines_returns_none(self) -> None:
1176
1176
  """Declining the prompt returns None without flushing or starting."""
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import subprocess
4
+ from pathlib import Path
4
5
  from unittest.mock import Mock
5
6
 
6
7
  import pytest
@@ -272,6 +273,45 @@ def test_create_temp_script_content(monkeypatch: pytest.MonkeyPatch) -> None:
272
273
  assert "exec" in script
273
274
 
274
275
 
276
+ def test_readiness_script_reports_failed_when_child_exits_immediately(tmp_path: Path) -> None:
277
+ """A broker success is rejected when its child cannot stay alive."""
278
+ from wade.utils.terminal import _create_readiness_script
279
+
280
+ readiness_file = tmp_path / "launch.ready"
281
+ readiness_file.touch()
282
+ launcher_script = _create_readiness_script(
283
+ ["bash", "-c", "exit 1"],
284
+ str(tmp_path),
285
+ str(readiness_file),
286
+ )
287
+ try:
288
+ completed = subprocess.run([launcher_script], capture_output=True, check=False)
289
+ finally:
290
+ Path(launcher_script).unlink(missing_ok=True)
291
+
292
+ assert completed.returncode == 1
293
+ assert readiness_file.read_text() == "failed\n"
294
+
295
+
296
+ def test_readiness_script_reports_ready_for_a_live_child(tmp_path: Path) -> None:
297
+ """A child that survives the stability window confirms the handoff."""
298
+ from wade.utils.terminal import _create_readiness_script, _wait_for_launch_readiness
299
+
300
+ readiness_file = tmp_path / "launch.ready"
301
+ readiness_file.touch()
302
+ launcher_script = _create_readiness_script(
303
+ ["bash", "-c", "sleep 1"],
304
+ str(tmp_path),
305
+ str(readiness_file),
306
+ )
307
+ process = subprocess.Popen([launcher_script])
308
+ try:
309
+ assert _wait_for_launch_readiness(str(readiness_file), timeout=2.0) is True
310
+ assert process.wait(timeout=2.0) == 0
311
+ finally:
312
+ Path(launcher_script).unlink(missing_ok=True)
313
+
314
+
275
315
  # ---------------------------------------------------------------------------
276
316
  # Batch launcher tests
277
317
  # ---------------------------------------------------------------------------
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes