diff --git a/AGENTS.md b/AGENTS.md index cd50fdc5..9db525f9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,105 +1,106 @@ # Bluepy Agent Runbook (Codex) -> CLAUDE.md mirrors this file. Only difference: reviewer identity. This file is for Codex coders; Claude reviews Codex-authored work. CLAUDE.md is for Claude coders; Codex reviews Claude-authored work. Commit both files together. Product/UX conventions, footer credits, palette, and domain language live in `docs/CONTEXT.md`. +> CLAUDE.md mirrors this file. Only difference: reviewer identity + review CLI. This file is for Codex coders; Claude reviews Codex-authored work. CLAUDE.md is for Claude coders; Codex reviews Claude-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/ /tmp/bluepy- bluesky` — also `feat/`, `chore/`, `agent/issue-`. +→ **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 `. 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 -sb` and `git diff --name-status`. Report any dirty files. +→ **Review loop** (cross-model, read-only): Claude reviews → you fix actionable findings in code → re-run relevant verification → re-review until Claude 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 against `bluesky`. Automation takes over: preview deploy → agent-pr loop → `human-review` label. + +## 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 -f` on other files need an explicit ask. +- Run all checks as-is: `--no-verify`, `.skip`, `xfail`, `eslint-disable`, `@ts-ignore`, `@ts-expect-error`, and `any` are 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.md` and get reviewer approval first. +- Only run `lingui extract` when source strings changed. Preserve `` tags. +- Keep generated images and locale catalogs out of code commits (separate commit, only if asked). +- Run `bunx oxlint ` on every changed file before pushing or opening a PR. +- Send your work to Claude for review. Codex does not review Codex-authored work. + +## Review CLI — Claude reviews Codex'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. -## Always - -- Work in a **worktree** off `bluesky`, on a branch: `fix/`, `feat/`, `chore/`, or `agent/issue-`. - - `git worktree add -b fix/ /tmp/bluepy- bluesky` -- Use Bun only: `bun install`, `bun run ...`, `bunx ...`. Runtime is browser / Cloudflare Workers. -- Run `git status -sb` and `git diff --name-status` before staging. Report dirty files. -- Open a draft PR against `bluesky` when work is ready for automated preview/review. +```bash +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 `. +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 -## Don't +claude -p \ + --model claude-opus-4-7 \ + --effort xhigh \ + --no-session-persistence \ + "$(cat /tmp/bluepy-review-prompt.md)" < /dev/null > claude-review.out 2>&1 +``` -- Merge PRs, force-push, or amend published commits unless the user explicitly asks. -- Delete or restore files you don't own. No `git restore`, `git checkout --`, or `git clean -f` outside your own staged work. -- Bypass checks: no `--no-verify`, `.skip`, `xfail`, `eslint-disable`, `@ts-ignore`, `@ts-expect-error`, or `any`. -- Commit generated images unless asked. Keep generated locale catalogs in separate commits. -- Run `lingui extract` unless source strings changed. Never strip `` tags. -- Touch the prod Worker (`bluepy`) unless explicitly asked. -- Self-review. Codex never reviews Codex-authored work. +- `< /dev/null` is required — the reviewer hangs waiting for stdin EOF in non-TTY contexts without it. +- Use the exact model and effort shown: `claude-opus-4-7` with `--effort xhigh`. +- Do not add `--max-budget-usd` or any timeout. Neither is needed. ## Secrets & Paths All secrets live in `~/.secrets/bluepy/` with private permissions. Never log, print, or commit them. -- Load secrets with `source ~/.secrets/bluepy/source.env`. -- This sets `CLOUDFLARE_EMAIL`, `CLOUDFLARE_API_KEY`, `CLOUDFLARE_ACCOUNT_ID`, `ATPROTO_TEST_IDENTIFIER`, and `ATPROTO_TEST_PASSWORD`. +- `source ~/.secrets/bluepy/source.env` sets `CLOUDFLARE_EMAIL`, `CLOUDFLARE_API_KEY`, `CLOUDFLARE_ACCOUNT_ID`, `ATPROTO_TEST_IDENTIFIER`, `ATPROTO_TEST_PASSWORD`. - Reference clients are read-only: `~/social-app` first, optionally `github.com/mozzius/graysky`. ## Branches, Remotes & Deploy - `bluesky` is main. Old TypeScript migration branches are not the default base for new work. -- `fork` (`github.com/aliceisjustplaying/bluepy`) is the GitHub deploy source. `tangled` is a mirror. +- `fork` (`github.com/aliceisjustplaying/bluepy`) is the GitHub deploy source; `tangled` is a mirror. - Production deploys from `fork/bluesky`, not feature branches. Use `/tmp/bluepy-bluesky-deploy` or another clean worktree for deploy-target work. - Cloudflare account: `aliceisjustplaying@gmail.com`, account ID `b752c979e541327de3e87e52f0906aa1`, zone `bluepy.social` id `c3e3ebea11871d784375b74624d3b6cd`. -- Workers: - - `bluepy` -> `bluepy.social` (prod). Do not touch unless asked. - - `bluepy-dev` -> `dev.bluepy.social` (`bunx wrangler deploy --env dev`). - - PR previews -> `https://pr-.bluepy.social` via `.github/workflows/bluepy-preview.yml`. +- Workers: `bluepy` → `bluepy.social` (prod, do not touch unless asked) · `bluepy-dev` → `dev.bluepy.social` (`bunx wrangler deploy --env dev`) · PR previews → `https://pr-.bluepy.social` via `.github/workflows/bluepy-preview.yml`. ## PR Automation -- Issue label `agent:codex` runs `.github/workflows/bluepy-agent.yml`, which calls `scripts/agent-loop.sh issue `. +- Issue label `agent:codex` runs `.github/workflows/bluepy-agent.yml` → `scripts/agent-loop.sh issue `. - PRs with `agent:preview` get a preview deploy on open, sync, reopen, ready-for-review, or label events. -- After a successful `Bluepy Preview` run, `.github/workflows/bluepy-agent-pr.yml` calls `scripts/agent-loop.sh pr ` unless the PR already has `human-review`. +- After a successful `Bluepy Preview` run, `.github/workflows/bluepy-agent-pr.yml` → `scripts/agent-loop.sh pr ` unless the PR already has `human-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-agent` comments from owners, members, or collaborators run `scripts/agent-loop.sh comment `. - Self-hosted preview jobs require runners tagged `bluepy-deploy`; agent jobs require `bluepy-agent`. -- Agent workflow logs live under `/workspace/agent-worktrees/bluepy/logs` and are uploaded as GitHub artifacts. Claude raw and normalized review JSON are preserved there. +- Agent workflow logs live under `/workspace/agent-worktrees/bluepy/logs` and are uploaded as GitHub artifacts (raw + normalized review JSON preserved). ## Environment Quirks -- Use authenticated views when checking visual or behavioral regressions. Logged-out routes are not correctness evidence for timeline, post, compose, notification, or account flows. - Agent-runner browser checks inherit `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH` and `AGENT_BROWSER_EXECUTABLE_PATH` from `scripts/agent-loop.sh`; both point at `scripts/chromium-for-agents`. -- Use `agent-browser` for ad-hoc visual checks (`agent-browser skills get core --full`). Use `bun run test` for Playwright smoke/regression tests. -- Playwright browsers are installed via Nix, not `npx playwright install`. If browser launch fails on NixOS, point Playwright at the Nix-provided Chromium wrapper. +- Use `agent-browser` for ad-hoc visual checks (`agent-browser skills get core --full`); `bun run test` for 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.js` and leave "Bluepy is still loading...". - Host is Hermes (Hetzner arm64, 4cpu/8gb). If CPU is hot, inspect `hermes`, `codex`, `claude`, `camoufox`, and `agent-browser` processes before killing anything. -## Verification - -- `bun run typecheck` gates normal code changes. -- `bun run test` gates modules with Playwright coverage. -- `bun run build` gates bundling, routing, import, asset, and runtime packaging changes. -- `bunx oxlint ` gates changed-file delta; the project baseline still has unrelated noise. -- Browser verification must be logged in for authenticated flows: `source ~/.secrets/bluepy/source.env`, `bun run dev`, expose via HTTPS, then walk the affected flow. - ## Models - Claude: `claude-opus-4-7` with `--effort xhigh`. Never Sonnet, Haiku, or defaults. - Codex: `gpt-5.5` with `model_reasoning_effort="high"`. Never defaults. - External manual reviewer of record: GPT-5.5 Pro via `scripts/dump-source-for-review.sh`. -## Lint & Code Discipline +## Domain Rules -- Do not turn off lint rules without explicit permission in the current turn. -- If a rule blocks useful work, report the rule name, diagnostic, and smallest compliant change. For a real rule change, write `docs/rule-change-proposals/YYYY-MM-DD-name.md` and get reviewer approval first. -- Reuse `@atproto/*` types before defining new ones. -- "Type X" means convert X to TS, not patch JSDoc into `.js`. +- AT URIs are native: serialize as `at://…`, never `at:/…`. - Do not invent types that codify known-broken behavior. -- AT URIs are native: serialize as `at://...`, never `at:/...`. - Bluesky image limits: 2 MB and about 4000 px long edge. - -## Review Loop - -Codex writes, verifies, stages, and fixes. Claude reviews read-only through structured output. Fix actionable findings in code, re-run relevant verification, and re-review until Claude reports no actionable findings. Hard cap is 5 review rounds. - -### Claude Review CLI - -Run from the staged worktree. `< /dev/null` is mandatory in non-TTY contexts. - -```bash -DIFF="$(git diff --no-color HEAD)" -TYPECHECK="$(bun run typecheck 2>&1)" -LINT="$(bunx oxlint 2>&1)" -claude -p \ - --model claude-opus-4-7 \ - --effort xhigh \ - --no-session-persistence \ - "$PROMPT" < /dev/null > claude-review.out 2>&1 -``` - -The prompt should ask for 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, and skipped tests. If none, Claude should say "no actionable findings" and list residual risks. diff --git a/CLAUDE.md b/CLAUDE.md index dbf35da8..92cd3dfe 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,106 +1,107 @@ # Bluepy Agent Runbook (Claude) -> AGENTS.md mirrors this file. Only difference: reviewer identity. 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`. +> 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/ /tmp/bluepy- bluesky` — also `feat/`, `chore/`, `agent/issue-`. +→ **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 `. 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 -sb` and `git 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 against `bluesky`. Automation takes over: preview deploy → agent-pr loop → `human-review` label. + +## 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 -f` on other files need an explicit ask. +- Run all checks as-is: `--no-verify`, `.skip`, `xfail`, `eslint-disable`, `@ts-ignore`, `@ts-expect-error`, and `any` are 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.md` and get reviewer approval first. +- Only run `lingui extract` when source strings changed. Preserve `` tags. +- Keep generated images and locale catalogs out of code commits (separate commit, only if asked). +- Run `bunx oxlint ` 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. -## Always - -- Work in a **worktree** off `bluesky`, on a branch: `fix/`, `feat/`, `chore/`, or `agent/issue-`. - - `git worktree add -b fix/ /tmp/bluepy- bluesky` -- Use Bun only: `bun install`, `bun run ...`, `bunx ...`. Runtime is browser / Cloudflare Workers. -- Run `git status -sb` and `git diff --name-status` before staging. Report dirty files. -- Open a draft PR against `bluesky` when work is ready for automated preview/review. +```bash +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 `. +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 -## Don't +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 +``` -- Merge PRs, force-push, or amend published commits unless the user explicitly asks. -- Delete or restore files you don't own. No `git restore`, `git checkout --`, or `git clean -f` outside your own staged work. -- Bypass checks: no `--no-verify`, `.skip`, `xfail`, `eslint-disable`, `@ts-ignore`, `@ts-expect-error`, or `any`. -- Commit generated images unless asked. Keep generated locale catalogs in separate commits. -- Run `lingui extract` unless source strings changed. Never strip `` tags. -- Touch the prod Worker (`bluepy`) unless explicitly asked. -- Self-review. Claude never reviews Claude-authored work. +- `< /dev/null` is required — `codex exec` hangs waiting for stdin EOF in non-TTY contexts without it. +- Use the exact model and effort shown: `gpt-5.5` with `model_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. -- Load secrets with `source ~/.secrets/bluepy/source.env`. -- This sets `CLOUDFLARE_EMAIL`, `CLOUDFLARE_API_KEY`, `CLOUDFLARE_ACCOUNT_ID`, `ATPROTO_TEST_IDENTIFIER`, and `ATPROTO_TEST_PASSWORD`. +- `source ~/.secrets/bluepy/source.env` sets `CLOUDFLARE_EMAIL`, `CLOUDFLARE_API_KEY`, `CLOUDFLARE_ACCOUNT_ID`, `ATPROTO_TEST_IDENTIFIER`, `ATPROTO_TEST_PASSWORD`. - Reference clients are read-only: `~/social-app` first, optionally `github.com/mozzius/graysky`. ## Branches, Remotes & Deploy - `bluesky` is main. Old TypeScript migration branches are not the default base for new work. -- `fork` (`github.com/aliceisjustplaying/bluepy`) is the GitHub deploy source. `tangled` is a mirror. +- `fork` (`github.com/aliceisjustplaying/bluepy`) is the GitHub deploy source; `tangled` is a mirror. - Production deploys from `fork/bluesky`, not feature branches. Use `/tmp/bluepy-bluesky-deploy` or another clean worktree for deploy-target work. - Cloudflare account: `aliceisjustplaying@gmail.com`, account ID `b752c979e541327de3e87e52f0906aa1`, zone `bluepy.social` id `c3e3ebea11871d784375b74624d3b6cd`. -- Workers: - - `bluepy` -> `bluepy.social` (prod). Do not touch unless asked. - - `bluepy-dev` -> `dev.bluepy.social` (`bunx wrangler deploy --env dev`). - - PR previews -> `https://pr-.bluepy.social` via `.github/workflows/bluepy-preview.yml`. +- Workers: `bluepy` → `bluepy.social` (prod, do not touch unless asked) · `bluepy-dev` → `dev.bluepy.social` (`bunx wrangler deploy --env dev`) · PR previews → `https://pr-.bluepy.social` via `.github/workflows/bluepy-preview.yml`. ## PR Automation -- Issue label `agent:codex` runs `.github/workflows/bluepy-agent.yml`, which calls `scripts/agent-loop.sh issue `. +- Issue label `agent:codex` runs `.github/workflows/bluepy-agent.yml` → `scripts/agent-loop.sh issue `. - PRs with `agent:preview` get a preview deploy on open, sync, reopen, ready-for-review, or label events. -- After a successful `Bluepy Preview` run, `.github/workflows/bluepy-agent-pr.yml` calls `scripts/agent-loop.sh pr ` unless the PR already has `human-review`. +- After a successful `Bluepy Preview` run, `.github/workflows/bluepy-agent-pr.yml` → `scripts/agent-loop.sh pr ` unless the PR already has `human-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-agent` comments from owners, members, or collaborators run `scripts/agent-loop.sh comment `. - Self-hosted preview jobs require runners tagged `bluepy-deploy`; agent jobs require `bluepy-agent`. -- Agent workflow logs live under `/workspace/agent-worktrees/bluepy/logs` and are uploaded as GitHub artifacts. Claude raw and normalized review JSON are preserved there. +- Agent workflow logs live under `/workspace/agent-worktrees/bluepy/logs` and are uploaded as GitHub artifacts (raw + normalized review JSON preserved). ## Environment Quirks -- Use authenticated views when checking visual or behavioral regressions. Logged-out routes are not correctness evidence for timeline, post, compose, notification, or account flows. - Agent-runner browser checks inherit `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH` and `AGENT_BROWSER_EXECUTABLE_PATH` from `scripts/agent-loop.sh`; both point at `scripts/chromium-for-agents`. -- Use `agent-browser` for ad-hoc visual checks (`agent-browser skills get core --full`). Use `bun run test` for Playwright smoke/regression tests. -- Playwright browsers are installed via Nix, not `npx playwright install`. If browser launch fails on NixOS, point Playwright at the Nix-provided Chromium wrapper. +- Use `agent-browser` for ad-hoc visual checks (`agent-browser skills get core --full`); `bun run test` for 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.js` and leave "Bluepy is still loading...". - Host is Hermes (Hetzner arm64, 4cpu/8gb). If CPU is hot, inspect `hermes`, `codex`, `claude`, `camoufox`, and `agent-browser` processes before killing anything. -## Verification - -- `bun run typecheck` gates normal code changes. -- `bun run test` gates modules with Playwright coverage. -- `bun run build` gates bundling, routing, import, asset, and runtime packaging changes. -- `bunx oxlint ` gates changed-file delta; the project baseline still has unrelated noise. -- Browser verification must be logged in for authenticated flows: `source ~/.secrets/bluepy/source.env`, `bun run dev`, expose via HTTPS, then walk the affected flow. - ## Models - Claude: `claude-opus-4-7` with `--effort xhigh`. Never Sonnet, Haiku, or defaults. - Codex: `gpt-5.5` with `model_reasoning_effort="high"`. Never defaults. - External manual reviewer of record: GPT-5.5 Pro via `scripts/dump-source-for-review.sh`. -## Lint & Code Discipline +## Domain Rules -- Do not turn off lint rules without explicit permission in the current turn. -- If a rule blocks useful work, report the rule name, diagnostic, and smallest compliant change. For a real rule change, write `docs/rule-change-proposals/YYYY-MM-DD-name.md` and get reviewer approval first. -- Reuse `@atproto/*` types before defining new ones. -- "Type X" means convert X to TS, not patch JSDoc into `.js`. +- AT URIs are native: serialize as `at://…`, never `at:/…`. - Do not invent types that codify known-broken behavior. -- AT URIs are native: serialize as `at://...`, never `at:/...`. - Bluesky image limits: 2 MB and about 4000 px long edge. - -## Review Loop - -Claude writes, verifies, stages, and fixes. Codex reviews read-only through structured output. Fix actionable findings in code, re-run relevant verification, and re-review until Codex reports no actionable findings. Hard cap is 5 review rounds. - -### Codex Review CLI - -Run from the staged worktree. `< /dev/null` is mandatory in non-TTY contexts. - -```bash -DIFF="$(git diff --no-color HEAD)" -TYPECHECK="$(bun run typecheck 2>&1)" -LINT="$(bunx oxlint 2>&1)" -codex exec \ - -c model='"gpt-5.5"' \ - -c model_reasoning_effort='"high"' \ - --dangerously-bypass-approvals-and-sandbox \ - --skip-git-repo-check \ - "$PROMPT" < /dev/null > codex-review.out 2>&1 -``` - -The prompt should ask for 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, and skipped tests. If none, Codex should say "no actionable findings" and list residual risks.