- TypeScript 80%
- JavaScript 17%
- Shell 3%
pi-rig #
My Pi setup: the subagents I delegate to, the review pipeline I run changes through, and the ledger that decides which reviewers are worth keeping.
Layout #
AGENTS.md— workflow and routing rules; start here.agents/— reviewer, advisor, rebaser, scout, and summarizer cards.skills/— the review tiers and branch reviews.extensions/— Gemini reviewer tool, subagent runner, review ledger, catalog filter.scripts/— ledger tooling, the roster generator, a few probes. Node runs the.tsdirectly.scripts/snapshot.zsh— on-demand V8 heap snapshot wrapper and trigger (SIGUSR2) for profiling extensions with zero overhead.docs/— reviewer head-to-heads and spend.peak-guard.zsh— launcher hook that reroutes DeepSeek runs to Luna during peak pricing (PI_PEAK_GUARD=offdisables it).
The loop #
The main session keeps the edits; read-only work goes to subagents, usually the free ones.
- Build the change.
- Once it compiles and the obvious checks pass, review the diff before showing it.
- Two or three reviewers read the same diff in parallel; I check every claim against the code before repeating it.
- Fix the real findings, then verify with a pass one tier down.
- If that pass still finds real issues, a third round is allowed. Two rounds is the norm.
- Docs-only and throwaway changes skip review.
Per-commit reviews stop once the user says the branch is ready to be considered for
merging; from then on it gets one branch-review-* pass instead — see below.
Review tiers #
| tier | what it's for |
|---|---|
review-min |
a trivial change, or an extra round when the last one still found things |
review-low |
a small, low-risk change; the usual verification pass |
review-medium |
an ordinary finished change — the default |
review-high |
rare; genuinely tricky, or riskier than average |
review-xhigh |
rarer; three reviewers instead of two |
review-max |
very rare — the expensive panel |
Two reviewers a tier, three for review-xhigh, from different labs. Each skill names its
own roster and effort level; the rosters are generated from one file, so the tables in the
skills, AGENTS.md, and the shared procedure can't drift.
-
review-min's second reviewer is OpenRouter's free router (openrouter/free), which picks a free model per request: cheap breadth, but no stable identity, and occasionally a 429 or a thin reply — a run like that is recorded as a missing arm, not a clean pass. -
No model reviews its own work: when the change's author or session model is on the panel, that arm is swapped out (a peak-guard reroute to Luna counts).
-
Every finding is Confirmed, Plausible, or Rejected before it's reported. Silence isn't proof: one arm once came back clean on a change two others found a real bug in.
Not in any tier: the advisor agents (haiku, luna, nemotron, sol) for ad-hoc
questions. Nothing they say is recorded.
Branch reviews #
Once the user says the branch is ready to be considered for merging, commit-level
reviews stop and the branch gets one branch-review-* pass instead. It runs against
the merge base rather than the latest commit: it reads the whole
git diff <merge-base>...HEAD, audits every commit message, and judges whether the
branch is one PR or several. If the branch has fallen behind its upstream, rebase it
first (upstream-rebase) so the review sees the final code.
| track | first pass | verify fixes |
|---|---|---|
| headed to your own main branch | branch-review-low |
branch-review-min |
| upstream-bound | branch-review |
branch-review-min |
| large or genuinely risky | branch-review-high |
branch-review-low |
branch-review-min is also fine for a small or draft branch. Every finding is
validated against the repository and history before it's reported, exactly as for a
per-commit pass, and the same reviewer-independence rule applies — a branch usually
has mixed authors, so run the tier's panel as specified rather than adjusting it for
the active model.
The ledger #
Every reviewer run is appended to a JSONL ledger under ~/repos/review-ledger (outside
this repo). A run records who it was (arm, model, effort), what it cost (dollars for
metered arms, plus wall-clock and tokens), and whether it delivered — a run cut short,
answered nothing, or replied with narration is flagged, never counted as a clean arm that
found nothing. Each panel also gets a hand-written assessment: a score, and a tally of
how many of its findings were real. Capture is automatic.
scripts/review-stats.ts rolls it up. Across the current ledger (337 panels, 599
invocations, 411 assessments), the per-variant table is:
| variant | avg score | runs | cells | mean | median | min | max |
|---|---|---|---|---|---|---|---|
| sol-medium | 8.33 | 3 | 3 | $0.2511 | $0.2446 | $0.1420 | $0.3666 |
| haiku-medium | 8.06 | 19 | 16 | $0.0124 | $0.0093 | $0.0039 | $0.0430 |
| haiku-high | 7.67 | 10 | 6 | $0.0223 | $0.0168 | $0.0072 | $0.0467 |
| luna | 7.63 | 4 | 8 | $0.0068 | $0.0057 | $0.0006 | $0.0151 |
| haiku-low | 7.16 | 78 | 69 | $0.0050 | $0.0043 | $0.0004 | $0.0177 |
| luna-medium | 7.00 | 58 | 66 | $0.0081 | $0.0075 | $0.0025 | $0.0216 |
| openrouter-free | 7.00 | 12 | 8 | $0.0000 | $0.0000 | $0.0000 | $0.0000 |
| gemini-medium | 6.89 | 97 | 124 | sub | sub | sub | sub |
| luna-xhigh | 6.70 | 5 | 10 | $0.0151 | $0.0149 | $0.0138 | $0.0162 |
| gemini-low | 6.52 | 69 | 65 | sub | sub | sub | sub |
| gemini-high | 6.51 | 53 | 98 | sub | sub | sub | sub |
| luna-low | 6.31 | 31 | 29 | $0.0026 | $0.0024 | $0.0016 | $0.0052 |
| luna-high | 6.30 | 54 | 74 | $0.0147 | $0.0128 | $0.0028 | $0.0336 |
| gemini | 5.97 | 19 | 38 | sub | sub | sub | sub |
| luna-max | 5.88 | 4 | 8 | $0.0525 | $0.0481 | $0.0353 | $0.0783 |
| ling-high | 5.82 | 58 | 51 | $0.0040 | $0.0030 | $0.0000 | $0.0143 |
| free-nemotron3super-120b-high | 5.47 | 22 | 17 | $0.0000 | $0.0000 | $0.0000 | $0.0000 |
What the columns mean:
avg score— the mean assessment score (out of 10) this variant earned, overcellsjudged cells.runs— how many invocations it took.mean/median/min/max— its per-run cost, in dollars.submeans subscription-billed (the Gemini tool) rather than metered, so the exact per-run cost is not recorded and cannot be known.- Ordering — by avg score (highest first); variants with no score are excluded.
Two things worth keeping in mind. Read the median over the mean, since a few cache-heavy runs pull the mean up. And compare arms within a tier, never across tiers — a follow-up round is a cheaper, smaller job than a first pass.
review-stats.ts also flags runs that produced no usable report, since their cost is retry
overhead rather than the arm's price for a review. --write emits the full report under the
ledger, and --panel <review-id> shows one panel's arms and its assessment state. Rosters
are picked from this table: an arm that scores low for a high price doesn't keep its slot.
Profiling memory #
scripts/snapshot.zsh profiles extension and session memory using Node's built-in V8 heap snapshots
via SIGUSR2. It requires zero code changes and has zero runtime overhead until triggered. Snapshots
are saved into snapshots/ (gitignored).
Workflow #
- Launch Pi with profiling enabled (Terminal 1):
./scripts/snapshot.zsh - Take a baseline snapshot (Terminal 2):
./scripts/snapshot.zsh snap - Execute the workload in Pi (e.g. run a subagent task, review pass, or chain).
- Take an after-action snapshot (Terminal 2):
./scripts/snapshot.zsh snap - Inspect in Chrome DevTools:
- Open Chrome or Brave and navigate to
chrome://inspect. - Go to the Memory tab and click Load.
- Load both snapshots from
snapshots/. - Select the second snapshot and switch the view perspective from Summary to Comparison to inspect exact retained byte deltas.
- Open Chrome or Brave and navigate to
Helper commands:
./scripts/snapshot.zsh list— list saved snapshots and total disk footprint../scripts/snapshot.zsh clean— remove all captured snapshot files.
Maintenance #
- Rosters are generated: edit
scripts/review-matrix.ts, then--write, then--check. Don't hand-edit the marked tables; the drift check will fail. - Ling is off every roster while its provider is rate-limited (
LING_AVAILABLE). auth.json, sessions, logs, and the model store are gitignored.