Skip to main content

Lab template

Copy the block below into docs/labs/lab-N-slug.mdx, replace every placeholder, and delete what you don't need. The order is fixed: every lab in a workshop has the same anatomy, so a learner who has done one lab knows how to read the rest.

Before you start, add the lab to src/data/labs.ts. <LabMeta> and the labs index read from there, and the frontmatter title must match the title field exactly (with the "Lab N — " prefix added).

Lab 5, Hooks, is the model. When the template and Lab 5 disagree, Lab 5 wins.

The template​

docs/labs/lab-N-slug.mdx
---
title: "Lab N — Title in sentence case"
sidebar_label: "Lab N · Short"
description: One sentence. What the learner does and what they find out.
---

# Lab N — Title in sentence case

<LabVideo />

<LabMeta lab={N} />

## Why this matters

Two to four short paragraphs. Where the previous lab left the learner, what
still goes wrong, and who it goes wrong for. Name a person from the scenario and
a consequence they would feel.

<Persona who="ruth">
One or two sentences in their voice. Only if it earns its place.
</Persona>

One paragraph on what this lab's mechanism is, in plain words, and why it
answers the person above.

<TryThis
title="Short imperative title"
lead="One sentence on what to do in the tool and what to notice. No key, no install."
href="/playground/tool-slug">
<PlaygroundComponent />
</TryThis>

## Objectives

- Verb-first objective the learner can check off
- Verb-first objective
- Verb-first objective, including the break-it step
- Verb-first objective
- Verb-first objective (4 to 5 in total)

### The idea in one paragraph

The mechanism in five or six sentences: where it lives, what it reads, what it
decides, and the single most common way to get it wrong.

<FileTree
title="What you add in this lab"
tree={`
.factory/
new-file.json [new]
existing-file.json [edit]
`}
/>

## Step 1 — see the gap

Show the problem this lab solves, with the harness as it stands.

<Terminal
title="project-dir"
lines={['npm run scoreboard -- --task T2 --runs 3']}
/>

<SessionTranscript
title="T2 · run 2 of 3"
mode="droid exec --auto medium"
turns={[
{type: 'tool', name: 'Read', input: 'AGENTS.md'},
{type: 'droid', text: 'What Droid said just before the mistake.'},
{type: 'tool', name: 'Edit', input: 'path/to/file — the change'},
]}
/>

<Q n={1}>
A question the learner answers in their notes. It asks them to explain, predict
or decide, not to recall.
</Q>

## Step 2 — build the first version

One thing per step. Every config or script block has a title with its path.

```json title=".factory/example.json"
{
"key": "value"
}
```

Test it without the agent first, if you can.

<Terminal
lines={['echo sample-input | node .factory/hooks/example.mjs; echo "exit $?"']}
output={`expected output
exit 2`}
/>

<Q n={2}>
A question about a failure mode that produces no error message.
</Q>

## Step 3 — break it on purpose

Give the agent a task that pushes toward the thing the control prevents, and
watch it find the way around. Show the transcript. Then revert.

<Q n={3}>
What did the first version actually guard: the outcome, or one path to it?
</Q>

## Step 4 — close the hole

The fix, and an honest sentence about what the fix still misses.

<KnowledgeCheck
questions={[
{
q: 'A question with one clearly right answer.',
options: ['Option A', 'Option B (right)', 'Option C'],
answer: 1,
explain: 'Why B is right, and what the wrong answers get wrong.',
},
{
q: 'A second question.',
options: ['Option A (right)', 'Option B', 'Option C'],
answer: 0,
explain: 'The explanation is the point of the check.',
},
]}
/>

## Step 5 — the step that proves it

<SessionReceipt
title="Step 5 · interactive session"
rows={[
['tool calls', 9],
['blocked', 1],
['protected files changed at end', 0],
]}
flag={['protected files changed at end']}
note="One sentence on what the numbers show."
/>

<Q n={4}>
A question the learner would need to answer for someone skeptical.
</Q>

## Step 6 — re-run the scoreboard

<Terminal lines={['npm run scoreboard']} />

<ScoreboardDelta lab={N} />

Explain what moved and why. If nothing should move, say why, and why running it
anyway matters. If something got worse, that is the most useful paragraph in
the lab.

