Skip to content

Commit 69be2db

Browse files
committed
Merge remote-tracking branch 'origin/main' into bohdan/relay-loki-init
2 parents e98c2e4 + c174edf commit 69be2db

26 files changed

Lines changed: 3769 additions & 157 deletions

.claude/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,8 @@
2424
]
2525
}
2626
]
27+
},
28+
"enabledPlugins": {
29+
"ralph-loop@claude-plugins-official": true
2730
}
2831
}
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
---
2+
name: loop-review-pr
3+
description: >
4+
Iteratively review and fix a Pluto PR until it is "ideal" — drives the
5+
/review-pr multi-agent pipeline inside /ralph-loop, applying fixes between
6+
iterations and never posting inline comments. After the loop terminates,
7+
posts a single summary comment to the GitHub PR with everything that was
8+
resolved. Invoke as `/loop-review-pr <PR-number|PR-URL>
9+
[--max-iterations N]`.
10+
---
11+
12+
# Loop Review PR
13+
14+
Orchestrate a self-improving review-and-fix loop for a Pluto PR. The
15+
[[review-pr]] skill is run repeatedly inside [[ralph-loop]]; each iteration
16+
the findings are addressed in code, then the next iteration re-reviews. **No
17+
inline comments are posted to GitHub during the loop.** After completion (or
18+
hitting the iteration cap) post one summary comment.
19+
20+
## 0. Inputs and constants
21+
22+
```text
23+
REPO = NethermindEth/pluto
24+
PR = <number from arg, or extracted from URL>
25+
MAX_ITERATIONS = <--max-iterations N | default 15>
26+
STATE_DIR = .claude/loop-review-state
27+
STATE = $STATE_DIR/pr-$PR.md
28+
COMPLETION_PROMISE = "PR_IDEAL"
29+
```
30+
31+
If the user passed a URL like `https://github.com/NethermindEth/pluto/pull/311`,
32+
extract `311`. Reject any other repo.
33+
34+
## 1. Preflight (outer turn — before the loop starts)
35+
36+
Run in parallel:
37+
38+
```bash
39+
gh pr view "$PR" --repo "$REPO" --json title,body,headRefName,headRefOid,baseRefName,state,isDraft
40+
gh pr diff "$PR" --repo "$REPO" | head -5 # confirm diff is fetchable
41+
git status --short
42+
```
43+
44+
Then:
45+
46+
1. Refuse if the PR is `MERGED` or `CLOSED`.
47+
2. Check out the PR branch locally:
48+
```bash
49+
gh pr checkout "$PR" --repo "$REPO"
50+
```
51+
Abort if the working tree is dirty (uncommitted changes) — ask the user to
52+
stash first. Do not run destructive cleanups.
53+
3. Create `$STATE_DIR` if missing. If `$STATE` exists from a prior run, read
54+
it — it contains the running log of resolved findings; the loop will
55+
append to it.
56+
4. If `$STATE` does not exist, initialize it:
57+
58+
```markdown
59+
# /loop-review-pr state for PR #<PR>
60+
61+
- Title: <PR title>
62+
- Branch: <headRefName>
63+
- Started: <ISO timestamp>
64+
- Max iterations: <MAX_ITERATIONS>
65+
66+
## Iteration log
67+
```
68+
69+
## 2. Build the ralph-loop prompt
70+
71+
The prompt is what ralph-loop will feed back to Claude on every iteration.
72+
Construct it as a single string. Substitute `$PR`, `$REPO`, `$STATE`,
73+
`$MAX_ITERATIONS` literally.
74+
75+
````text
76+
You are running iteration of /loop-review-pr for $REPO PR #$PR.
77+
78+
Goal: keep iterating — review the PR with the same parallel-agent pipeline
79+
as /review-pr, then fix what reviewers flag — until the PR is "ideal".
80+
A PR is ideal when an internal review pass produces NO findings at
81+
severity `bug` or `major`, AND `cargo +nightly fmt --all --check`,
82+
`cargo clippy --workspace --all-targets --all-features -- -D warnings`,
83+
and `cargo test --workspace --all-features` all succeed.
84+
85+
State file: $STATE
86+
Read it FIRST every iteration. It is the running log of what previous
87+
iterations did. Append to it; never rewrite earlier entries.
88+
89+
## Per-iteration workflow
90+
91+
1. **Read state.** `cat $STATE`. Note the iteration number — increment by 1
92+
for this iteration. If the prior iteration ended with "PR is ideal",
93+
verify the claim by re-running the quality gates; if still clean, output
94+
`<promise>PR_IDEAL</promise>` and stop.
95+
96+
2. **Sync.** `git fetch origin && git status --short && git log --oneline -5`.
97+
Make sure you are still on the PR branch.
98+
99+
3. **Internal review (no GitHub writes).** Spawn the same four agents in
100+
parallel as /review-pr Step 2:
101+
102+
| Agent | Skill | Focus |
103+
|---|---|---|
104+
| pluto-review | /pluto-review | Functional equivalence with Charon Go |
105+
| security | — | Auth, key material, DoS, exhaustion |
106+
| rust-style | /rust-style | Idiomatic Rust, error handling, naming |
107+
| code-quality | — | Concurrency, state machines, lifecycle |
108+
109+
Give each agent the diff (`gh pr diff $PR --repo $REPO`) and the changed
110+
files on disk. Each agent returns JSON findings as in /review-pr Step 2.
111+
112+
**You MUST NOT** call any of the following during this loop:
113+
- `gh pr review`
114+
- `gh pr comment`
115+
- `gh api .../pulls/.../reviews`
116+
- `gh api .../pulls/.../comments`
117+
- `gh api .../issues/.../comments`
118+
- any GraphQL `addPullRequestReview*` mutation
119+
The summary comment is posted by the OUTER turn after the loop ends —
120+
not from inside the loop.
121+
122+
4. **Dedupe + assess.** Merge findings; assign final severity
123+
(`bug` > `major` > `minor` > `nit`). Same rules as /review-pr Step 3.
124+
125+
5. **Decide.**
126+
- If there are zero `bug` and zero `major` findings → go to step 7.
127+
- Else → step 6.
128+
129+
6. **Fix.** Pick the highest-severity finding, fix the code (and add/update
130+
tests where the finding is about behavior). Re-run the relevant tests
131+
for the touched crate. Commit the fix with a focused message — one
132+
commit per finding is fine, batched commits per file are also fine,
133+
but DO NOT batch unrelated fixes into one commit. Then append an entry
134+
to $STATE:
135+
136+
```markdown
137+
### Iteration <N> — <ISO timestamp>
138+
- [<severity>] <title> @ <file>:<line>
139+
Fix: <one-line description of what changed>
140+
Commit: <sha>
141+
```
142+
143+
After fixing as many findings as you can in this iteration, exit. The
144+
ralph-loop Stop hook will re-invoke this prompt for the next iteration,
145+
which will re-review against the new state of the branch.
146+
147+
7. **Quality gates.** Run from `pluto/`:
148+
```bash
149+
cargo +nightly fmt --all --check
150+
cargo clippy --workspace --all-targets --all-features -- -D warnings
151+
cargo test --workspace --all-features
152+
```
153+
If any fail, treat the failure as a `bug` finding and go back to step 6.
154+
If all pass AND step 5 found no `bug`/`major` findings, append to $STATE:
155+
156+
```markdown
157+
### Iteration <N> — <ISO timestamp> — IDEAL
158+
- Internal review: clean (only minor/nit, or none)
159+
- fmt / clippy / test: green
160+
```
161+
162+
Then output exactly: `<promise>PR_IDEAL</promise>`
163+
164+
## Hard rules
165+
166+
- One PR, one branch. Never switch branches; never rebase onto main inside
167+
the loop unless a fix explicitly requires it (and then say so in $STATE).
168+
- Do not force-push.
169+
- Do not skip git hooks (no `--no-verify`).
170+
- Do not include a `Co-Authored-By:` trailer in commits — the user has
171+
explicitly rejected it.
172+
- Do not delete work-in-progress files left by earlier iterations.
173+
- If progress stalls (two consecutive iterations with no new fixes and the
174+
same findings) — append a `### STALLED` note to $STATE explaining what is
175+
blocking, then output `<promise>PR_IDEAL</promise>` is FORBIDDEN. Instead
176+
let the iteration cap end the loop; the outer turn will summarize the
177+
stall.
178+
````
179+
180+
## 3. Start the loop
181+
182+
Invoke ralph-loop with the prompt above. From the outer turn, call the
183+
ralph-loop slash command (it's the `ralph-loop:ralph-loop` plugin command):
184+
185+
```text
186+
/ralph-loop "<the prompt from §2>" --completion-promise "PR_IDEAL" --max-iterations <MAX_ITERATIONS>
187+
```
188+
189+
Use the Skill tool with `skill: "ralph-loop:ralph-loop"` and pass the prompt
190+
plus flags as args. The loop runs inside the current session; the Stop hook
191+
keeps re-firing the prompt until the completion promise appears or the
192+
iteration cap is hit.
193+
194+
## 4. After the loop ends
195+
196+
When control returns to the outer turn (either the completion promise was
197+
emitted or `--max-iterations` was reached):
198+
199+
1. **Verify gates one more time** from the outer turn (don't trust the loop):
200+
```bash
201+
cd pluto
202+
cargo +nightly fmt --all --check
203+
cargo clippy --workspace --all-targets --all-features -- -D warnings
204+
cargo test --workspace --all-features
205+
```
206+
207+
2. **Push** the accumulated commits to the PR branch:
208+
```bash
209+
git push # branch already tracks the PR head; no --force
210+
```
211+
If the upstream rejected (someone else pushed) → stop and surface to the
212+
user; do not force.
213+
214+
3. **Build the summary** from `$STATE`. Group resolved findings by severity
215+
and reference commits. Compute:
216+
- `iterations_run` = number of `### Iteration N` headers.
217+
- `terminated_by` = `completion_promise` | `iteration_cap` | `stall`.
218+
- `gates` = result of step 1 above.
219+
220+
4. **Post exactly one comment** to the PR:
221+
222+
```bash
223+
gh pr comment "$PR" --repo "$REPO" --body-file /tmp/loop-review-summary.md
224+
```
225+
226+
Body template:
227+
228+
```markdown
229+
## /loop-review-pr summary
230+
231+
Ran <iterations_run> review-and-fix iteration(s) against this PR.
232+
Terminated by: **<terminated_by>**.
233+
234+
### Quality gates (final)
235+
- `cargo fmt` — <pass|fail>
236+
- `cargo clippy` — <pass|fail>
237+
- `cargo test` — <pass|fail>
238+
239+
### Resolved during the loop
240+
241+
**Bugs (<N>)**
242+
- <title> — `<file>:<line>` — fix in <commit-sha>
243+
244+
**Major (<N>)**
245+
- …
246+
247+
**Minor (<N>)**
248+
- …
249+
250+
**Nits (<N>)**
251+
- …
252+
253+
### Outstanding
254+
<Any findings the loop chose not to address, or — if terminated by
255+
iteration_cap / stall — the unresolved items from $STATE, with reasons.>
256+
257+
### Verdict
258+
<One of:
259+
- "PR is ideal — all bug/major findings resolved, gates green."
260+
- "Hit iteration cap (<N>) before reaching ideal state — see Outstanding."
261+
- "Stalled at iteration <N> — see Outstanding for blockers."
262+
>
263+
```
264+
265+
This is the **only** comment posted to GitHub. No inline review comments.
266+
No second comment. If the body is empty (no findings resolved, gates
267+
already green on entry), still post a single one-line "no changes were
268+
needed" comment so the run is auditable.
269+
270+
5. **Print to the user** the PR URL and a one-line verdict, plus the path
271+
to `$STATE` if they want to inspect the full log.
272+
273+
## Error & edge cases
274+
275+
- **No findings in iteration 1, gates green** → loop exits immediately with
276+
`PR_IDEAL`; outer turn posts the "no changes needed" comment.
277+
- **Loop emits the promise but gates fail in the outer verification** →
278+
re-enter the loop with the failing-gate output prepended; do not post a
279+
misleading "ideal" summary.
280+
- **User cancels with `/cancel-ralph`** → outer turn still runs §4 with
281+
`terminated_by: user_cancel` and posts the summary of whatever was done.
282+
- **PR has additional commits pushed by someone else mid-loop** → next
283+
iteration's `git fetch` will surface it; the loop should rebase only if
284+
necessary, and the summary should mention it.
285+
286+
## Output
287+
288+
After §4 step 4 succeeds, print:
289+
290+
```text
291+
Loop done: <iterations_run> iteration(s), terminated by <terminated_by>.
292+
Summary comment: <gh pr comment URL>
293+
State log: $STATE
294+
```

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,8 @@ coverage.json
2828

2929
.peerinfo*
3030

31-
test-infra/sszfixtures/sszfixtures
31+
test-infra/sszfixtures/sszfixtures
32+
33+
.claude/worktrees/
34+
.claude/scheduled_tasks.lock
35+
test-cluster

Cargo.lock

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/app/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ pluto-ssz.workspace = true
3737
pluto-build-proto.workspace = true
3838

3939
[dev-dependencies]
40+
pluto-testutil.workspace = true
4041
wiremock.workspace = true
4142
test-case.workspace = true
4243

0 commit comments

Comments
 (0)