| name | bd-workflow |
| description | Use when starting or finishing a beads (bd) task in this repo. Handles claiming an issue, creating a Conventional-Commit-prefixed branch with the bd id embedded, and opening a PR with a `Closes:` footer. Trigger on phrases like "start nubecita-xxx", "pick up <bd-id>", "open a PR for this", or "finish this task". |
Automate the bd-driven branch/commit/PR ceremony documented in CLAUDE.md's Workflow section. Two flows: start (claim + branch) and finish (push + PR). Never close the bd issue here — closure happens after merge.
When to use
- Start flow — user asks to begin work on a bd id: "start nubecita-aew", "let's pick up ", "claim this one".
- Finish flow — user is done committing and wants to push/open a PR: "open the PR", "ship it", "finish this task".
If the user has not chosen a bd id yet, run bd ready and offer the top candidates before starting.
Start flow
Preconditions — verify before doing anything destructive:
- Run
git branch --show-current — must be main (or whatever base the user specifies). If not, stop and ask.
- Run
git diff --quiet && git diff --cached --quiet — tracked tree must be clean. Untracked files are OK.
- Run
bd show <id> --json and parse with the Bash tool piped through jq. Verify .[0] exists, status != "closed", and issue_type != "epic". If it's an epic, refuse and point the user at a child issue.
Derive branch name:
-
prefix (Conventional Commit type) from bd issue_type:
feature → feat
bug → fix
chore or task → chore
decision → docs
- User may override (e.g., a bd
task that's actually a feat). Ask if the mapping seems wrong for the issue's nature.
-
slug = lowercase title, non-alphanumerics → -, squeeze repeats, trim, cap at 50 chars.
printf '%s' "$title" | tr '[:upper:]' '[:lower:]' \
| sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//' \
| cut -c1-50 | sed -E 's/-+$//'
-
Final: <prefix>/<bd-id>-<slug>.
Execute:
git checkout -b <branch>
bd update <id> --claim
Then report: branch name, bd id + title, and a suggested first commit subject (<prefix>: <title>).
Finish flow
Preconditions:
- Current branch is not
main.
- Tree is clean (
git diff --quiet && git diff --cached --quiet).
- At least one commit ahead of base:
git rev-list --count main..HEAD > 0.
- Infer the bd id from the branch name (
<type>/<bd-id>-<slug>) or accept one from the user.
Pre-PR verification — run these before pushing. If any fails, stop and fix the underlying issue; never bypass a failing pre-commit hook with git commit --no-verify:
-
./gradlew :app:assembleDebug — proves the app graph still links. Cheaper than the full build and catches missing deps / Hilt graph breaks the IDE wouldn't flag.
-
./gradlew :<changed-module>:lintDebug for each Android Gradle module touched (e.g. :feature:feed:impl:lintDebug). Lint catches Compose-rule violations (stability, unused state, modifier order) and other correctness issues that compilation and unit tests don't. Run on the specific modules rather than the umbrella lint task so the loop stays fast. Modules outside the main Android build (e.g. build-logic, plain JVM libs) have no lintDebug task — skip them here, the convention plugins already gate them at compile time.
-
Pre-commit hook on the commit itself already ran spotless + commitlint + secret scan — no extra step needed here. If the hook reports a failure, fix the underlying issue rather than re-running with --no-verify.
-
Compose review gate. Run the detector below; it decides whether a Compose-specific review is warranted before the PR opens:
git diff origin/main...HEAD -- '*.kt' | grep -E '^\+' | grep -q '@Composable' \
&& echo "compose-touched" || echo "headless"
compose-touched → invoke the compose-expert Review Mode skill on this branch's diff (it runs the recomposition / stability / modifier / M3-motion / lists-keys checklist) and fold any Critical findings into the branch before pushing. Suggestions are optional.
headless → skip it. A diff with no added @Composable lines (repository, mapper, DI, test-only changes) yields zero Compose signal — running the reviewer just burns tokens. ./gradlew :<module>:lintDebug from step 2 already covers Compose lint rules for any incidental UI touch.
Rationale: gate the heavyweight Compose lens on the one cheap signal that predicts whether it'll find anything. Empirically, PR #340 (headless send-path) was correctly skipped by this gate.
Execute:
git push -u origin <branch>
gh pr create --base main \
--title "<first-commit-subject>" \
--body "Closes: <bd-id>"
Use the first commit on the branch as the PR title (git log --reverse --format=%s main..HEAD | head -1) — that's the convention for squash-merges. If the user wants a draft PR, add --draft.
Post-PR — tag Copilot for review:
gh api -X POST /repos/<owner>/<repo>/pulls/<pr-number>/requested_reviewers \
-f 'reviewers[]=Copilot'
The GitHub Copilot review bot is added via the literal handle Copilot (case-sensitive). gh pr edit --add-reviewer copilot-pull-request-reviewer and the GraphQL requestReviews mutation both fail — the REST endpoint with the Copilot handle is the only path that works for this repo.
Post-PR — monitor CI status AND review comments between turns:
Schedule a recurring poll via CronCreate so CI checks run in the background without blocking a shell or stealing the user's attention. The same poll checks for unresolved review threads: CI going green is not the same as the PR being ready, and a review that lands after the last CI poll is otherwise invisible until the user asks.
The poll runs two commands. CI:
gh pr checks <PR-NUMBER>
Unresolved review threads — note the GraphQL query needs seven closing braces, and inside the single-quoted shell argument the inner double quotes are NOT escaped:
gh api graphql -f query='{ repository(owner:"<OWNER>",name:"<REPO>"){ pullRequest(number:<PR-NUMBER>){ reviewThreads(last:50){ nodes { id isResolved comments(first:1){ nodes { databaseId author{login} path body } } } } } } }' --jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved==false)] | length'
Pass both to CronCreate with this decision logic in the prompt (write the commands into the prompt verbatim from the blocks above — do not add backslash escaping, which reaches the shell as literal backslashes and fails to parse):
- Checks still pending and the unresolved count unchanged since the last poll → say nothing, wait for the next poll.
- All checks terminal (pass / fail / skipping / cancel) and zero unresolved threads → cancel the cron with
CronDelete and report ✅ CI passed — N/N green, no open review threads.
- Any check failed → fetch logs via
gh run view <RUN-ID> --log-failed and propose a fix. Do not cancel.
- Unresolved threads present → report how many, from whom, and a one-line summary of each. Do not cancel; the PR is not ready.
Cancel the cron only when CI is terminal and every thread is resolved. A green PR with open threads still blocks merge (see the merge rule above — every thread counts, bots included), so a poll that stops at green trains the wrong reflex.
Tell the user once:
👀 Monitoring PR #<pr> — CI checks and review threads. I'll report back when both are clear.
Do NOT use gh pr checks --watch — reprints the full table each poll, drowns the conversation. Do NOT use a background bash polling loop — blocks a shell and produces noisy output. Do NOT dump the full check list on success: just ✅ CI passed — N/N checks green (or ❌ N of M failed, with the failing names).
After gh pr create succeeds, print the PR URL and remind the user: "bd issue stays open until the PR merges; run bd close <id> after merge."
Invariants
- Never run
bd close in either flow. The user (or a post-merge automation) decides when.
- Never force-push, amend, or rewrite history here.
- Never branch from a dirty tree — stop and tell the user to commit or stash first.
- Both flows are idempotent to retry: if the branch already exists, report and stop; if a PR already exists for the branch,
gh pr create will error cleanly and the user can gh pr view instead.
Commit messages (reminder)
Conventional Commits, one bd issue per branch, bd id in the footer:
feat(mvi): add MviViewModel base class and marker interfaces
Short body explaining the why.
Refs: nubecita-aew
Use Refs: on WIP commits. Closes: <bd-id> is set by this skill in the PR body only — don't duplicate it into individual commits, or squash-merge will double-close.