<Q n={5}>
A question about the trade-off the scoreboard just revealed.
</Q>

## Checkpoint

<Checkpoint
id="lab-N"
items={[
'A concrete, checkable thing the learner has done',
'Each item is something you could verify on their machine',
'Include the break-it step',
'Scoreboard re-run, and they can explain the result',
]}>
One closing question that ties the lab back to the scenario.
</Checkpoint>

## Extensions

1. **Short bold title.** A self-contained extra, with enough context to do it
without re-reading the lab.
2. **Short bold title.** One that connects to a later lab.
3. **Short bold title.** One that takes the idea to the learner's own repo.

What each section is for​

Frontmatter and title​

title must match src/data/labs.ts with "Lab N — " in front. sidebar_label is short, with a middle dot: "Lab 5 · Hooks". description is one sentence for search results and link previews: what the learner does and finds out.

<LabVideo />​

Shows "Recording coming soon" until you pass a youtubeId. Leave it in every lab from day one, so pages don't shift when the recordings land.

<LabMeta lab={N} />​

Time, what the lab builds on, the tag it centers on, and the one idea, all from labs.ts. Don't restate these in prose.

Why this matters​

The most important section and the easiest to get wrong. It is not an introduction to the feature. It says where the previous lab left things, what still goes wrong, and who it goes wrong for. Name a person from the scenario and a consequence they would feel. A <Persona> quote earns its place when the person says something the prose can't: a doubt, a demand, a line the learner will remember.

Try this​

An embedded playground, placed before the objectives on purpose: the learner gets a feel for the idea before being told what it is. Skip it if no playground fits. Don't build a weak one to fill the slot.

Objectives and the idea in one paragraph​

Four or five verb-first objectives the learner could check off, including the break-it step. Then the mechanism in one paragraph: where it lives, what it reads, what it decides, and the most common way to get it wrong. A learner who reads only this paragraph should know what the lab teaches.

<FileTree>​

Include it when the lab adds or changes files. Mark new files [new] and changed files [edit].

Steps​

Five to seven steps. Each does one thing, and its heading says what: "Step 2 — the file-tool guard", not "Step 2 — configuration". Include:

  • Step 1 — see the gap. Show the problem with the current harness before fixing it. A lab that opens with the fix teaches the fix. A lab that opens with the gap teaches why.
  • A break-it step. The learner watches their own control fail. It's the step people remember.
  • Honest limits. After the fix, one or two sentences on what it still misses. The next step or lab usually covers it.

<Q> questions​

Four to six per lab, numbered from 1 within the lab. Answers go in the facilitator keys, not on the page. A good Q asks the learner to explain, predict or decide. "What does X do?" is recall. "What happens, with no error anywhere, if you write edit?" makes them think.

<SessionTranscript> and <SessionReceipt>​

Use a transcript where reading the session is the point: the turn where the agent reasoned its way past a rule, or the hook that stopped it. Use one receipt per lab, for the numbers that tell you what a run actually did. Both show the Illustrative badge by default. Keep it until the pilot replaces them with recorded runs.

<KnowledgeCheck>​

Two or three questions, in the middle or late in the lab. They aren't graded. The explanation that appears after the learner commits to an answer is the point, so write the explanation first and the wrong options second. Make the wrong options things a reasonable person believes.

Re-run the scoreboard​

Every lab has this step, including labs that can't move the number. <ScoreboardDelta> shows the before and after, or a hold message. The prose after it matters more than the table: what moved, why, and what got worse.

Checkpoint​

Four or five items the learner could prove on their own machine, including the break-it step and the scoreboard. The closing question, passed as children, ties the lab back to the scenario. Use id="lab-N": ticks are stored in the browser under that id.

Extensions​

Three or four, numbered. Each is self-contained, with enough context to do it cold. Good extensions connect forward to a later lab, go deeper on something the lab skipped, or take the idea to the learner's own repository.

Length​

Around 2,500 to 4,500 words of source per lab. Lab 5 is about 4,000. If a lab is running long, move a step into an extension before you cut an explanation.

Before you hand it over​

Run node scripts/check-mdx.mjs docs/labs/lab-N-slug.mdx until it passes. Then work through the publishing checklist.