Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions .claude/commands/feature.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,26 @@ $ARGUMENTS

**The prompt is the context — read the intent.** Autonomy, scope, which packages and tiers, whether to confirm before merging: infer it from the words. "Just ship it" → run start-to-finish, decide everything yourself, merge on green, surfacing decisions in the PR body instead of asking. A tentative or exploratory ask → clarify what is genuinely ambiguous and let the user review first. Don't make the user configure you. Always stop for a true blocker: a destructive or irreversible action, a **shipped error code** you would have to change (they are stable forever), a design that needs a ninth primitive or a new tier, or a dependency you cannot satisfy.

**One sweep, one PR, merged before the next sweep starts.** A sweep is one wave of agents. When the wave reports: gate it, commit it, open its PR, get CI green, merge it — and only then brief the next wave, which starts from the new `main`. Never let several waves pile up uncommitted in the tree and then cut them into a stack of PRs at the end. Plan 101 did that (2026-10-01): ~1,450 files across 13 stacked PRs. Each chunk failed CI on its own, because a lower tier's change landed before its consumers. CodeRabbit auto-reviews only PRs whose base is `main`, so it skipped 12 of the 13 until triggered by hand. The real verdict came only from the top of the stack, and every review fix had to be merged up through every branch above it. Size each wave so its PR stays under the cap below. Do not wait on a CodeRabbit review that has not arrived when CI is green; act on reviews that do arrive.
**One PR at a time: build → CI → merge → next.** The loop is fixed, and it is the only one:

**Pick the PR mode before briefing anyone.** **Slice-per-PR** (default) — one concern per PR; packages release independently, so slices do too. **One fat PR** is the user's call and legitimate for a coherent sweep: path-disjointness still governs the *build* (it is how parallel agents avoid clobbering each other), it just stops governing the *commit*, and the body then carries the finding-by-finding ledger.
1. **Cut one PR's worth of work** — at most ~100 changed files — from the lowest unmerged tier.
2. **Several agents build that one PR together**, each on a disjoint file set, in this checkout.
3. **Gate it** (`bun run verify`), commit, push, open the PR.
4. **CI green, then merge.** Nothing else is in flight while it runs.
5. **Only then** `git checkout main && git pull`, branch again, and brief the next PR's agents from the new `main`.

**Cap a PR at ~110–120 files.** CodeRabbit refuses outright above 150 changed files, so the biggest, riskiest PR gets the *least* automated review; a human cannot hold 279 files either, so approval becomes a formality. One red job blocks everything — CI runs lint, typecheck, boundaries and tests as separate jobs, and a single tier violation would hold every unrelated fix hostage. Bisecting later lands on one enormous commit. Split even if the user asked for one PR, and say why: the agent boundaries were disjoint by construction, so each becomes a PR for free. **Tier order is the split order** — land the tier-0/1 change first, then the packages above it adopt it. Never the reverse; imports only go down.
Never two PRs open, never a stack, never a second wave building while the first waits on CI. Never let several waves pile up uncommitted in the tree and then cut them into a stack at the end. Plan 101 did that (2026-10-01): ~1,450 files across 13 stacked PRs. Each chunk failed CI on its own, because a lower tier's change landed before its consumers. CodeRabbit auto-reviews only PRs whose base is `main`, so it skipped 12 of the 13 until triggered by hand. The real verdict came only from the top of the stack, and every review fix had to be merged up through every branch above it. Do not wait on a CodeRabbit review that has not arrived when CI is green; act on reviews that do arrive.

**Cap a PR at ~100 changed files.** A plan slice larger than that is split into consecutive PRs; several small slices of one tier band may share a PR while the total stays under the cap. CodeRabbit refuses outright above 150 changed files, so the biggest, riskiest PR gets the *least* automated review; a human cannot hold 279 files either, so approval becomes a formality. One red job blocks everything — a single tier violation would hold every unrelated fix hostage — and bisecting later lands on one enormous commit. Size the PR **before briefing**, from the slice's file table, so the tree only ever holds one PR's work: the gate proves the tree it ran on, and a green `bun run verify` over two PRs' changes proves neither alone. Count again before committing (`git status --short | wc -l`); if the build still overran the cap, move the higher-tier paths out (`cp` them to the scratchpad, `git checkout --` the originals), **re-run the gate on the exact tree being committed**, and restore them for the next PR. Split even if the user asked for one PR, and say why. **Tier order is the PR order** — land the tier-0/1 change first, then the packages above it adopt it. Never the reverse; imports only go down.

