Bluepy Agent Runbook (Claude)#
AGENTS.md mirrors this file. Only difference: reviewer identity + review CLI. This file is for Claude coders; Codex reviews Claude-authored work. AGENTS.md is for Codex coders; Claude reviews Codex-authored work. Commit both files together. Product/UX conventions, footer credits, palette, and domain language live in
docs/CONTEXT.md.
The Path — follow top to bottom, every task#
→ Worktree off bluesky. Never work on bluesky directly.
git worktree add -b fix/<name> /tmp/bluepy-<name> bluesky — also feat/<name>, chore/<name>, agent/issue-<number>.
→ Secrets: source ~/.secrets/bluepy/source.env.
→ Build: Bun only (bun install, bun run …, bunx …). Reuse @atproto/* types before defining new ones. "Type X" means convert X to TS, not patch JSDoc into .js. Runtime is browser / Cloudflare Workers.
→ Verify (apply the gates that match your change):
- typecheck — always:
bun run typecheck - test — when touching a module with Playwright coverage:
bun run test - build — when changing bundling, routing, imports, assets, or packaging:
bun run build - lint — every change, regardless of scope:
bunx oxlint <changed files>. If you changed a file, lint it before staging. - behavioral change (timeline, post, compose, notifications, auth, routing)? Write and run an e2e test, then walk the affected flow logged in (
bun run dev, expose via HTTPS). Logged-out routes are not correctness evidence. Pure refactors / typecheck-only / copy tweaks: existing suite passing is enough, no new e2e. → Stage:git status -sbandgit diff --name-status. Report any dirty files. → Review loop (cross-model, read-only): Codex reviews → you fix actionable findings in code → re-run relevant verification → re-review until Codex reports "no actionable findings". Hard cap 5 rounds. See Review CLI below. → Ship: only after review rounds are clean and e2e is written/run — push your branch and open a draft PR againstbluesky. Automation takes over: preview deploy → agent-pr loop →human-reviewlabel.
Ask first — everything else is yours#
The Path above is yours to run autonomously. These few actions are the exceptions — check in before proceeding:
- To update pushed work, add a follow-up commit and
git push. Force-push (--force,--force-with-lease) is not part of our workflow. - Add new commits to update published work. Merging and amending published commits need an explicit ask.
- Keep PRs in draft until asked to mark ready.
- Leave the prod Worker (
bluepy→bluepy.social) to the human. - Only modify files that are part of your task.
git restore,git checkout --,git clean -fon other files need an explicit ask. - Run all checks as-is:
--no-verify,.skip,xfail,eslint-disable,@ts-ignore,@ts-expect-error, andanyare off-limits. - If a lint rule blocks useful work, report the rule name, diagnostic, and smallest compliant change; for a real change write
docs/rule-change-proposals/YYYY-MM-DD-name.mdand get reviewer approval first. - Only run
lingui extractwhen source strings changed. Preserve<Trans>tags. - Keep generated images and locale catalogs out of code commits (separate commit, only if asked).
- Run
bunx oxlint <changed files>on every changed file before pushing or opening a PR. - Send your work to Codex for review. Claude does not review Claude-authored work.
Review CLI — Codex reviews Claude's work#
Robust by construction: do not inline the diff. The reviewer runs git itself, so there is nothing to quote or truncate. Build the prompt with a quoted heredoc (<<'PROMPT') so the shell never expands $, backticks, or quotes.
cat > /tmp/bluepy-review-prompt.md <<'PROMPT'
You are read-only in the staged worktree. Gather context yourself: run
`git diff --no-color HEAD`, `bun run typecheck`, and `bunx oxlint <changed files>`.
Report correctness findings ordered by severity with file:line refs — behavioral
regressions, unsafe casts, hidden runtime assumptions, missing tests, rule bypasses,
generated/lockfile/locale churn, unrelated drive-bys, skipped tests. If none, say
"no actionable findings" and list residual risks.
PROMPT
codex exec \
-c model='"gpt-5.5"' \
-c model_reasoning_effort='"high"' \
--dangerously-bypass-approvals-and-sandbox \
--skip-git-repo-check \
"$(cat /tmp/bluepy-review-prompt.md)" < /dev/null > codex-review.out 2>&1
< /dev/nullis required —codex exechangs waiting for stdin EOF in non-TTY contexts without it.- Use the exact model and effort shown:
gpt-5.5withmodel_reasoning_effort="high". - Do not add a timeout or a budget cap. Neither is needed.
Secrets & Paths#
All secrets live in ~/.secrets/bluepy/ with private permissions. Never log, print, or commit them.
source ~/.secrets/bluepy/source.envsetsCLOUDFLARE_EMAIL,CLOUDFLARE_API_KEY,CLOUDFLARE_ACCOUNT_ID,ATPROTO_TEST_IDENTIFIER,ATPROTO_TEST_PASSWORD.- Reference clients are read-only:
~/social-appfirst, optionallygithub.com/mozzius/graysky.
Branches, Remotes & Deploy#
blueskyis main. Old TypeScript migration branches are not the default base for new work.fork(github.com/aliceisjustplaying/bluepy) is the GitHub deploy source;tangledis a mirror.- Production deploys from
fork/bluesky, not feature branches. Use/tmp/bluepy-bluesky-deployor another clean worktree for deploy-target work. - Cloudflare account:
aliceisjustplaying@gmail.com, account IDb752c979e541327de3e87e52f0906aa1, zonebluepy.socialidc3e3ebea11871d784375b74624d3b6cd. - Workers:
bluepy→bluepy.social(prod, do not touch unless asked) ·bluepy-dev→dev.bluepy.social(bunx wrangler deploy --env dev) · PR previews →https://pr-<number>.bluepy.socialvia.github/workflows/bluepy-preview.yml.
PR Automation#
- Issue label
agent:codexruns.github/workflows/bluepy-agent.yml→scripts/agent-loop.sh issue <number>. - PRs with
agent:previewget a preview deploy on open, sync, reopen, ready-for-review, or label events. - After a successful
Bluepy Previewrun,.github/workflows/bluepy-agent-pr.yml→scripts/agent-loop.sh pr <number>unless the PR already hashuman-review. - The PR loop runs deterministic verification, asks Claude for structured JSON review, lets Codex fix actionable findings, repeats, waits for a passing preview, then labels
human-review. /bluepy-agentcomments from owners, members, or collaborators runscripts/agent-loop.sh comment <number>.- Self-hosted preview jobs require runners tagged
bluepy-deploy; agent jobs requirebluepy-agent. - Agent workflow logs live under
/workspace/agent-worktrees/bluepy/logsand are uploaded as GitHub artifacts (raw + normalized review JSON preserved).
Environment Quirks#
- Agent-runner browser checks inherit
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATHandAGENT_BROWSER_EXECUTABLE_PATHfromscripts/agent-loop.sh; both point atscripts/chromium-for-agents. - Use
agent-browserfor ad-hoc visual checks (agent-browser skills get core --full);bun run testfor Playwright smoke/regression tests. - Playwright browsers are installed via Nix, not
npx playwright install. If launch fails on NixOS, point Playwright at the Nix-provided Chromium wrapper. - Tailscale previews need HTTPS for OAuth/SubtleCrypto. Use
p.tailec2dc.ts.net; the bare IP only works for non-OAuth flows. - Mobile Safari content blockers can kill
src/utils/push-notifications.jsand leave "Bluepy is still loading...". - Host is Hermes (Hetzner arm64, 4cpu/8gb). If CPU is hot, inspect
hermes,codex,claude,camoufox, andagent-browserprocesses before killing anything.
Models#
- Claude:
claude-opus-4-7with--effort xhigh. Never Sonnet, Haiku, or defaults. - Codex:
gpt-5.5withmodel_reasoning_effort="high". Never defaults. - External manual reviewer of record: GPT-5.5 Pro via
scripts/dump-source-for-review.sh.
Domain Rules#
- AT URIs are native: serialize as
at://…, neverat:/…. - Do not invent types that codify known-broken behavior.
- Bluesky image limits: 2 MB and about 4000 px long edge.