The first hour of a project decides how the next hundred go. A repo that starts as "just a quick scaffold, discipline later" accumulates features faster than it accumulates gates, and by the time you retrofit verification, every gate you add catches something and the catches are all archaeology. The alternative costs one session: scaffold, harness, hooks, and CI, in that order, before a single feature exists. From this lesson on, Briefcase is a real repo on your machine and everything lands through dispatches.
Day zero, in order
The scaffold. Next.js with the current recommended defaults. As of July 2026 that means:
npx create-next-app@latest briefcase
# choose: Yes, use recommended defaults
# (TypeScript, ESLint, Tailwind CSS, App Router, AGENTS.md)
cd briefcase && npm run devThe recommended defaults give you TypeScript, ESLint, Tailwind, the App Router, and an AGENTS.md with a CLAUDE.md that references it, guidance written by the Next.js team to keep coding agents writing current-idiom Next.js. Keep that file; your own CLAUDE.md rules go alongside its reference, not instead of it. Minimum Node.js is 20.9, and you should be on the LTS you standardized on in Part 0. Verify the scaffold runs before touching anything: npm run dev, load localhost:3000, then stop it. That thirty seconds establishes the only baseline you will ever have where nothing can be your fault.
The harness. You built the personal harness in 3.9; this is its first production installation. Into the fresh repo go: STATUS.md (from the 3.1 template, with "Active work: scaffold dispatch" as its first line), docs/ holding the brief, both specs, and the three ADRs from 4.1 through 4.3, a dispatches/ directory where every capstone dispatch gets committed as an artifact, and a CLAUDE.md that wires it together:
# CLAUDE.md - Briefcase
Read STATUS.md before starting any task.
## Authority
- docs/TECH-SPEC.md is the naming authority: entities, enums,
routes, roles come from it. On any mismatch between spec and
code: STOP, log it in STATUS.md open questions, do not amend
the spec and do not guess.
- Architecture decisions live in docs/adr/. Do not relitigate
them; propose changes as new ADR drafts in STATUS.md.
## Hard rules
- Never read, write, or print .env, .env.local, or any secret.
A hook enforces this; plan around the boundary.
- Migrations are the only schema path (4.5). Never edit a
committed migration; write a new one.
- Verification before any "done" claim:
npm run typecheck && npm run lint && npm test && npm run build
## Conventions
- Next.js App Router idioms per AGENTS.md (scaffold-provided).
- Conventional commits: feat(scope): ..., fix(scope): ...
- Small files; split components over ~200 lines.The secret hook, before the first secret. Order matters here: the hook gets installed and proven while .env.local does not exist yet, so there is never a window where an agent session and an unprotected secret share a machine. This is 3.8's discipline made structural. Two files:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Read|Edit|Write|Bash",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/protect-env.sh\""
}
]
}
]
}
}#!/bin/bash
# Blocks any tool call that touches env files. Exit 2 = block,
# with stderr fed back to the model. Fails closed on odd input.
input=$(cat) || { echo "protect-env: no input" >&2; exit 2; }
# Scan the whole tool input blob: file paths and bash commands.
targets=$(printf '%s' "$input" | tr -d '\n')
if printf '%s' "$targets" | grep -qE '\.env(\.[A-Za-z0-9_.]+)?'; then
echo "BLOCKED: env files are off limits (CLAUDE.md hard rule)." >&2
echo "If you need a config value, ask the operator to add a" >&2
echo "non-secret example to .env.example instead." >&2
exit 2
fi
exit 0Mechanics, verified against the current hooks reference: PreToolUse fires before a tool call executes and receives the tool name and full tool input as JSON on stdin; exit code 2 blocks the call and returns stderr to the model; the matcher filters by tool name, and Read|Edit|Write|Bash covers the file tools plus shell (where a cat .env.local would otherwise stroll through). The grep is deliberately crude and broad: it scans the whole input blob rather than parsing per-tool fields, trading occasional false positives for zero parsing gaps. At this repo's stakes, over-blocking costs a rephrase; under-blocking costs a key.
Make it executable, then prove it the way 3.2 taught you to prove every gate, by watching it fail: chmod +x .claude/hooks/protect-env.sh, start a session, ask Claude to read .env.local, and confirm the block lands. Only after seeing the block do you create .env.local at all. Add .env.example (committed, fake values) as the sanctioned way to communicate shape.
CI before features. An empty pipeline that runs typecheck, lint, and build on every push costs five minutes now and is politically free, because nothing exists to be slowed down by it. Retrofitted in week three, the same pipeline surfaces forty accumulated warnings and an argument with yourself about fixing versus muting. The skeleton:
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: lts/*
cache: npm
- run: npm ci
- run: npm run typecheck
- run: npm run lint
- run: npm run buildAdd the typecheck script the workflow expects ("typecheck": "tsc --noEmit") to package.json. Tests join this file in 4.5 (policy tests) and 4.9 (Playwright); the point today is that the pipe exists and is green, so every later addition is a one-line diff to a working thing.
The dispatch, not the checklist
Everything above could be done by hand in an hour. Do it as a dispatch instead, for two reasons: the artifacts (dispatch file, STATUS.md update, conventional commits) start existing from commit one, and the scaffold dispatch is the cheapest possible rehearsal of the loop you will run for every feature: write dispatch, run it, gate it, commit. Here is the reference dispatch; every capstone lab from here to 4.10 hands you one of these, in exactly the 3.1 shape.
DISPATCH: briefcase scaffold and foundations
GOAL: the briefcase repo is a verified foundation: harness docs installed, env hook proven blocking, CI skeleton green on GitHub, all three verification commands passing locally.
IN SCOPE: CLAUDE.md (from the operator's draft); STATUS.md; docs/BRIEF.md, docs/PRODUCT-SPEC.md, docs/TECH-SPEC.md, docs/adr/001..003 (copy from briefcase-notes, content unchanged); dispatches/ (this file as dispatches/2026-07-scaffold.md); .claude/settings.json and .claude/hooks/protect-env.sh; .github/workflows/ci.yml; package.json (add typecheck script only); .env.example (placeholder keys, fake values); .gitignore (ensure .env* ignored except .env.example).
OUT OF SCOPE: any feature code or UI change to the scaffold; Supabase setup (next dispatch); installing any dependency; editing AGENTS.md; "improving" scaffold defaults (eslint config, tsconfig) in any way.
DON'T TOUCH: docs/* content after the copy (naming authority, operator-owned); package.json dependencies; app/ beyond what create-next-app generated.
VERIFICATION:
- npm run typecheck && npm run lint && npm run build, all green.
- Operator: fresh session, ask to read .env.local; expect the hook block message, then verify with a Bash cat attempt too.
- Operator: push to GitHub, confirm the CI run is green in the Actions tab.
- git log shows conventional commits; git diff --stat against the pre-dispatch commit touches only IN SCOPE paths.
Before writing anything, read STATUS.md and tell me your plan. If any instruction conflicts with what create-next-app generated, stop and say so instead of working around it.
Notice what the dispatch refuses to do: it does not let the agent write the CLAUDE.md rules (you drafted them; the agent installs them), it does not allow dependency installation (nothing yet justifies one), and it keeps the spec copy content-unchanged so the naming authority enters the repo exactly as the operator ratified it in 4.3.
Where it breaks
The tempting shortcut is skipping the harness because the repo is small. But harness cost is front-loaded and flat while its value compounds, and "small repo" is a temporary condition that ends the moment 4.5's schema dispatch lands. The projects that need retrofitting are always the ones that grew, which is to say, exactly the ones where retrofitting is expensive.
The second failure is a hook you never watched fail. An unproven gate is worse than no gate, because you extend trust to it (3.2's lesson). The env hook gets the same treatment as any other gate: a deliberate violation attempt, observed block, then trust.
The third is scaffold "improvement" creep: the agent, or you, deciding the generated eslint config is too strict, the tsconfig needs adjusting, the folder structure should be reorganized. Every deviation from generated defaults is a delta you maintain forever against every framework upgrade. The scaffold is not your code; treat it like a vendored dependency and change it only when a real requirement forces you to, as a logged decision.
Lablab-4-4Capstone dispatch: scaffold
Goal: Stand up the briefcase repo with scaffold, harness, proven env hook, and green CI, entirely through the day-zero sequence and the reference dispatch.
Prereqs: Node LTS (20.9 minimum for Next.js), a GitHub account, your briefcase-notes folder from 4.1 through 4.3, Claude Code.
- Run
npx create-next-app@latest briefcase, accept the recommended defaults, and verify the baseline:npm run dev, page loads, stop it. Commit the untouched scaffold:git commit -m "chore: create-next-app scaffold"(create-next-app already initialized git). - Draft your CLAUDE.md by hand from the reference above, adjusting only paths that differ on your machine. This document is operator-owned; writing it is not delegable.
- Save the reference dispatch as
dispatches/2026-07-scaffold.md, editing the copy-source path to yourbriefcase-noteslocation. Open a fresh Claude Code session in the repo and paste the dispatch. Review the plan against IN SCOPE before approving. - When the agent reports done, run the VERIFICATION section yourself, in order. Item 2 is the one that matters most: see the hook block a Read of
.env.localAND acat .env.localthrough Bash before you put a single real value in that file. - Create the GitHub repo, push, and watch the Actions run go green. Then create
.env.local(it can be empty for now) knowing it was born protected. - Close the loop: STATUS.md updated (just shipped: scaffold; active: 4.5 schema dispatch next), commit,
/clear.
Verify
npm run typecheck && npm run lint && npm run buildall pass locally, and the same three are green in GitHub Actions.- Both violation attempts (Read tool and Bash cat) produced the hook's BLOCKED message in-session, and
.env.localdid not exist until after you saw both blocks. git log --onelineshows the scaffold baseline commit, then conventional commits for the dispatch work;git diff --statbetween them touches only IN SCOPE paths.- STATUS.md names the schema dispatch as active work, and
dispatches/2026-07-scaffold.mdis committed.
>Troubleshooting
- The hook does not fire at all: check that
.claude/settings.jsonis valid JSON (a trailing comma kills it silently) and that the script is executable. Then check the session was started in the repo root, since project settings load from.claude/at the cwd. - The hook blocks everything including innocent files: your grep pattern is looser than the reference (a bare
envwithout the dot will matchsrc/lib/env-check.tsand half your codebase). Match the reference pattern exactly, including the leading\.. - CI fails on
npm ciwith a lockfile mismatch: you (or the agent) touched package.json without running npm install locally. Regenerate the lockfile, and note which dispatch rule that violated (OUT OF SCOPE: installing any dependency).
Knowledge check
Knowledge check
Sources
- Next.js installation (create-next-app recommended defaults including AGENTS.md with CLAUDE.md reference, Node.js 20.9 minimum): https://nextjs.org/docs/app/getting-started/installation (fetched July 2026)
- Claude Code hooks reference (PreToolUse stdin JSON, matcher semantics, exit code 2 blocks with stderr fed to the model): https://code.claude.com/docs/en/hooks (fetched July 2026)
- Playwright CI intro (the actions/checkout@v5 + actions/setup-node@v5 workflow shape the CI skeleton follows): https://playwright.dev/docs/ci-intro (fetched July 2026)
- Best practices for Claude Code (CLAUDE.md guidance, verification-first working style): https://code.claude.com/docs/en/best-practices (fetched July 2026)