## Work as a hive mind, in one checkout

**Whether to hive is a judgement call, not a ritual.** Two things justify it: **searching** (a broad sweep where you want conclusions, not file dumps) and **scale** (independent, path-separable work — 28 packages make that common). Everything else should not hive. A single-file fix or one bug with an obvious home: do it yourself; briefing, collision management and report-reading cost more than the change, and you pay it in the one context that must survive to the merge.

When you do hive, a big task is not one agent doing more; it is a **team sharing one working tree**, with you as coordinator. **Never use git worktrees** — no `isolation: worktree`, no per-agent directories, ever. They fragment the tree, hide half-finished work from the gate, and each one needs its own `bun install`, its own `tsc -b` build graph and its own regenerated manifest. One checkout, many hands; the file set is the only lock.

- **One chunk, one PR, up to four workers inside it.** Split the problem into chunks, where a chunk is the unit that becomes a single PR; parallelise *inside* the chunk across agents whose file sets are disjoint. Finish and merge a chunk before opening the next one — `claudetm merge-pr` operates on the current directory, so parallel building is fine and parallel merging is not.
- **The hive is you plus at most 4 workers, concurrently.** Four is the ceiling, not the target: size the wave to the work you actually estimated, and one agent is the right answer more often than four. Extra agents past the real parallelism buy nothing and cost briefing, collision mediation and report-reading — all paid from the one context that must survive to the merge. When a chunk has more slices than workers, queue them: a worker that reports is re-tasked with `SendMessage`, keeping its context and its file lock, rather than spawned alongside.
- **One PR, up to four workers inside it.** Parallelism lives *inside* the PR, across agents whose file sets are disjoint — never across PRs. The next PR's agents are not briefed until this one is merged and `main` is pulled.
- **The hive is you plus at most 4 workers, concurrently.** Four is the ceiling, not the target: size the wave to the work you actually estimated, and one agent is the right answer more often than four. Extra agents past the real parallelism buy nothing and cost briefing, collision mediation and report-reading — all paid from the one context that must survive to the merge. When a PR has more slices than workers, queue them: a worker that reports is re-tasked with `SendMessage`, keeping its context and its file lock, rather than spawned alongside.
- **Only you spawn agents.** A worker does its slice and reports; it never delegates further. Nested fan-out makes the live count unknowable and breaks the disjointness guarantee — two grandchildren you never briefed end up editing one file. A worker that finds its slice too large says so and returns; widening the split is your call, not its own.
- **You coordinate; you do not code.** You own git, the ledger and the merge, and you are the only participant who must survive to the end — spend your context on routing, not on reading files an agent will report back. Editing `packages/core/` yourself means you took a slice from someone who had room for it.
- **The file set is the lock.** Every brief names that agent's exclusive paths *and* what every other live agent holds — here that is naturally `packages/<name>/`, which makes clean boundaries cheap. An agent needing a file it does not own **stops and reports the collision**; it never edits across the line and never negotiates peer-to-peer. You mediate: hand the change to the owner, or re-cut the boundary.
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@ jobs:
jq -r '
(.steps // [])[]
| "\(if .skipped then "-" elif .ok then "✓" else "✗" end) \(.name) \(.durationMs)ms\(if .tests then " \(.tests.ran) ran, \(.tests.skipped) skipped\(if .tests.errors then ", \(.tests.errors) errored" else "" end)" else "" end)",
(.findings[]? | " \(.code)\(if .at then " (\(.at))" else "" end)\n cause: \(.cause)\n fix: \(.fix)")
(.findings[]? | " \(.code)\(if .at then " (\(.at))" else "" end)\n cause: \(.cause)\n fix: \(.fix)"),
(select(.ok | not) | .output // empty | " output (last lines):\n\(split("\n")[-40:] | map(" " + .) | join("\n"))")
' "$parts/${PART}.json" || true
exit "$status"
# Uploaded when the part is RED too: the verdict is the merge's, and a part that failed
Expand Down
Loading
Loading