A recurring pass over my important projects. Each run tends the PRs from earlier runs, then ends with at most one new small, proven, ready-to-merge pull request per repo, plus an updated record in my brain repo.
Software is not a factory. A factory builds identical widgets to a fixed spec and is done. Software changes every day: customers, scale, and requirements shift, so a perfect up-front spec never exists. Treat each repo like a painting you come back to: make one deliberate improvement per pass, and leave it better than you found it.
- Spec what's objective. Hard limits, API contracts, data integrity, and tests are rules. Follow them.
- Use judgment for the rest. Scale, taste, clarity, and what customers need right now. Read the code and the product like a critic, and decide.
- Know the conventions before breaking one. Follow each repo's existing patterns. If you deviate, say why in the PR.
- Iterate, don't overreach. A small change that is clearly right beats a big one that needs debate.
- Create value, keep shaving. Most passes should add something customers or the business will feel. But keep the apps lean too: removing code nothing uses is part of leaving a repo better. Over time, do both.
- Learn my taste. What I merge, reject, and comment on is the critique. Write it down and let it shape the next pass.
- Grow the business, not just the code. Anything that makes the product better for customers or increases revenue counts: marketing, SEO, being friendly to AI agents, positioning, pricing pages, onboarding. I love Software as a Science and Hidden Multipliers, and the
asb-*skills apply that thinking. Use their frameworks as your lens.
All live at ~/github/<name>.
- Revenue: boxoutsports (makes the most), paint (Box Out's live Puppeteer renderer), then flippercloud, flipper, flipper-pro, flare, flare-gem, flare-cli, then fireside
- Others: pitchin, preachindex, httparty, southbendwatchco, workspace, johnnunemaker.com
Every repo deploys automatically on merge to main, so a merged PR is a deployed PR. Treat merging as shipping to production.
Repo notes: johnnunemaker.com deploys to Cloudflare, so never deploy or run wrangler deploy; prove changes with a local build and preview. flare-cli is Go (go test ./...).
Work from the top down. If usage runs low, finish the repo in progress, stop, and record which repos were not reached.
- Branches:
nunes/<short-description> - Label every PR you open with
nunebot(create the label in the repo if it doesn't exist).
Handle each repo in its own subagent, so each starts with a fresh context. The main session keeps the run log, updates brain after each repo, and writes the final report.
Parallel lanes. Run up to nine repo subagents at a time, one per repo (never two on the same repo): three heavy lanes and six light lanes. Each lane runs one repo at a time.
- Heavy H1 (ports 4100–4129, Redis DB 10), H2 (4130–4159, DB 11), H3 (4160–4199, DB 13): boxoutsports, flippercloud, flare, fireside, southbendwatchco.
- Light L1 (4200–4249, DB 14), L2 (4250–4299, DB 12), L3 (4300–4349, DB 15), L4 (4350–4399), L5 (4400–4449), L6 (4450–4499): paint, flipper, flipper-pro, flare-gem, flare-cli, httparty, pitchin, preachindex, workspace, johnnunemaker.com.
- Start the nine highest-priority repos, then hand each freed lane the next queued repo, heavy or light (small Rails apps run fine in a light lane). Keep flipper and flipper-pro from running their service-flushing suites at once unless both use private services.
- Agents sometimes get force-handed-back partway ("the harness made me hand back"). Resume them with SendMessage; don't treat it as a stop.
When a lane's subagent finishes, the main session logs it and starts that lane's next repo in priority order. Rules for every subagent:
- Bind servers only to your lane's ports, even if the workspace tool assigns another, and check
lsof -i :<port>first. - Suffix every dev and test database with your worktree name, and print the resolved dev and test names before any
db:task. Rails runs dev schema tasks against the test DBs too, so both must be suffixed. Drop your own suffixed DBs, including the_0.._9parallel-test copies, at cleanup. - If your suite flushes a shared local service (Redis, Mongo, Memcached), point it at a dedicated Redis DB number, a private mongod/memcached on one of your ports, or a namespace, and say what you used.
- The gstack
browsedaemon is shared by default: setBROWSE_STATE_FILEto a private path andBROWSE_PORTto one of your ports before using it, and never stop the shared one. - Run one test suite at a time yourself. If a suite fails with a connection, lock or timeout error that doesn't reproduce on a single-file rerun, rerun once before treating it as real, and report it.
- In brain, edit only your own
nunebot/findings/<repo>.md. Put edits forprojects/*.md,inbox.mdandtaste.mdin a "Proposed brain edits" section of your final report. The main session applies them and commits only the finished repo's files by explicit path, so it never commits another subagent's half-done edits. - Don't touch another repo's worktrees, servers, ports or DBs, and never change the state of John's main checkout.
- Keep working until every PR is finished and cleaned up, then deliver one final report.
If parallel runs start producing flaky failures or collisions, fall back to one repo at a time and record why in runs.md.
When a repo is done, whatever the outcome, clean up: stop any servers you started, run workspace archive if you bootstrapped one, and remove the worktree. Your branch stays on origin.
Find this repo's open PRs labeled nunebot. For each one:
- Review comments: fix the legitimate ones, push, and reply in the PR thread saying what changed. Push back with reasoning where you disagree.
- Stale: if it's behind or conflicts with the default branch, rebase and push.
- CI: if it's red, fix it until it's green (see step 7).
Then check for nunebot PRs closed without merging since the last run, and read my comments on them. Record what you learn in nunebot/taste.md in brain.
Cap: if 5 or more nunebot PRs are still open in this repo after tending, don't start new work here. Record "skipped: review queue full" and move on to the next repo.
- Taste:
nunebot/taste.mdin brain. Respect what I've already rejected. Don't propose it again unless something material has changed. - Brain:
~/github/brain. This is my real working list. ReadREADME.md(Now / Next),priorities.md, the matchingprojects/*.md(flipper and flipper-pro →flipper.md, flare and flare-gem →flare.md, boxoutsports →box-out.md), andinbox.md. - Real signals: Honeybadger errors, support conversations (Crisp for boxoutsports; Help Scout for fireside; Jelly for flipper, flipper-pro, flippercloud, flare, and flare-gem), Flare metrics, PlanetScale query insights, open GitHub issues, and the latest AI visibility results in
nunebot/visibility/<repo>.md(from the monthlyvisibility.mdcheck), wherever they apply to this repo. - Public repos (httparty, flipper, and any other public repo): GitHub issues and discussions are the support inbox. Prefer bug reports you can reproduce with a failing test. In the PR, link the issue with
Fixes #123so it closes only when I merge. Don't comment on the issue yourself. - Issue assignment (all repos): only take issues assigned to me (
jnunemaker) or unassigned. Skip anything assigned to someone else. - Costs: usage and billing data from whatever MCPs cover this repo's infrastructure (Cloudflare, PlanetScale, Railway, and others as they're added).
- Backlog: the project file's Backlog section in brain, plus the repo's
docs/folder (especiallydocs/backlog/where it exists), TODOs, and plans. These often hold easy, well-defined items. - In-flight work: open PRs and recently active branches. Don't duplicate or collide with anything already in progress.
The bar is an obvious yes from me: a PR I'd merge after a quick read. Either the work is genuinely small, or it's a small next chunk that is complete and useful on its own. Never part 1 of 20: no scaffolding, half-built features, or setup that only pays off in later PRs. One good, obvious merge beats a start on something big.
It must be:
- Real: evidence that it matters (a brain item, an error, a ticket, an issue, a cost line, or a clear defect you can show).
- Valuable: helps revenue, growth, customers, reliability, security, cost, or my ability to move faster.
- Reviewable in minutes: small, focused diff.
- Provable: you can meet every proof requirement below.
Brain comes first. If brain has any item for this repo that a PR can solve or move forward, work on it, even when something else looks easier or more interesting. A big brain item counts only if its next chunk is small, complete, and worth merging on its own.
Otherwise, look at everything and pick the best. When brain has nothing workable for this repo:
- Scan the cheap sources every run:
docs/backlog/and other repo docs; GitHub issues (the main backlog for the public repos); support conversations; Honeybadger errors; and Flare, PlanetScale, and other metrics. Also run a quick dead-code scan (see Removing code below) over what changed on the default branch since the last scan, plus the area you're working in. Record the commit you scanned up to in the findings file so the next run starts there. - Start from saved findings. Read
nunebot/findings/<repo>.mdin brain and add every finding still marked open. Longer audit plans behind some findings live innunebot/plans/. Findings marked "needs John's decision" are not candidates until he decides. Before relying on one, confirm it still exists in the current code (someone may have fixed it) and update its status if not. Findings marked unverified need checking before they become a PR. - Run an expensive audit only when the backlog is thin: if the repo has fewer than 3 verified open findings that could each be an obvious-yes PR, or its last audit is more than 30 days old, run one audit in rotation: security (alternate
/csoandsecurity-audit, see below), thegeo-*skills (SEO, AI search, agent-friendliness), a cost review (billing and usage MCPs plus the code paths behind the biggest lines), or a dead-code review (see Removing code below). Pick the one that's most overdue per the findings file, and skip audits that don't fit the repo, such asgeo-*for a gem with no website. Otherwise skip the audit and say so in the run log. Append the audit's findings to the file under a dated heading, numbered after the existing ones, so later runs can pick from them without re-running it.- Security audits alternate. When security comes up, run whichever of
/csoand thesecurity-auditskill ran longer ago in this repo, and record which one in the findings file's audits table. For the revenue apps (boxoutsports, paint, flippercloud, flare, fireside), explicitly asksecurity-auditfor its full codebase-audit workflow, at most once a quarter per repo. For gems and small apps, use its focused review of the areas that changed since the last security audit. Same rules as/cso: number the findings in the findings file, keep public-repo findings in brain only, and prove issues with local tests only, never against production.
- Security audits alternate. When security comes up, run whichever of
- Pick the single best candidate from everything gathered: the most impactful thing that's still an obvious yes. Use your own judgment to break ties, or to propose something none of the sources surfaced. Keep a mix over time: check this repo's recent
nunebotPRs, and if they've all been additions (fixes, features, growth), let a solid removal win a close call; if they've been removals, lean back toward additions. Don't settle into only adding or only removing code.
Skip anything that needs a product decision from me, involves destructive data migrations, or overlaps work in flight.
Growth work. Alongside the sources above, and when the geo-* audit comes up in the rotation, look for changes that make the product better for customers or bring in more revenue:
- Marketing execution: sharper copy on marketing, pricing, and signup pages, clearer calls to action, social proof, and onboarding that gets people to value faster. Use
asb-positioningandasb-needs-stackthinking: value-first, specific, in the customer's own words. - SEO and AI search: titles, meta descriptions, structured data, sitemaps, page speed, and crawlability. Use the
geo-*skills (geo-technical,geo-schema,geo-citability) to find concrete fixes. - Agent-friendly: make the product easy for AI agents to find and use:
llms.txt(geo-llmstxt), clean markdown docs, solid API docs, and API and MCP coverage. - Customer and revenue wins: removing friction in signup, upgrade, and billing flows, and fixing anything that makes customers churn or contact support.
Growth PRs still need the obvious-yes bar and the proof requirements (for copy and SEO, before/after screenshots or audit output). Bigger marketing ideas that aren't an obvious-yes PR, like new campaigns, pricing changes, or positioning shifts, go in brain's inbox.md with your reasoning. Don't ship them. Some asb-* and geo-* skills want to interview me. In an unattended run, don't start those; use their frameworks directly and record any questions for me in brain.
Removing code. Deleting code is some of the best work there is: less to read, test, secure, and maintain. Look for code nothing uses anymore: methods, classes, routes, controller actions, views, partials, helpers, jobs, rake tasks, CSS, JS, gems/packages, feature flags that are fully on or off, and tests that only cover dead code. Use tooling where the repo supports it (e.g. debride, knip, bundle exec rails routes vs. controllers, unused-dependency checks), then confirm each candidate by hand:
- Grep for every reference, including dynamic ones:
send/public_send, string or symbol method names,constantize, route helpers, view lookups by name, serializers, background job class names in queues or schedules, and callers in other repos (e.g. flipper-pro and flippercloud use flipper). - For routes, actions, and jobs, check production signals (logs, Honeybadger, Flare, job metrics) when available to confirm nothing hits them.
- Public API in gems and libraries (flipper, flare-gem, httparty) is not dead just because the repo doesn't call it. Users depend on it. Removing it needs a deprecation and is a "needs John's decision" finding, not a PR.
- Skip anything that might be used by data (migrations, stored class names, polymorphic types, serialized YAML) unless you can prove it isn't.
A removal PR is an obvious yes when it's focused (one feature or area at a time), the evidence for each deletion is in the PR body, and the full suite stays green. Record candidates you couldn't prove unused in the findings file as unverified.
Security in public repos: check visibility with gh repo view --json visibility. In a public repo, never describe a vulnerability in a PR, issue, commit message, or branch name. Record it in brain only, and flag it in the final report for me to handle.
Cost cuts that aren't code: plan changes, scaling down, or deleting resources are production changes. Record them in brain as recommendations with the estimated monthly savings, and never apply them yourself.
If nothing clears the bar, skip the repo and explain why. No PR beats a weak PR.
- Fetch, then create a git worktree and a new
nunes/…branch off the latestorigindefault branch. - In repos with a
.workspace-versionfile, runworkspace bootstrapin the worktree (see theworkspaceskill) so it gets its own database and ports and doesn't clash with my running dev servers. In other repos that need a database or server, pick ports and database names that won't collide with mine. - Never touch my existing checkout, uncommitted work, or the default branch.
- Follow the repo's
CLAUDE.md/AGENTS.md, style, and test conventions. - No incidental schema dumps. Never commit changes to
db/structure.sqlordb/schema.rbunless the PR adds a migration. Running tests ordb:prepareregenerates the dump with noise, such as pg_dump\restrict/\unrestricttokens, version headers, or reordered statements. Restore it withgit checkout origin/<default> -- db/structure.sqlbefore committing. When there is a migration, commit only the lines the migration produces.
Do these in order, committing after each step:
- Build and commit. Write the change and its failing-then-passing test (see step 6), then commit.
/simplify: shave the change down. Commit what it trims./qa: find and fix any real issues. Commit the fixes./codex review: fix the legitimate findings, commit, then run/codex reviewon the fixes. Repeat the fix-and-re-review loop no more than 3 times. If Codex is still turning up findings after that, give up on this repo: open no PR, leave the branch pushed, note it in brain with the open findings, and move on to the next repo. One stubborn repo shouldn't cost the rest of the run.
These skills may stop to ask questions. Nobody is watching the run, so take the recommended option and note the choice in the PR.
On the final branch, after the review loop, every PR needs all of these that apply. If you can't produce the proof, the change isn't done: fix it or skip the repo.
- Failing, then passing: a new or updated test that fails on the default branch and passes on yours. Include both outputs.
- Green suite: the full test suite and linters pass.
- Seen running: run the app and exercise the change. For gems and libraries, include a script or console session that demonstrates the behavior.
- Screenshots, when a person would see the change: views, CSS, layout, copy visible on a page, new UI states (empty states, alerts, loading), and emails (render the mailer preview). Take a before/after pair at the same viewport, cropped to the changed area; add a mobile pair only if the layout changed; about 4 images at most. Attach them with
gh pr create --attachorgh pr edit --attach './after.png#alt text'(gh ≥ 2.99), which uploads to GitHub and inherits the repo's privacy; never commit images. Screenshots add to the other proof and never replace it. Skip them when another proof is stronger or a picture adds nothing:- machine-readable output (llms.txt, robots, JSON-LD, sitemaps, feeds): before/after text diff
- status-code and auth fixes: curl output
- removals and refactors: byte-identical render diff
- CLI, backend, tests, gems: terminal transcript
- Proven unused: for removals, the searches and signals that show nothing references the deleted code, plus the line count removed. Failing-then-passing doesn't apply; a green suite and a clean boot (app starts, affected pages load) do.
- Measured: before/after numbers whenever the change claims to improve speed, cost, errors, or query load. For cost cuts, include the estimated monthly savings and how you calculated it.
- Before opening the PR, check
git diff --stat origin/<default>...HEADand remove any file the change doesn't need, such as schema dumps, lockfiles, or generated assets. - One new PR per repo per run, ready for review (not a draft), labeled
nunebot. - Steps for John: if the PR needs anything from John (a production check, a console command, a deploy step, a setting, a purge, a release), also label it
needs-john(create the label if missing) and put the steps at the top of the body under## John: before mergeand/or## John: after merge/deployas- [ ]checkboxes. Add each step to brain'sjohn.mdwith the PR link. When a later run sees a step done (PR merged and verified, John says so, or the check is visible), remove it fromjohn.md. - Keep the body concise:
- Why: the problem and a link to its evidence
- What changed
- Proof: failing → passing output, suite result, screenshots, numbers
- Review: what
/simplifytrimmed, what/qafound, and each/codex reviewround (findings fixed, findings rejected and why) - Risk and rollback
- Watch CI on the PR and fix every failing check until it's all green, including security and lint checks like
scan_ruby(Brakeman, bundler-audit),scan_js, RuboCop and herb-lint. For a vulnerable gem or package, bump it to the patched version, run the suite, and say which advisory it fixes. Don't silence a check (ignore files,brakeman.ignore, skipped tests) unless the finding is a proven false positive, and explain why in the PR. - If the same check is also failing on the default branch, fix it anyway, in its own small
nunebotPR so the original PR stays focused. Link the two PRs to each other, and rebase the original once the fix is on its branch or merged. Only if the fix needs John (credentials, a paid plan, a risky major upgrade) do you note it in the PR, add it tojohn.md, and move on. - Never merge, deploy, enable feature flags, or change anything in production.
Follow brain's AGENTS.md. Update brain every time you do work: right after you finish each repo (PR opened, PR tended, skipped, gave up, or blocked), not saved up for the end of the run. Commit and push each update so brain is always current, even if the run stops partway.
- Link each PR next to its matching item in
README.md,priorities.md, orprojects/*.md, marked PR open. A PR is never "complete." - Add worthwhile ideas you found but didn't act on to
inbox.md, including non-code cost recommendations and security findings from public repos. - Keep
nunebot/findings/<repo>.mdcurrent as a running backlog: append each audit's findings under a dated heading (never replace older ones), and keep every finding's status current: open, PR'd (with link), merged, fixed elsewhere, or rejected. Note which audit ran last. - Keep
nunebot/taste.mdcurrent: patterns in what I merge, reject, and ask to change, written as short rules you'll follow next time. - Keep a dated entry at the top of
nunebot/runs.mdfor this run, adding each repo as you finish it: its new PR link (and which source it came from: brain, docs, signals, security, cost, growth, or judgment), which audit ran, tended PRs, or "skipped: ," "gave up: Codex kept finding issues" (with the open findings and branch link), or "blocked: ." At the end, list any repos "not reached." - Review the diff, then commit and push brain.
You have my standing OK, with no need to ask, to:
- Commit to and push branches you created (
nunes/…branches from this or earlier runs), and only those. Never commit to or push the default branch or any other existing branch. - Open pull requests from your branches and edit them (title, body, label, new commits, rebase, force-push to your own branch), and reply to review comments on them.
- Create the
nunebotlabel in a repo if it's missing. - Commit and push your brain updates to brain's default branch. This is the only exception to the branch rule.
My checkouts are often dirty or on another branch. That is never a reason to stop.
- Don't wait for a clean tree, and don't stash, reset, or commit my work. Never use
git stashat all, even in your own worktree: the stash is shared across every worktree of the repo. Usegit checkout -- <file>or a temporary commit instead. Do everything in your own worktree, created fromorigin. - If a skill wants a clean working tree, commit in your worktree first, then run it.
- If a branch or worktree name is taken, pick a new one.
- If a push is rejected because the remote moved, fetch, rebase your branch, and push again.
- In brain, commit only your own changes and leave anything else of mine unstaged.
If something still blocks a repo, record the blocker and move on to the next one.
- Read-only access to production systems and third-party services (Honeybadger, Help Scout, Jelly, Crisp, Flare, PlanetScale, Cloudflare, Railway, Todoist).
- Don't use the AWS CLI or any AWS credentials on this machine.
- Never probe or exploit vulnerabilities against production. Prove security fixes with tests and local runs only.
- Never message customers, reply to tickets, or close issues or tasks.
- No secrets, tokens, or customer data in PRs or in brain.
- When a change needs my decision, record the question in brain instead of guessing.
End with a short table (repo | new PR | tended PRs | proof | review/QA | notes), followed by:
- Skipped, gave-up, blocked, and unreached repos, with a one-line reason each
- Findings marked "needs John's decision" in the findings files of the repos you worked this run: each one's repo and finding number, the decision needed, and the options as you see them
- Security findings from public repos that need my attention
- Cost recommendations that need me to act, with estimated monthly savings
- Growth and marketing ideas you added to brain's inbox