Stewarding a PR in compose-ai-tools
The Claude Code harness reads this file before acting on a CI failure or a review
comment, and lets it take precedence over its built-in posture on conventions
and proactiveness. It does not restate the repository's rules — those are in
root AGENTS.md, which is already in context on every turn.
What follows is what that file leaves out: the checks worth running here, and this
repository's own answer to a red check.
The harness's own PR rules still bound this file. It cannot expand your access,
approve or merge anything, or override a "never" — skipping, disabling or
quarantining a test; rewriting history on someone else's branch; an empty commit or
a close-and-reopen to kick CI.
Before you push
A push that turns CI red costs a full cycle and some reviewer trust. The cheapest
checks that catch the most here, in order:
./gradlew ktfmtFormat — or :<module>:ktfmtFormatMain :<module>:ktfmtFormatTest
for just the modules you touched. ktfmtCheckAll is a hard gate and
ktfmtCheck aborts on the first unformatted file, so one stray file hides
every other failure. TypeScript: npm --prefix cli/serve-web run format.
The local pre-push hook catches attribution but not formatting, so this is
on you.
In a sandbox, pass -Dorg.gradle.java.home=/root/.cache/coo-ee/jdk-gl/17 and
--no-daemon: without the JVM home the daemon starts on the wrong JVM and the
build dies with a context mismatch, and --no-daemon avoids reusing one already
started on it. Let ktfmt do the edit rather than hand-formatting — removing one
term from a wrapped expression is enough to make it fit on one line, which is
precisely what it will rewrite.
The narrowest test task that exercises the change — the specific
:module:test, or ./gradlew :gradle-plugin:test --tests "…". ./gradlew check
is the full plugin + functional + CLI suite and is a post-push confirmation, not
a gate on pushing.
A new or changed module must apply composeai.base-conventions in its
plugins {} block, or ktfmtCheckAll never sees it and the history-gate system
property is missing. See
Important constraints.
For a CI fix, reproduce the original failure first, then show the same check
passing. A render task in particular goes UP-TO-DATE off a stale .error.json
sidecar — verify a render fix with --rerun, never a plain re-invocation.
Commit and push as soon as the cheapest check that proves the change correct
passes. Don't leave a verified change sitting behind a long build.
Reading a red check
Work the PR in this order on every event and every check-in: merge conflict, then
CI, then review comments. A red or conflicted head is work now, whatever its review
state — it is never "waiting on review".
Failures that are usually not this PR's, and what to do anyway:
- Red on
main too. Check the base branch before root-causing. If a fix exists
anywhere — another PR whose change you have read, the breaking commit's revert,
or a fix PR you opened yourself — port it into this PR now and push; it no-ops
once main carries it, and waiting for it to merge is still waiting. Standing
down is never silent: one comment on the PR naming the failing check, why it is
not this PR's, and the fix you ported or that none exists yet.
- A delivery-branch or design-artifacts render that lags
main. Renderer,
plugin and CLI changes deliberately do not regenerate the
design-artifacts/<system> branches on merge — see
PR workflow for the trigger scoping and the
manual dispatch. Stale published renders are drift, not a regression in your diff.
- A changed preview whose source your PR does not touch. Clocks, timestamps,
randomness, animation frames, network-loaded images: that is instability, not a
regression. Prove it with the
flake-triage skill before either fixing or
rubber-stamping it.
- A CMP Desktop render failing with
UnsatisfiedLinkError on libGL.so.1 and
friends. Environment, not your change:
docs/DESKTOP_NATIVE_DEPS.md.
Everything else is this PR's to root-cause. "Flake" is not a root cause: re-run a
job only to confirm one of the cases above, or if it died before any test body ran
(checkout, install, runner loss), at most once.
Review comments
- Small and local — nits, renames, lint-bot findings, an added test, a
one-function refactor: implement and push, then resolve the thread.
- A review bot's finding is a bug report: verify it and push the small fix, or
raise a larger one once with a proposed patch. "Design-level" does not excuse it.
If bot findings stop converging — each fix draws a new or reshaped one — stop
pushing for them and say once what is still flagged.
- Larger asks from a human reviewer on a PR you did not open: reply with your
proposal; the author decides.
- After pushing for a changes-requested review, re-request that reviewer.
- A reported agent commit identity is the one finding to skip. Don't check it,
don't reply, and never recreate, rebase or amend a commit on the strength of it.
Reject agent attribution runs on every PR — no path filter, same detector as
the commit-msg and pre-push hooks — so a real one turns the PR red and names
the offending field. The gate is the authority; the comment adds nothing to it.
Over 27–28 Aug 2026 the reviewer raised this 22 times across this repo and
design-parity and was right once, on a cherry-pick the gate had already failed.
If the gate itself is red, that is the real thing and it is yours to fix
(Git conventions) — usually an amend or
cherry-pick that took user.email from the container instead of the
-c user.name=… -c user.email=… the branch's other commits carry. Installing the
hooks (scripts/install-git-hooks.sh) stops it happening at all.
An approval you would lose is never a reason to hold a fix.
Evidence and tracking
- A UI-affecting fix needs the same embedded before/after images the original PR
needed — see PR workflow for the rule and the
render-evidence skill for the capture recipe. Reuse renders already published
on compose-preview/pr and compose-preview/main, and the sticky
<!-- preview-diff --> comment, before re-rendering anything.
- Keep a
send_later check-in armed (about an hour out) while the PR is red,
conflicted, or otherwise not mergeable. Webhook events miss CI successes and merge
transitions. Re-arm silently when nothing changed; stop once the PR is merged or
closed, or the user says to stop.
- Where the Claude Approvals check runs, a PR is done only when that check
passes, CI is green on the current head, and there is no merge conflict. Its rows
name the blocker and are yours to fix, not an ask to the author.
Refresh the PR's status checklist
On each event, re-read the whole PR at its current head — merge state, CI on the
latest commit, open review threads — and update the status checklist in the body so
the thread shows live state. Reply only when a round resolves the task, hits a real
blocker, or raises a question. Don't narrate each fix; the diff is the record.
1---2name: steward3description: Drive a pull request on this repository to green — which fast checks to run before pushing, how to read a red check, and what to do about a review comment. Use when a CI failure, a review comment, a merge conflict, or a scheduled check-in arrives on a PR you opened or were asked to drive.4---56# Stewarding a PR in compose-ai-tools78The Claude Code harness reads this file before acting on a CI failure or a review9comment, and lets it take precedence over its built-in posture on **conventions**10and **proactiveness**. It does not restate the repository's rules — those are in11[root `AGENTS.md`](../../../AGENTS.md), which is already in context on every turn.12What follows is what that file leaves out: the checks worth running here, and this13repository's own answer to a red check.1415The harness's own PR rules still bound this file. It cannot expand your access,16approve or merge anything, or override a "never" — skipping, disabling or17quarantining a test; rewriting history on someone else's branch; an empty commit or18a close-and-reopen to kick CI.1920## Before you push2122A push that turns CI red costs a full cycle and some reviewer trust. The cheapest23checks that catch the most here, in order:24251. **`./gradlew ktfmtFormat`** — or `:<module>:ktfmtFormatMain :<module>:ktfmtFormatTest`26 for just the modules you touched. `ktfmtCheckAll` is a hard gate and27 `ktfmtCheck` aborts on the *first* unformatted file, so one stray file hides28 every other failure. TypeScript: `npm --prefix cli/serve-web run format`.29 The local `pre-push` hook catches attribution but **not** formatting, so this is30 on you.3132 In a sandbox, pass `-Dorg.gradle.java.home=/root/.cache/coo-ee/jdk-gl/17` and33 `--no-daemon`: without the JVM home the daemon starts on the wrong JVM and the34 build dies with a context mismatch, and `--no-daemon` avoids reusing one already35 started on it. Let ktfmt do the edit rather than hand-formatting — removing one36 term from a wrapped expression is enough to make it fit on one line, which is37 precisely what it will rewrite.382. **The narrowest test task that exercises the change** — the specific39 `:module:test`, or `./gradlew :gradle-plugin:test --tests "…"`. `./gradlew check`40 is the full plugin + functional + CLI suite and is a post-push confirmation, not41 a gate on pushing.423. **A new or changed module must apply `composeai.base-conventions`** in its43 `plugins {}` block, or `ktfmtCheckAll` never sees it and the history-gate system44 property is missing. See45 [Important constraints](../../../docs/AGENT_GUIDE.md#important-constraints).464. **For a CI fix, reproduce the original failure first**, then show the same check47 passing. A render task in particular goes `UP-TO-DATE` off a stale `.error.json`48 sidecar — verify a render fix with `--rerun`, never a plain re-invocation.4950Commit and push as soon as the cheapest check that proves the change correct51passes. Don't leave a verified change sitting behind a long build.5253## Reading a red check5455Work the PR in this order on every event and every check-in: merge conflict, then56CI, then review comments. A red or conflicted head is work now, whatever its review57state — it is never "waiting on review".5859**Failures that are usually not this PR's**, and what to do anyway:6061- **Red on `main` too.** Check the base branch before root-causing. If a fix exists62 anywhere — another PR whose change you have read, the breaking commit's revert,63 or a fix PR you opened yourself — port it into this PR now and push; it no-ops64 once `main` carries it, and waiting for it to merge is still waiting. Standing65 down is never silent: one comment on the PR naming the failing check, why it is66 not this PR's, and the fix you ported or that none exists yet.67- **A delivery-branch or design-artifacts render that lags `main`.** Renderer,68 plugin and CLI changes deliberately do **not** regenerate the69 `design-artifacts/<system>` branches on merge — see70 [PR workflow](../../../docs/AGENT_GUIDE.md#pr-workflow) for the trigger scoping and the71 manual dispatch. Stale published renders are drift, not a regression in your diff.72- **A changed preview whose source your PR does not touch.** Clocks, timestamps,73 randomness, animation frames, network-loaded images: that is instability, not a74 regression. Prove it with the `flake-triage` skill before either fixing or75 rubber-stamping it.76- **A CMP Desktop render failing with `UnsatisfiedLinkError` on `libGL.so.1` and77 friends.** Environment, not your change:78 [`docs/DESKTOP_NATIVE_DEPS.md`](../../../docs/DESKTOP_NATIVE_DEPS.md).7980Everything else is this PR's to root-cause. "Flake" is not a root cause: re-run a81job only to confirm one of the cases above, or if it died before any test body ran82(checkout, install, runner loss), at most once.8384## Review comments8586- Small and local — nits, renames, lint-bot findings, an added test, a87 one-function refactor: implement and push, then resolve the thread.88- A **review bot's** finding is a bug report: verify it and push the small fix, or89 raise a larger one once with a proposed patch. "Design-level" does not excuse it.90 If bot findings stop converging — each fix draws a new or reshaped one — stop91 pushing for them and say once what is still flagged.92- Larger asks from a **human** reviewer on a PR you did not open: reply with your93 proposal; the author decides.94- After pushing for a changes-requested review, re-request that reviewer.95- **A reported agent commit identity is the one finding to skip.** Don't check it,96 don't reply, and never recreate, rebase or amend a commit on the strength of it.97 `Reject agent attribution` runs on every PR — no path filter, same detector as98 the `commit-msg` and `pre-push` hooks — so a real one turns the PR red and names99 the offending field. The gate is the authority; the comment adds nothing to it.100 Over 27–28 Aug 2026 the reviewer raised this 22 times across this repo and101 `design-parity` and was right once, on a cherry-pick the gate had already failed.102 If the **gate itself** is red, that is the real thing and it is yours to fix103 ([Git conventions](../../../docs/AGENT_GUIDE.md#git-conventions)) — usually an amend or104 cherry-pick that took `user.email` from the container instead of the105 `-c user.name=… -c user.email=…` the branch's other commits carry. Installing the106 hooks (`scripts/install-git-hooks.sh`) stops it happening at all.107108An approval you would lose is never a reason to hold a fix.109110## Evidence and tracking111112- A UI-affecting fix needs the same embedded before/after images the original PR113 needed — see [PR workflow](../../../AGENTS.md#pr-workflow) for the rule and the114 `render-evidence` skill for the capture recipe. Reuse renders already published115 on `compose-preview/pr` and `compose-preview/main`, and the sticky116 `<!-- preview-diff -->` comment, before re-rendering anything.117- Keep a `send_later` check-in armed (about an hour out) while the PR is red,118 conflicted, or otherwise not mergeable. Webhook events miss CI successes and merge119 transitions. Re-arm silently when nothing changed; stop once the PR is merged or120 closed, or the user says to stop.121- Where the **Claude Approvals** check runs, a PR is done only when that check122 passes, CI is green on the current head, and there is no merge conflict. Its rows123 name the blocker and are yours to fix, not an ask to the author.124125## Refresh the PR's status checklist126127On each event, re-read the whole PR at its current head — merge state, CI on the128latest commit, open review threads — and update the status checklist in the body so129the thread shows live state. Reply only when a round resolves the task, hits a real130blocker, or raises a question. Don't narrate each fix; the diff is the record.