From 7aa08907fa3cb801adeabdfe9d74de43138ea7b0 Mon Sep 17 00:00:00 2001 From: mhenrixon Date: Thu, 9 Jul 2026 10:49:00 +0200 Subject: [PATCH] =?UTF-8?q?chore:=20review=20workflow=20supports=20merge?= =?UTF-8?q?=20conflicts=20=E2=80=94=20Phase=20A0=20before=20CI=20failures?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backport of mhenrixon/phlex-reactive#220 adapted to this repo: adds the github-review-pr / github-review-failures / github-review-comments command trio with a merge-conflict phase (A0) that runs before CI diagnosis. Adapted to locallingo's realities: single `bundle exec rake` CI job on a Ruby 3.2–3.4 matrix, releases bumping lib/locallingo/version.rb directly on main via rake release, docs/bun.lock as the only tracked lockfile, and no tracked generated artifacts. --- .claude/commands/github-review-comments.md | 237 +++++++++++++++++++++ .claude/commands/github-review-failures.md | 174 +++++++++++++++ .claude/commands/github-review-pr.md | 162 ++++++++++++++ 3 files changed, 573 insertions(+) create mode 100644 .claude/commands/github-review-comments.md create mode 100644 .claude/commands/github-review-failures.md create mode 100644 .claude/commands/github-review-pr.md diff --git a/.claude/commands/github-review-comments.md b/.claude/commands/github-review-comments.md new file mode 100644 index 0000000..f028a7d --- /dev/null +++ b/.claude/commands/github-review-comments.md @@ -0,0 +1,237 @@ +--- +model: sonnet +description: "Use when a PR has unresolved review comments that need responses -- evaluates each comment, implements valid fixes, pushes back on incorrect suggestions, and resolves all threads." +argument-hint: "PR number (e.g., 123 or #123)" +allowed-tools: Bash(gh pr list:*), Bash(gh pr view:*), Bash(gh pr diff:*), Bash(gh pr comment:*), Bash(gh api:*), Bash(git log:*), Bash(git blame:*), Bash(git push:*), Bash(git commit:*), Bash(git add:*), Bash(bundle exec:*), Read, Write, Edit, Glob, Grep, Agent +--- + +# Review GitHub PR Comments: $ARGUMENTS + +You are reviewing and responding to all unresolved review comments on a GitHub pull request. Apply technical rigour -- evaluate each comment against the actual codebase before accepting or rejecting it. + +## Phase 0: Determine the PR Number + +The user may provide a PR number as `$ARGUMENTS`. Parse it flexibly: + +- `PR123`, `PR 123`, `pr123` -> PR 123 +- `123` -> PR 123 +- `#123` -> PR 123 +- Empty/blank -> auto-detect from current branch + +**If no PR number is provided**, detect it automatically: + +```bash +gh pr list --author=@me --head="$(git branch --show-current)" --state=open --json number,title +``` + +If exactly one open PR exists for the current branch, use it. If none or multiple, ask the user. + +Once you have the PR number, confirm it: + +```bash +gh pr view --json title,state,url +``` + +--- + +## Phase 1: Fetch All Unresolved Review Comments + +Retrieve all review comments and identify unresolved ones: + +```bash +# Get all review comments (not resolved) +gh api "repos/mhenrixon/locallingo/pulls//comments" --paginate + +# Get all review threads to check resolution status +gh api graphql -f query=' + query($owner: String!, $repo: String!, $pr: Int!) { + repository(owner: $owner, name: $repo) { + pullRequest(number: $pr) { + reviewThreads(first: 100) { + nodes { + id + isResolved + path + line + comments(first: 20) { + nodes { + id + databaseId + body + author { login } + createdAt + } + } + } + } + } + } + } +' -f owner=mhenrixon -f repo=locallingo -F pr= +``` + +For each unresolved thread, extract: +- Thread ID (for resolving) +- Comment body (the review feedback) +- File path and line number (if inline) +- Author (to understand context) + +Filter to only **unresolved** threads. Skip bot comments (CodeRabbit, dependabot), resolved threads, and PR description comments. + +If there are no unresolved review comments, report that and stop. + +--- + +## Phase 2: Read and Categorise Each Comment + +For each unresolved comment, read the full body and categorise it: + +| Category | Action | +|----------|--------| +| Valid fix needed | Implement the fix | +| Valid test gap | Add the missing test | +| Valid style/consistency issue | Fix it | +| Incorrect suggestion | Push back with technical reasoning | +| Suggestion conflicts with architecture | Push back, reference existing patterns | +| Over-engineering / YAGNI | Push back, explain why it's unnecessary | +| Unclear | Ask for clarification (do NOT implement) | + +**Before categorising**, always: +1. Read the actual file and line being commented on +2. Check if the suggestion is technically correct for THIS codebase +3. Check if it would break existing functionality +4. Check if existing patterns/conventions contradict the suggestion +5. Check the repo's conventions (the README, existing code patterns, and `docs/AGENTS.md` for the docs app) -- project conventions override reviewer preferences. Remember the gem's floor is Ruby 3.2: reject suggestions that need 3.3+/3.4-only syntax. + +--- + +## Phase 3: Implement Accepted Fixes + +For all comments you've decided to accept: + +1. **Make the code changes** -- edit the relevant files +2. **Run affected tests** to verify nothing breaks: + ```bash + bundle exec rspec + ``` +3. **Run validators**: + ```bash + bundle exec rubocop + ``` +4. **Commit** all fixes together with a clear message: + ```bash + git commit -m "$(cat <<'EOF' + fix: address PR review feedback + + - Description of fix 1 + - Description of fix 2 + EOF + )" + ``` +5. **Push** to the remote branch: + ```bash + git push + ``` + +--- + +## Phase 4: Reply to Every Comment + +For **each** unresolved thread, reply: + +### For accepted fixes: + +Reply with what was fixed and the commit SHA: + +```bash +gh api "repos/mhenrixon/locallingo/pulls//comments//replies" \ + --method POST \ + -f 'body=Fixed in . .' +``` + +### For rejected suggestions: + +Reply with technical reasoning: + +```bash +gh api "repos/mhenrixon/locallingo/pulls//comments//replies" \ + --method POST \ + -f 'body=' +``` + +### Resolving threads (via GraphQL): + +After replying, resolve the thread: + +```bash +gh api graphql -f query=' + mutation($threadId: ID!) { + resolveReviewThread(input: {threadId: $threadId}) { + thread { isResolved } + } + } +' -f threadId= +``` + +### For general PR comments (not inline review threads): + +Reply directly: + +```bash +gh pr comment --body "" +``` + +--- + +## Phase 5: Verify Completion + +After processing all comments, verify no unresolved threads remain: + +```bash +gh api graphql -f query=' + query($owner: String!, $repo: String!, $pr: Int!) { + repository(owner: $owner, name: $repo) { + pullRequest(number: $pr) { + reviewThreads(first: 100) { + totalCount + nodes { isResolved } + } + } + } + } +' -f owner=mhenrixon -f repo=locallingo -F pr= +``` + +Report the final tally: how many comments were accepted/fixed, how many were pushed back on, and confirm all threads are resolved. + +--- + +## Response Style + +When replying to comments: + +- **No performative agreement** -- never say "Great point!" or "You're absolutely right!" +- **No gratitude** -- never say "Thanks for catching that" +- **Be direct** -- state the fix or the reasoning, nothing more +- **Reference commits** -- always include the short SHA when a fix was made +- **Be specific** -- when pushing back, reference actual code, not abstract principles + +When pushing back: + +- Use technical reasoning grounded in the actual codebase +- Reference existing patterns if the suggestion contradicts them +- Reference the repo's documented conventions when applicable +- Explain what would break or what edge case the reviewer missed +- If the suggestion is valid in principle but wrong for this context, say so + +--- + +## Important Notes + +- Always read the actual code before evaluating a comment -- reviewers sometimes misread diffs +- If a comment reveals a genuine bug you missed, fix it without defensiveness +- If multiple comments suggest the same change, implement it once and reference the fix in all replies +- Bot reviewers (CodeRabbit, etc.) sometimes suggest changes that conflict with project conventions -- verify against the repo's actual patterns before accepting +- If a new round of review comments appears after your push (from re-review), report that to the user rather than entering an infinite loop + +Now begin by determining the PR number from `$ARGUMENTS` or the current branch. diff --git a/.claude/commands/github-review-failures.md b/.claude/commands/github-review-failures.md new file mode 100644 index 0000000..20bde1f --- /dev/null +++ b/.claude/commands/github-review-failures.md @@ -0,0 +1,174 @@ +--- +model: sonnet +description: "Use when CI checks are failing on a PR — fetches failure logs, diagnoses root causes, implements fixes, and pushes until CI is green." +argument-hint: "PR number (e.g., 41 or #41)" +allowed-tools: Bash(gh pr list:*), Bash(gh pr view:*), Bash(gh pr checks:*), Bash(gh pr diff:*), Bash(gh api:*), Bash(gh run view:*), Bash(git log:*), Bash(git diff:*), Bash(git push:*), Bash(git commit:*), Bash(git add:*), Bash(bundle exec:*), Read, Write, Edit, Glob, Grep, Agent +--- + +# Fix GitHub CI Failures: $ARGUMENTS + +You are diagnosing and fixing CI failures on a GitHub pull request. Work systematically: identify failures, read logs, diagnose root causes, fix locally, verify, push. + +## Phase 0: Determine the PR Number + +The user may provide a PR number as `$ARGUMENTS`. Parse it flexibly: + +- `PR41`, `PR 41`, `pr41` -> PR 41 +- `41` -> PR 41 +- `#41` -> PR 41 +- Empty/blank -> auto-detect from current branch + +**If no PR number is provided**, detect it automatically: + +```bash +gh pr list --author=@me --head="$(git branch --show-current)" --state=open --json number,title +``` + +If exactly one open PR exists for the current branch, use it. If none or multiple, ask the user. + +Once you have the PR number, confirm it: + +```bash +gh pr view --json title,state,url,mergeable +``` + +**Pre-flight: merge conflicts (detection only).** If `mergeable` is `CONFLICTING`, STOP — do not diagnose CI on a conflicted branch (the merge itself may fix or cause the failures). Report the conflict and hand off to `/github-review-pr`, whose Phase A0 owns the resolution runbook — this command's toolset deliberately does not include the merge machinery. If `mergeable` is `UNKNOWN`, note it and proceed: the orchestrator resolves the ambiguity; a standalone run shouldn't block on GitHub's recompute. + +--- + +## Phase 1: Identify Failing Checks + +```bash +gh pr checks +``` + +CI is a single job type — `bundle exec rake` (spec + rubocop, per the Rakefile) — run on a Ruby matrix: + +| Check Type | Examples | How to Get Logs | +|------------|----------|----------------| +| Suite + lint (rake = spec + rubocop) | `rake (Ruby 3.2)`, `rake (Ruby 3.3)`, `rake (Ruby 3.4)` | `gh run view --job= --log-failed` | + +Extract the run ID and job IDs from the check URLs. The URL format is: +`https://github.com/mhenrixon/locallingo/actions/runs//job/` + +If all checks pass or are pending, report that and stop. + +--- + +## Phase 2: Fetch Failure Logs + +For each failing check, get the logs: + +```bash +# Get the failed job logs (condensed output) +gh run view --job= --log-failed +``` + +If `--log-failed` output is too large or unclear, try: + +```bash +# Full log for a specific job +gh run view --job= --log 2>&1 | tail -100 +``` + +--- + +## Phase 3: Diagnose Each Failure + +For each failure, determine the root cause. A `rake (Ruby X.Y)` job fails on the FIRST of specs or rubocop that breaks — read the log to see which half failed. + +### Lint Failures + +Look for: +- RuboCop offenses: file path, line number, cop name, message + +**Key**: RuboCop failures can often be auto-fixed with `bundle exec rubocop -A `. The lint scope is `exe lib spec Rakefile Gemfile locallingo.gemspec` (Rakefile patterns); the `docs/` app has its own separate `.rubocop.yml` and is NOT covered by this job. + +### Spec Failures + +Look for: +- Test name and file path +- Error class and message +- Relevant backtrace lines (ignore framework noise) +- Whether it's a test environment issue vs actual code bug + +**Key patterns**: +- `NameError: uninitialized constant` -> missing require or renamed class +- `NoMethodError: undefined method` -> API change, missing method +- `Errno::ENOENT` in specs -> fixture/tmpdir path issue (the suite writes locale fixtures under `Dir.mktmpdir`) +- `expected: X, got: Y` -> logic bug or test needs updating + +### Build/Dependency Failures + +Look for: +- Bundle install failures in the `Set up Ruby` step (bundler-cache): dependency conflicts, a gem that dropped support for an older matrix Ruby + +--- + +## Phase 4: Fix Locally + +For each diagnosed failure: + +1. **Read the relevant file** to understand context before fixing +2. **Make the fix** -- edit the file +3. **Verify locally** before committing: + +```bash +# For rubocop failures +bundle exec rubocop + +# For spec failures +bundle exec rspec + +# For full validation (exactly what CI runs) +bundle exec rake +``` + +### Fix Priority Order + +1. **Lint/style fixes** first (fast, deterministic) +2. **Spec failures** second (may require understanding the code change) +3. **Build/dependency issues** third (usually Gemfile or gemspec) + +--- + +## Phase 5: Commit and Push + +```bash +git add +git commit -m "$(cat <<'EOF' +fix(ci): + +- Fix 1 description +- Fix 2 description +EOF +)" +git push +``` + +--- + +## Phase 6: Verify + +After pushing, check if CI has been re-triggered: + +```bash +gh pr checks +``` + +If there are still pending checks, report which checks are running and what was fixed. Do NOT poll in a loop -- report the status and let the user know. + +If you can identify that certain failures will persist for environmental reasons (e.g., a runner outage or a RubyGems network hiccup during bundle install), flag that explicitly. + +--- + +## Important Notes + +- **Read before fixing** -- always read the actual failing code before attempting a fix +- **Fix the root cause** -- don't add `# rubocop:disable` to bypass lint; fix the actual issue (a targeted `# rubocop:disable` is acceptable only when RuboCop is demonstrably wrong) +- **Don't fix unrelated failures** -- if a spec was already failing on main, note it but don't fix it in this PR +- **The Ruby matrix is 3.2 / 3.3 / 3.4** -- a failure on only ONE Ruby version is a version-specific bug, not flakiness. The gem's floor is Ruby 3.2 (`required_ruby_version`, `TargetRubyVersion: 3.2`), so a fix must not use 3.3+/3.4-only syntax; check the failing version's log for `SyntaxError` first. +- **Flaky tests** -- if a test passes locally but fails in CI, note it as potentially flaky rather than adding workarounds +- **Don't retry CI blindly** -- diagnose first, fix, then push. Each push triggers a full CI run across all three Rubies. + +Now begin by determining the PR number and fetching the failing checks. diff --git a/.claude/commands/github-review-pr.md b/.claude/commands/github-review-pr.md new file mode 100644 index 0000000..914c5f2 --- /dev/null +++ b/.claude/commands/github-review-pr.md @@ -0,0 +1,162 @@ +--- +model: opus +description: "Use when a PR needs full review — resolves merge conflicts with the base first, then fixes CI failures, then addresses unresolved review comments. Conflicts first so CI diagnoses the post-merge reality; failures before comments because comment fixes trigger new CI runs that obscure the original failures." +argument-hint: "PR number (e.g., 42 or #42)" +allowed-tools: Bash(gh pr list:*), Bash(gh pr view:*), Bash(gh pr checks:*), Bash(gh pr checkout:*), Bash(gh pr diff:*), Bash(gh pr comment:*), Bash(gh api:*), Bash(gh run view:*), Bash(git log:*), Bash(git blame:*), Bash(git diff:*), Bash(git status:*), Bash(git switch:*), Bash(git fetch:*), Bash(git merge:*), Bash(git merge-tree:*), Bash(git rev-parse:*), Bash(git push:*), Bash(git commit:*), Bash(git add:*), Bash(bundle exec:*), Bash(bun install:*), Read, Write, Edit, Glob, Grep, Agent +--- + +# Review GitHub PR (full pass): $ARGUMENTS + +You are running a full review pass on a pull request. The pass has three phases that MUST run in this order: + +1. **Phase A0: merge conflicts** — bring the branch up to date with its base and resolve any conflicts before anything else. +2. **Phase A: CI failures** — fix anything red before touching review comments. +3. **Phase B: review comments** — only after Phase A leaves CI green (or pending green after a push). + +## Why this order matters + +**Conflicts before failures**: CI results only matter for the code that will actually merge. On a conflicted (or stale) branch you'd diagnose failures against a base that no longer exists — and the conflict resolution itself changes code, invalidating the run you just fixed. Resolving conflicts first means Phase A reads CI for the post-merge reality, and you spend exactly one extra CI cycle instead of two. + +**Failures before comments**: if you fix review comments first, every commit pushes a new CI run. By the time the review-comment fixes finish, the original failure logs are buried under new pipeline runs. Symptoms: + +- The `rake (Ruby 3.2)` log you needed to read is now from a stale run; the latest run is still in progress on top of your unrelated comment fixes. +- A review-comment fix accidentally repairs the CI failure as a side effect, and you lose the chance to verify the failure was real. +- A review-comment fix accidentally INTRODUCES a CI failure, and you can't tell whether the new failure was pre-existing or your fault. + +Conflicts-first, then failures-first eliminates this confusion. CI is either green or red on a known commit against the current base; the review-comment fixes layer cleanly on top. + +## Phase 0: Determine the PR Number + +The user may provide a PR number as `$ARGUMENTS`. Parse it flexibly: + +- `PR42`, `PR 42`, `pr42` → PR 42 +- `42` → PR 42 +- `#42` → PR 42 +- Empty/blank → auto-detect from current branch + +**If no PR number is provided**, detect it automatically: + +```bash +gh pr list --author=@me --head="$(git branch --show-current)" --state=open --json number,title +``` + +If exactly one open PR exists for the current branch, use it. If none or multiple, ask the user. + +Once you have the PR number, confirm it: + +```bash +gh pr view --json title,state,url +``` + +--- + +## Phase A0: Merge conflicts + +Check whether the branch merges cleanly into its base: + +```bash +gh pr view --json mergeable,mergeStateStatus,baseRefName +``` + +| `mergeable` | Action | +|-------------|--------| +| `MERGEABLE` | Skip to Phase A. | +| `UNKNOWN` | GitHub is recomputing (common right after pushes, and it can stay UNKNOWN for minutes). Don't poll it — verify **locally**, against the PR's actual head (NOT `HEAD`, which may be some other checked-out branch): `git fetch origin ` and `git fetch origin pull//head`, verify both refs resolve (`git rev-parse --verify origin/^{commit}` and `git rev-parse --verify FETCH_HEAD^{commit}` — a bad ref also exits 1 from merge-tree, so exit code alone can't be trusted), then `git merge-tree --write-tree --name-only origin/ FETCH_HEAD`. Clean exit → no conflicts, skip to Phase A. Exit 1 **with conflict output** → resolve below (the `--name-only` file list is your work list). | +| `CONFLICTING` | Resolve, below. | + +### Resolution procedure + +1. Check out the PR's branch (`gh pr checkout `) with a clean tree (`git status`). Stash nothing — if the tree is dirty, stop and ask the user. +2. `git fetch origin ` then **`git merge origin/`** — MERGE, never rebase. The branch is shared (it has a PR); a rebase would require a force-push to a shared branch, which is never acceptable. +3. Resolve every conflicted file **semantically** — read both sides and produce the version that preserves BOTH changes' intent. Never blanket `--ours`/`--theirs` a source file. Repo-specific rules: + - **`CHANGELOG.md` (Unreleased)**: union — keep BOTH sides' entries (main's landed bullets and this branch's), most recent first, without duplicating the `### Added`/`### Fixed`/`### Changed` subheads (Keep a Changelog format). Losing either side is a real regression reviewers rarely catch. + - **`lib/locallingo/version.rb`**: releases land DIRECTLY on `main` via `rake release[X.Y.Z]` (the Rakefile bumps, commits, pushes `main`, and creates the GitHub Release — no PR), so an ordinary feature branch never edits this file — a conflict here means the BRANCH bumped it on purpose (a release-prep PR). Keep the branch's bump in that case; if the intent isn't obvious from the branch's own commits, stop and ask. Only take the base's version when the branch's edit was clearly accidental. + - **`docs/bun.lock`** (the only tracked lockfile — the gem root's `Gemfile.lock` is gitignored and can never conflict, and `docs/Gemfile.lock` isn't committed): take the base's file, then run `bun install --cwd docs` so the branch's own dependency changes, if any (in `docs/package.json`), re-resolve on top. Never hand-edit a lockfile. + - **Shipped default config** (`config/default.yml`, `config/locallingo.default.yml`): both sides usually added/changed different keys — merge to keep both, and confirm the result is valid YAML the gem can load. + - This repo tracks **no generated artifacts** (the docs app's compiled Tailwind CSS under `docs/app/assets/builds/` is gitignored) — every other conflicted file is source; merge it semantically. +4. Run the verification gates BEFORE pushing the merge — scoped to what the conflict touched, at minimum: + ```bash + bundle exec rubocop + bundle exec rspec + # or exactly what CI runs (spec + rubocop in one): + bundle exec rake + ``` + The `docs/` app is NOT exercised by PR CI (its deploy workflow fires on release), so a `docs/` conflict has no CI safety net — review the semantic merge extra carefully there. +5. Commit the merge (keep git's standard merge-commit message; add a body line naming any non-obvious resolution choice) and `git push` — a merge commit never needs force. + +### Phase A0 exit criteria + +- The PR reports `MERGEABLE` (or the local `git merge-tree` check is clean), AND the merge commit (if one was needed) is pushed. +- If the merge produced changes, CI is now re-running — that's expected; Phase A reads the fresh run. +- If a conflict cannot be resolved with confidence (both sides rewrote the same logic and the correct combination isn't decidable from the code), **stop and ask the user** — a guessed resolution that compiles is worse than a question. + +--- + +## Phase A: Run `/github-review-failures` + +Invoke the existing `/github-review-failures` slash command with the same `$ARGUMENTS` value. Its purpose: fix every failing CI check, push, leave the branch in a state where CI is either green or running-pending-toward-green. + +Follow that command's full process — phases 1–6 of the failures runbook. The slash command is at `.claude/commands/github-review-failures.md`. Its workflow: + +1. Identify failing checks via `gh pr checks `. +2. Fetch failure logs. +3. Diagnose root cause for each. +4. Fix locally — lint first (fast, deterministic), then specs, then build issues. +5. Verify locally before commit (`bundle exec rspec `, `bundle exec rubocop`). +6. Commit + push + report which checks are now running. + +### Phase A exit criteria + +Before moving to Phase B, one of these must be true: + +- All CI checks are green on the latest pushed commit. OR +- All CI checks are pending (running) on the latest pushed commit, AND no checks failed in the most recent completed run on this commit. OR +- A persistent CI failure exists that is **not caused by changes on this branch** (e.g., a flaky test on `main`, or an environmental failure in the runner). Report this explicitly and proceed to Phase B with the caveat noted. + +If failures persist on this branch's changes, **do NOT proceed to Phase B**. Report what's still failing, what's been tried, and ask the user how to proceed. + +--- + +## Phase B: Run `/github-review-comments` + +Once Phase A's exit criteria are met, invoke `/github-review-comments` with the same `$ARGUMENTS`. Its purpose: address every unresolved review thread on the PR, push fixes, reply with commit SHAs, and resolve the threads. + +The slash command is at `.claude/commands/github-review-comments.md`. Its workflow: + +1. Fetch all unresolved review threads via the GitHub GraphQL API. +2. Read and categorise each comment (valid fix / invalid suggestion / unclear). +3. Implement accepted fixes; verify locally (specs, rubocop). +4. Commit all fixes together with a clear message; push. +5. Reply to every thread with the commit SHA (for accepted fixes) or technical reasoning (for rejections). +6. Resolve each thread via the GraphQL `resolveReviewThread` mutation. +7. Verify no unresolved threads remain. + +### Phase B exit criteria + +- All unresolved review threads have been replied to and resolved (or the user has explicitly approved leaving a specific thread open). +- The branch has been pushed with all accepted fixes. + +--- + +## Phase C: Final report + +Before reporting, re-check mergeability once more (`gh pr view --json mergeable`, or the local `git merge-tree` check if UNKNOWN) — the base can move underneath a long pass. If a NEW conflict appeared, loop back to Phase A0. + +After all phases complete, report: + +1. **Phase A0 summary**: whether the branch was conflicted, which files conflicted, how each was resolved (and the merge commit SHA) — or "clean merge, no action". +2. **Phase A summary**: which CI failures were diagnosed and fixed. Note the commit SHAs for the fixes. +3. **Phase B summary**: which review comments were accepted (with commit SHAs), which were pushed back on (with reasoning), and the final unresolved-thread count (should be 0). +4. **End state**: final mergeability + CI status on the latest commit. +5. **Outstanding work**: anything that still needs attention — e.g., CI was pending at the end of Phase B and the user should verify the latest run after the comment fixes. + +--- + +## Important Notes + +- **Do not interleave the phases.** Don't fix a CI failure, then a review comment, then another CI failure. The whole point of this command is the strict ordering. +- **A new CI failure emerging during Phase B** (e.g., a comment fix breaks a spec) means looping back to Phase A — fix the new failure before continuing comment work. Likewise, **a new conflict appearing mid-pass** (the base moved) means looping back to Phase A0. These loop-backs are the only allowed reverse directions. +- **If the PR is already merged**, there is nothing to review — report that and stop. (A stale `$ARGUMENTS` or a just-merged PR shows up as `state: MERGED` in Phase 0's confirm step.) +- **If the PR merges cleanly, has no failures AND no unresolved comments**, report "PR is clean" and stop. +- **If `$ARGUMENTS` is the same as the current open PR**, the two child slash commands will see the same PR. They share state through the git branch and the GitHub API, not through any in-process variable. +- **Don't re-implement the child slash commands' logic**. Invoke them and let them do their work. This command is the orchestrator.