Skip to content

The Compound Engineering loop, and where /ce-plan and /ce-work actually fit

Agents that start typing immediately produce work nobody can review. Plan in a file, execute against it, then write down what you learned so the next pass is shorter.

Next.js on Vercel4 min readships at docs/solutions/nextjs-vercel/compound-engineering-loop.md

Tags: workflow · planning · compound-engineering · agents · process

Here is a session that happens to everyone about a week into working with a coding agent.

You ask for something reasonable ("add team invites") and forty minutes later you have a diff touching nineteen files. Some of it is good. Some of it solves a problem you do not have. There is a new dependency you did not agree to and a database column you cannot explain. Reviewing it costs more than writing it would have. So you throw it away, ask again more carefully, and get a different nineteen files.

The reflex is to blame the model. The actual problem is structural: the request never became a written artefact, so there was no point at which anyone could disagree cheaply. Every disagreement had to be expressed as a rewrite.

Compound Engineering is the fix, and it is not complicated: make the thinking a file, execute against the file, then write down what you learned. The compounding is the third step, which is the one everybody drops.

The wrong way: conversation as the only artefact

you:   add team invites
agent: [starts editing src/, invents a schema, picks an email template,
        decides invites expire in 24 hours]
you:   no, seven days, and only owners can invite
agent: [rewrites]
you:   also we already have a workspace_members table
agent: [rewrites again]

Three things are wrong here, and none is about model quality.

The decisions (expiry, permissions, existing schema) surfaced as code, which is the most expensive form. Nothing survives the session: tomorrow the same questions get answered differently, because the answers live in a chat log nobody will reopen. And there is no verification standard, because "add team invites" cannot be checked. Done is whenever both parties get tired.

The right way: four artefacts, in order

1. A spec, in docs/plans/. Run /write-spec. It reads the code first, asks the five questions only a human can answer, and writes a file:

---
title: Team invites for workspace owners
status: draft
---

## Problem
Owners can only add members who already have an account...

## Scope
In: email invite, 7-day link, seat check, revoke.
Out: bulk CSV import, SSO provisioning, invite reminders.

## Behaviour
Given an owner on a workspace with 3 of 5 seats used
When they invite a new member by email
Then an invite row is created with status "pending"
And the seat count shows 4 of 5 immediately

The Out list is the most valuable part. It is a decision, recorded, that stops the change doubling in week two.

A human reads this and sets status: approved. That pause is the whole mechanism, and in team mode the plan-gate hook makes it structural: edits under src/ are refused until an approved plan exists.

2. An ordered plan: /ce-plan. It takes the approved spec and turns it into steps with files attached: which migration first, which handler next, which test proves each one. It resolves the sequencing questions ("migration before the code that reads it") while they are still free to change.

3. Execution: /ce-work. It implements the plan, in order, and stops when the plan is done rather than when it runs out of ideas. Because the plan names files and acceptance criteria, "done" is checkable by someone who was not in the conversation. The guard hooks run underneath: auto-lint on each edit, enforce-typecheck if it reaches for a bare tsc, env-leak-detector if a credential appears.

4. Verification. /qa-feature walks the acceptance criteria, breaks the feature deliberately, and reports pass / fail / not tested. Then pr-reviewer reviews the diff against this repo's rules, not against generic taste.

The step everyone skips

/ce-compound, or the documentarian agent, writes what was learned into docs/solutions/. One file, one problem, frontmatter so it is findable:

---
title: Invite links break when the app URL has a trailing slash
summary: NEXT_PUBLIC_APP_URL is concatenated, not joined, so a trailing slash produces a double-slash path that the middleware matcher misses.
tags: [nextjs, env, middleware, invites]
---

Skipping this is why the loop stops compounding. Without it you have a nice process that costs you a planning step and returns nothing. With it, the next person who hits that problem finds the answer in twenty seconds, and "the next person" is usually you, or an agent reading docs/solutions/ at the start of a session.

When the same correction happens twice, escalate it from a doc to a rule. Ask the system-manager agent to write a path-scoped rule, and the mistake stops being available rather than being caught again.

What this actually costs

Ten to twenty minutes of planning per unit of work. It buys back the review cost of a diff nobody understands, the rework from decisions surfacing as code, and the third time somebody solves the same problem.

The exemption is real work: a typo, a copy change, a one-line fix with an obvious verification. Planning a two-line change is bureaucracy, and bureaucracy is how a good process gets abandoned. The test is whether a second engineer could implement it and produce roughly the same thing. If yes, skip the plan.

The loop, end to end

request
  -> /write-spec        docs/plans/<date>-<slug>.md, status: draft
  -> human approval     status: approved   (plan-gate opens)
  -> /ce-plan           ordered steps, files named
  -> /ce-work           implementation
  -> /qa-feature        evidence, not "it works"
  -> pr-reviewer        against this repo's rules
  -> /deploy-to-vercel  preview, promote, watch
  -> /ce-compound       docs/solutions/, and a rule if it repeats

Seven steps, and the last one is the only one that makes the next pass shorter. A team that runs the first six has a tidy process. A team that runs all seven has a repo that gets easier to work in every month, which is the entire point of the word "compound".