From 8c3f035ea98385c41d479587fdf6609adc069f01 Mon Sep 17 00:00:00 2001 From: Scott Werner Date: Wed, 8 Jul 2026 12:01:41 -0400 Subject: [PATCH] Add pr-simplify workflow for automated PR simplify passes (#557) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What Adds `pr-simplify`, a Fabro workflow that runs a "simplify" code-review pass over an existing PR and updates that same PR in place. ## How it works - **One agent, three parallel reviews.** A single agent node runs the pass and uses `spawn_agent` to fan out three reviewers — code reuse, code quality, and efficiency — concurrently, then aggregates their findings. Sub-agent results return directly to the orchestrator, which is the clean way to aggregate multiple perspectives. (A fork + `tripleoctagon` fan-in was the wrong primitive here: fan-in selects a single "best" branch and merges only its worktree, so it would silently drop two of the three reviews.) - **Updates the existing PR — no new PR.** The agent runs `gh pr checkout` on the PR's branch, applies the fixes, commits, and pushes — landing one fixup commit on the existing PR, plus a summary comment and a `simplify:` label. `[run.pull_request] enabled = false` keeps Fabro from opening a second PR from its run branch. - **Fable by default, overridable.** The graph sets `default_model=claude-fable-5`, which floors the orchestrator and all three reviewers to Fable. `--model ` wins over it per run (`configured model → graph default_model → catalog default`), and the label reflects whatever actually ran. ## Usage ```bash fabro run pr-simplify -I pr= # Fable (default) fabro run pr-simplify -I pr= --model gpt-55 # override the model ``` Requires GitHub token permissions `contents` / `pull_requests` / `issues` = write (declared in the workflow) so it can push the commit, comment, and label. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: Bryan Helmkamp <19+brynary@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 Co-authored-by: Bryan Helmkamp <19+brynary@users.noreply.github.com> --- .../workflows/pr-simplify/prompts/simplify.md | 65 +++++++++++++++++++ .fabro/workflows/pr-simplify/workflow.fabro | 14 ++++ .fabro/workflows/pr-simplify/workflow.toml | 31 +++++++++ 3 files changed, 110 insertions(+) create mode 100644 .fabro/workflows/pr-simplify/prompts/simplify.md create mode 100644 .fabro/workflows/pr-simplify/workflow.fabro create mode 100644 .fabro/workflows/pr-simplify/workflow.toml diff --git a/.fabro/workflows/pr-simplify/prompts/simplify.md b/.fabro/workflows/pr-simplify/prompts/simplify.md new file mode 100644 index 000000000..a1e641e55 --- /dev/null +++ b/.fabro/workflows/pr-simplify/prompts/simplify.md @@ -0,0 +1,65 @@ +# Simplify: Code Review and Cleanup + +Run the simplify code-review pass on **PR #{{ inputs.pr }}**: review the changes for reuse, quality, and efficiency, fix what's worth fixing, and update the existing PR in place. + +## Phase 1: Identify the changes + +Run `gh pr diff {{ inputs.pr }}` to see what changed. (Fall back to `git diff origin/main...HEAD` if that returns nothing.) This diff is the shared context for the reviews below. + +## Phase 2: Launch three review sub-agents in parallel + +Use the `spawn_agent` tool to launch all three reviewers below. Spawn all three first so they run concurrently, then `wait` for their results and aggregate them. Give each sub-agent its full brief, and tell it to run `gh pr diff {{ inputs.pr }}` itself to see the changes. Each reviewer **only reports findings — it does not edit code.** + +### Reviewer 1 — Code Reuse + +For each change: + +1. Search for existing utilities and helpers that could replace newly written code. Use grep to find similar patterns elsewhere — utility directories, shared modules, and files adjacent to the changed ones. +2. Flag any new function that duplicates existing functionality; name the existing function to use instead. +3. Flag inline logic that could use an existing utility — hand-rolled string manipulation, manual path handling, custom environment checks, ad-hoc type guards, and similar. + +This is a greenfield app — focus on maximizing simplicity; don't worry about backward compatibility. + +### Reviewer 2 — Code Quality + +Review the same changes for hacky patterns: + +1. Redundant state: state that duplicates existing state, cached values that could be derived, observers/effects that could be direct calls. +2. Parameter sprawl: new parameters bolted onto a function instead of generalizing or restructuring existing ones. +3. Copy-paste with slight variation: near-duplicate blocks that should be unified with a shared abstraction. +4. Leaky abstractions: exposing internals that should be encapsulated, or breaking existing boundaries. +5. Stringly-typed code: raw strings where constants, enums, or branded types already exist. + +This is a greenfield app — be aggressive in optimizing quality. + +### Reviewer 3 — Efficiency + +Review the same changes for efficiency: + +1. Unnecessary work: redundant computations, repeated file reads, duplicate network/API calls, N+1 patterns. +2. Missed concurrency: independent operations run sequentially when they could run in parallel. +3. Hot-path bloat: new blocking work added to startup or per-request/per-render hot paths. +4. Unnecessary existence checks: pre-checking a file/resource before operating (TOCTOU) — operate directly and handle the error. +5. Memory: unbounded data structures, missing cleanup, listener leaks. +6. Overly broad operations: reading whole files when a portion suffices, loading all items when filtering for one. + +## Phase 3: Apply fixes + +Wait for all three reviewers, aggregate their findings, and fix each issue directly. If a finding is a false positive or not worth addressing, note it and move on — don't argue with it, just skip it. + +## Phase 4: Update the existing PR + +1. **Check out the PR branch:** run `gh pr checkout {{ inputs.pr }}` so your edits land on the PR's own branch. Do not create a new branch or a new PR. +2. **Commit and push:** commit with a clear message describing the simplify pass, then push to update PR #{{ inputs.pr }}. Push to the **existing** branch — do **not** open a new pull request. If you made no changes (the code was already clean), skip the commit and say so in the comment below. +3. **Comment:** post a summary on PR #{{ inputs.pr }} noting this was an automated simplify run, what was fixed, and anything you skipped and why: + + gh pr comment {{ inputs.pr }} --body "" + +4. **Label:** record which model ran this pass. Read the model you are running as from your environment block (the `Model:` line) and build the label `simplify:` — e.g. if your model is `claude-opus-4-8`, the label is `simplify:claude-opus-4-8`. Apply it via the GitHub **issues** API; do **not** use `gh pr edit --add-label` (it walks a deprecated Projects-classic GraphQL path and fails here). Ensure the label exists, then add it (gh fills `{owner}/{repo}` from the current repo): + + gh label create "simplify:" --color BFD4F2 --description "Simplify pass by " 2>/dev/null || true + gh api repos/{owner}/{repo}/issues/{{ inputs.pr }}/labels -X POST -f "labels[]=simplify:" + +## Done + +Briefly summarize what was fixed (or confirm the code was already clean), and confirm the PR was updated, commented, and labeled. diff --git a/.fabro/workflows/pr-simplify/workflow.fabro b/.fabro/workflows/pr-simplify/workflow.fabro new file mode 100644 index 000000000..7866adad6 --- /dev/null +++ b/.fabro/workflows/pr-simplify/workflow.fabro @@ -0,0 +1,14 @@ +digraph PrSimplify { + graph [ + goal="Run the simplify code-review pass on PR #{{ inputs.pr }}: review the changes for reuse, quality, and efficiency, apply the fixes, and update the existing PR in place.", + default_model="claude-fable-5", + rankdir=LR + ] + + start [shape=Mdiamond, label="Start"] + exit [shape=Msquare, label="Exit"] + + simplify [label="Simplify Pass", prompt="@prompts/simplify.md"] + + start -> simplify -> exit +} diff --git a/.fabro/workflows/pr-simplify/workflow.toml b/.fabro/workflows/pr-simplify/workflow.toml new file mode 100644 index 000000000..478356fc0 --- /dev/null +++ b/.fabro/workflows/pr-simplify/workflow.toml @@ -0,0 +1,31 @@ +_version = 1 + +# PR simplify workflow. Runs the "simplify" code-review pass on an existing PR: +# one agent reviews the diff for reuse, quality, and efficiency (fanning the +# three reviews out in parallel via spawn_agent subagents and aggregating their +# results), applies the fixes, and updates the PR in place (commit + push + +# comment + label). +# +# Required input — the PR number — is passed at run time: +# +# fabro run pr-simplify -I pr=547 # defaults to Fable +# fabro run pr-simplify -I pr=547 --model gpt-55 # override the model +# +# The graph sets default_model=claude-fable-5, so every stage (orchestrator + +# review subagents) runs on Fable unless you pass --model, which wins over the +# default (configured model / --model beats the graph default_model, which +# beats the catalog default). The apply stage reads the model it is actually +# running as and labels the PR `simplify:`, so the label always +# matches the real run with nothing to keep in sync. + +# Fabro's built-in flow opens a NEW PR from the run branch when enabled, and +# this repo's .fabro/project.toml turns that on by default. Turn it off here: +# pr-simplify updates the EXISTING PR in place (the agent pushes onto the PR's +# own branch), so Fabro must not open a second PR afterward. +[run.pull_request] +enabled = false + +[run.integrations.github.permissions] +contents = "write" # push the fixup commit to the PR branch +pull_requests = "write" # gh pr checkout / comment on the PR +issues = "write" # add the simplify: label (labels use the issues API)