Your team already has the ship-a-feature playbook — the worked recipe for writing a spec, getting the plan first, letting Claude implement, proving the new behaviour with tests, and committing in clean chunks. This module is the layer above the recipe. It’s where you master why each step happens before the next — the spec that gives you the test criteria, the plan that catches the wrong approach while it’s still prose, the implementation that’s proved rather than assumed, the diff you read as the author of record — and prove, against a real rubric, that you can hold the loop together.
“A plan you didn’t read is a diff you’ll have to unwind.” The whole value of plan mode evaporates if you approve the plan unread, and the whole value of the spec evaporates if the tests don’t map to it. The judgment this module teaches is knowing where in the loop the leverage is and what to verify at each gate.
The spec — testable criteria, not aspirational prose
The playbook asks you to write “4–6 acceptance criteria phrased as testable statements.” Mastery is the judgment that distinguishes criteria you can turn into tests from criteria that sound precise and aren’t.
- A testable criterion names the observable outcome. “The invoice PDF is generated” is not testable — it doesn’t say how you’d know it was generated, what format it should be, or what error it should return if generation fails. “A POST to
/invoices/:id/pdfreturns a 200 withContent-Type: application/pdfand the PDF body within 2 seconds; if generation fails it returns{ error: 'pdf_generation_failed', code: 'PDF_ERR' }” is testable — it’s a test you can write right now. - The out-of-scope list is doing real work. Naming what you’re not building now is the cheapest way to stop scope creep. “Out of scope: password-protected PDFs, custom page sizes, batch export” means Claude doesn’t quietly add PDF password protection “since we’re in here.” The non-goals list is the contract that keeps a two-file feature from becoming a nine-file one.
- Acceptance criteria are the source of truth the tests inherit. When you write the tests in step four, you test against the criteria you wrote in step one — not against the implementation that emerged. If you can’t trace each test back to a criterion, the tests are testing the code, not the feature.
- A spec you can’t express in criteria isn’t defined yet. If you struggle to write testable acceptance criteria, the signal is that the feature isn’t yet defined clearly enough to build — not that you should start coding and figure it out. “I’m not sure what ‘done’ looks like” is a spec problem, not an implementation problem. Resolve it in prose before a line is written.
The plan review — where the leverage is
The playbook says to use plan mode and review the plan before approving it. Mastery is the judgment behind what a useful plan review looks like — and the failure mode here is treating approval as a formality.
- A plan is cheap; a diff is not. Correcting a wrong approach in a paragraph costs a minute. Unwinding a wrong 300-line diff costs an afternoon. The whole value of plan mode is that you get the approach wrong cheaply, in prose, before any code exists. A plan review that doesn’t push back on anything is not a review; it’s a ceremony. Read it like a design review — “this touches the auth path, don’t”; “put this behind the existing flag”; “split the migration out” — and say those things before approving.
- Plans have failure modes of their own. “Files to touch: [list of every file in the repo]” is a plan that’s going to produce a diff you can’t review. “No test strategy mentioned” is a plan that will produce untested code. “Changes the auth path” on a feature that shouldn’t touch auth is a plan that should be sent back. Reading the plan means checking the scope (does it match the spec?), the approach (is this the right abstraction?), and the test strategy (where are the tests going and what are they testing?).
- Correcting the plan is the highest-leverage minute in the loop. A plan that touches the wrong module, proposes the wrong abstraction, or misses the concurrency risk is correctable in one sentence while it’s prose. Once it’s a diff, correcting it requires reading every changed line, understanding what the wrong abstraction built, and then undoing it. The minute you spend pushing back on the plan is worth an hour downstream.
- The plan gates the implementation. Don’t say “the plan looks right, go ahead” if you haven’t read it. You’re approving the approach, not the tone. Once the plan is approved, the implementation that follows is the plan you signed off on — which means if the diff comes back wrong, the problem started in the plan review.
The implementation — accept or reject as you go, not at the end
The playbook notes that in Claude Desktop, changes come back as visual diffs you accept or reject. Mastery is the discipline of using that loop correctly — not rubber-stamping the wall of code at the end.
- Review each diff before accepting it. The diff-as-it-builds model exists for a reason: you’re the author of record on every line, and accepting a diff you haven’t read is the same as writing code you haven’t read. In Claude Desktop each change lands as a visual diff in the file pane — read it and accept or reject before Claude moves on.
- If a diff drifts from the plan, reject it now. If a change introduces code that wasn’t in the plan, or touches a file the plan didn’t mention, or changes behaviour outside the spec’s scope — reject it and explain why. Don’t accept it and plan to fix it later. “Accept for now, fix later” is how scopes grow and diffs become unmanageable.
- The plan is the spec for the diff. Every change in the implementation should trace to a step in the plan. If a diff introduces something the plan didn’t mention, that’s either a gap in the plan (missed dependency) or an unplanned addition (scope creep). Name which one before deciding whether to accept.
The tests — “it passes” is not “it’s done”
The playbook asks for tests that pin the new behaviour against each acceptance criterion. Mastery is the judgment that distinguishes tests that prove the feature from tests that merely pass.
- A test that tests the wrong thing is worse than no test. A test that checks whether a function was called (rather than whether the output was right) is a test that will pass on a broken implementation. A test that checks the happy path but misses the error path is a test that will go green even when the error handling is wrong. Green does not mean correct; it means the tests passed, not that the tests are testing the right thing.
- Cross-check the tests against the criteria. After Claude writes the tests, check each one against the acceptance criteria you wrote in step one. Can you trace each test to a criterion? If a test doesn’t map to a criterion, it’s either testing an implementation detail (delete it) or testing a criterion you forgot to write (add the criterion). If a criterion has no test, the test coverage is incomplete, not the criterion.
- Edge cases the criteria implied. A good set of tests covers the criteria you wrote plus the obvious edge cases the criteria implied: empty input, boundary values, the error path, the concurrent case. Those aren’t additions to the spec; they’re the precision the spec was written at.
- The suite running is the Power Track step. In Claude Desktop you see the tests written and can read them. Running the full suite (unit + E2E) needs a terminal-enabled session — the Power Track. If you’re not in a terminal session, read the tests carefully, then run them yourself and paste any red back into the Desktop chat; Claude fixes from the actual output the same way.
Your assignment
Complete the full feature-shipping loop for one real ticket — your own codebase (recommended) or the Mizan “send invoice as PDF” feature worked through this module. Open the project folder in Claude Desktop with the project’s CLAUDE.md in place (from Module 1) and work in the chat — no terminal needed until you run the suite.
Module 3 deliverable — shipped feature
1. Spec (half a page)
- one-paragraph summary of what the feature does and who it's for
- 4–6 acceptance criteria as testable statements (observable outcomes, not
aspirational descriptions)
- explicit "out of scope for now" list
2. Reviewed plan (demonstrable in the session or as a note)
- the plan you received, your pushback (even if "approved as-is"), and the
final approach you signed off on
- at minimum: the files to touch, the order, and the test strategy
3. Implementation with tests (the code + test file)
- implementation that matches the plan you approved
- tests that map to the acceptance criteria (one-to-one traceable)
- suite green (or the red output and your explanation of what's failing)
4. Clean commits + PR description (git log + PR description draft)
- one concern per commit with a why body (Module 2 convention)
- PR description: what the feature does, the acceptance criteria, how you
tested it
How it’s graded — the rubric
Shipping rubric
1. Spec has testable Each acceptance criterion names an observable outcome
criteria you could verify in a test. The out-of-scope list is
present and specific.
2. Plan was critiqued Evidence that the plan was read and a judgment was
before code made — even "approved as-is" with a reason is valid.
The plan matches what was built.
3. Tests pin the Each test maps to a criterion. The suite is green or
acceptance criteria the red is explained. No tests test implementation
details; each tests an observable outcome.
4. Full diff was read No "accept and fix later" — each change was read
before it landed. Drift from the plan was caught and
named.
5. Commits follow One concern per commit, why body present, messages
the convention follow the CLAUDE.md format from Module 1.
The bar, shown — a worked model answer (Mizan)
Spec — Mizan "send invoice as PDF" feature
Summary: When a client requests an invoice in PDF format (via the API or the
dashboard), Mizan generates a PDF that matches the web invoice view, attaches
it to a Resend email, and delivers it to the client's billing email address.
The PDF supports both the English and Arabic invoice templates.
Acceptance criteria:
1. POST /invoices/:id/pdf returns 200 with Content-Type: application/pdf
and the PDF body within 3 seconds for a typical invoice (≤20 line items).
2. GET /invoices/:id/pdf-status returns { status: 'ready' | 'generating' |
'failed', url?: string } — the `url` is present only when status is 'ready'.
3. A PDF sent to the client's billing email arrives with subject
"Invoice [invoice.number] from Mizan" and the PDF attached.
4. Arabic invoices render with RTL layout, Gulf Arabic register, and the
Mizan Arabic logo (src/assets/logo-ar.svg).
5. If generation fails, the API returns { error: 'pdf_generation_failed',
code: 'PDF_ERR' } and no email is sent; the failure is logged to
invoice_events with event_type: 'pdf_generation_failed'.
6. A duplicate request for the same invoice (within 60 seconds) returns
the cached PDF without re-generating.
Out of scope: password-protected PDFs, custom page sizes, batch PDF export,
PDF preview in the dashboard (separate ticket #219).
Tests — Mizan "send invoice as PDF" (excerpt, maps to criteria)
// Criterion 1: returns PDF within 3s
it('returns PDF body for a valid invoice within 3 seconds', async () => {
const start = performance.now()
const res = await POST('/invoices/inv_001/pdf', { headers: auth })
expect(res.status).toBe(200)
expect(res.headers.get('content-type')).toBe('application/pdf')
expect(performance.now() - start).toBeLessThan(3000)
})
// Criterion 5: failure path
it('returns PDF_ERR and does not send email when generation fails', async () => {
mockPdfGenerator.mockRejectedValue(new Error('render failed'))
const res = await POST('/invoices/inv_002/pdf', { headers: auth })
expect(res.status).toBe(500)
expect(await res.json()).toMatchObject({ error: 'pdf_generation_failed', code: 'PDF_ERR' })
expect(mockMailer.send).not.toHaveBeenCalled()
expect(mockEventLog).toHaveBeenCalledWith(
expect.objectContaining({ event_type: 'pdf_generation_failed' })
)
})
What you’ve proven — and what’s next
Clear the rubric and you’ve demonstrated the full feature-shipping loop: a spec that gave you the test criteria, a plan you critiqued before the code existed, an implementation proved by the suite, and a diff you owned line by line.
Module 4 — safe change is next: the refactor discipline that makes structural cleanup something you can stand behind in review, with the test suite as the seatbelt. The CLAUDE.md from Module 1 and the commit convention from Module 2 both carry forward into the refactor commits.