Draft stops being a review-turn precondition and becomes its in-progress signal (like π on issue turns): re-assigning the bot to a ready (LGTM'd) PR must fire a re-review turn; the workflow converts the PR to draft while the turn runs; on failure the handoff restores the trigger-time state.
Context: this task comes from ask #98. Implement after #96 merges (same files). Coordinate with #97: whichever lands second rebases; do not block on it.
Changes:
.github/workflows/claude.review.yml
- Drop
github.event.pull_request.draft from the job if:; four guards remain (assignee==bot, PR author==bot, head claude/task-*, same-repo+open). Update the "All five guards" comment.
- Add a
Draft step (after Checkout, before Secrets β the signal must not depend on Infisical): step-level if: ${{ !github.event.pull_request.draft }}, runs gh pr ready --undo "$PR" with GH_TOKEN: ${{ github.token }} (job already has pull-requests: write; GraphQL convertPullRequestToDraft works under it). The step if: makes the already-draft first-review case a clean skip rather than relying on CLI idempotency.
- Pass trigger-time draft state to the handoff:
draft: ${{ github.event.pull_request.draft }}.
- No trigger loops: draftβready conversions emit
converted_to_draft/ready_for_review; this workflow listens only to assigned (unchanged).
actions/claude.handoff/action.yml
- New optional input
draft (default ''): "the PR's trigger-time draft state as a string ('true'/'false'); empty for issue turns".
- New step
Restore, if: inputs.status != 'success' && inputs.draft == 'false': gh pr ready "$ISSUE" with github.token. Mirrors Unack: the in-progress signal is removed on success (the agent flips ready on LGTM) and failure alike; a PR that was draft going in stays draft. Issue-turn callers (plan/implement) pass nothing β step never runs.
- Update the action
description: to mention the draft-restore.
actions/claude.review/prompt.md β moot check (step 2): moot is now merged or closed only. Drop "already marked ready" β the workflow converts to draft at kickoff, and re-reviews of previously-LGTM'd PRs are legitimate. Add one line of re-review context: prior reviews may include an LGTM; a re-review walks the acceptance criteria against the new head as usual.
Verdict semantics are unchanged: LGTM β reviewer flips ready; blocking findings β stays draft (draft is the only merge guard β the bot cannot REQUEST_CHANGES on its own PR).
Acceptance criteria:
- Guard has exactly four clauses; assigning the bot to a ready open bot-authored same-repo
claude/task-* PR starts the job (verify with act or a dry event-payload eval).
- The Draft step converts a ready PR before the agent step and is skipped when the PR was already draft.
- Handoff: non-success +
draft == 'false' β PR restored to ready; non-success + draft == 'true' β stays draft; success β step never runs. Both prior states exercised (a forced-failure run or act per repo CLAUDE.md).
- Issue-turn callers of claude.handoff are byte-for-byte unaffected in behavior (no
draft input passed).
- Review prompt's moot definition = merged/closed only.
Draft stops being a review-turn precondition and becomes its in-progress signal (like π on issue turns): re-assigning the bot to a ready (LGTM'd) PR must fire a re-review turn; the workflow converts the PR to draft while the turn runs; on failure the handoff restores the trigger-time state.
Context: this task comes from ask #98. Implement after #96 merges (same files). Coordinate with #97: whichever lands second rebases; do not block on it.
Changes:
.github/workflows/claude.review.ymlgithub.event.pull_request.draftfrom the jobif:; four guards remain (assignee==bot, PR author==bot, headclaude/task-*, same-repo+open). Update the "All five guards" comment.Draftstep (after Checkout, before Secrets β the signal must not depend on Infisical): step-levelif: ${{ !github.event.pull_request.draft }}, runsgh pr ready --undo "$PR"withGH_TOKEN: ${{ github.token }}(job already haspull-requests: write; GraphQL convertPullRequestToDraft works under it). The stepif:makes the already-draft first-review case a clean skip rather than relying on CLI idempotency.draft: ${{ github.event.pull_request.draft }}.converted_to_draft/ready_for_review; this workflow listens only toassigned(unchanged).actions/claude.handoff/action.ymldraft(default''): "the PR's trigger-time draft state as a string ('true'/'false'); empty for issue turns".Restore,if: inputs.status != 'success' && inputs.draft == 'false':gh pr ready "$ISSUE"withgithub.token. Mirrors Unack: the in-progress signal is removed on success (the agent flips ready on LGTM) and failure alike; a PR that was draft going in stays draft. Issue-turn callers (plan/implement) pass nothing β step never runs.description:to mention the draft-restore.actions/claude.review/prompt.mdβ moot check (step 2): moot is now merged or closed only. Drop "already marked ready" β the workflow converts to draft at kickoff, and re-reviews of previously-LGTM'd PRs are legitimate. Add one line of re-review context: prior reviews may include an LGTM; a re-review walks the acceptance criteria against the new head as usual.Verdict semantics are unchanged: LGTM β reviewer flips ready; blocking findings β stays draft (draft is the only merge guard β the bot cannot REQUEST_CHANGES on its own PR).
Acceptance criteria:
claude/task-*PR starts the job (verify withactor a dry event-payload eval).draft == 'false'β PR restored to ready; non-success +draft == 'true'β stays draft; success β step never runs. Both prior states exercised (a forced-failure run oractper repo CLAUDE.md).draftinput passed).