# Setup (docs/operations/setup.md)

Truth document, operations genre (`type: operations`). Use for: **the one-time path from a fresh machine to a first verified run.** It covers what the repo is built on, prerequisites, install, env files, local services, the first commands, proof that it works, and the failures a fresh clone hits. There is **one per repo**, always named `setup.md`, and it is a mandated scaffold. The root README's only setup content is a Destinations row linking it.

It is distinct from `local-development.md`, the daily loop: watch modes, resetting state, per-package traps, the inner test cycle. Setup runs once per machine. Local development runs every session. Setup ends by handing off to it, and neither copies the other. Deploying is `deployment.md`.

Required: **Goal**, **Stack**, **Prerequisites**, **Installation**, **First run**, **Verification**. Optional, included only with real content: the audience fork, **Before cloning**, **Environment files**, **Local services**, **Reset**, **Common failures**.

### Writing rules

- **Never write a version a file already pins.** Each row names the pinning file (`.nvmrc`, `package.json` `engines` / `packageManager`, `rust-toolchain.toml`, `go.mod`) and the command that checks it. A typed number is a second source that drifts on the next bump.
- **Stack is one row per layer.** Give the technology, what it is for in this repo, and where its version is pinned. Why it was chosen belongs to `docs/architecture/`, linked once. Don't repeat the dependency list: the manifests are the roster.
- **One path, from the lockfile.** `pnpm-lock.yaml` gives pnpm, `yarn.lock` gives yarn, otherwise npm. Say how it is activated (`corepack enable`). Never write "or npm / yarn". Give a per-OS variant only where the commands really differ.
- **Traps before commands.** Anything a fresh clone hits before the first command goes in Before cloning, not halfway down the steps: LFS, submodules, a required stub file, an access grant, a credential that has to be requested.
- **Copy-paste runnable, one action per step, each with its expected outcome.** Run non-interactively where the tool allows it, so an agent can execute the step unchanged. "It should work now" is never a step.
- **Script first, then document.** If a bootstrap script or task exists, it is the step, and the doc carries only the judgment around it. A setup sequence run often with no script gets a script.
- **Env files as a table, never a variable dump.** One row per file: where it is copied from, what must be filled, where the values come from, whether it is required to boot. The variable contract is the example file and the code's env type, linked and never copied. Secret values never appear.
- **Destructive commands state their blast radius where they appear:** what is deleted or truncated, whether it prompts, and the flag that skips the prompt.
- **Verification is observable:** a health probe and its expected status, a URL and what it shows, a seeded login, a doctor command and its output, or the test command passing from a cold shell.
- **Common failures are keyed by the exact error text,** followed by the cause and the fix, where the fix is a command or a file edit. Append a failure only once it has been root-caused, never as speculation.
- **Per-package divergence is a subsection of this file.** A unit whose setup differs gets a `### {unit}` block under First run, or a row linking its own procedure. It never gets a second setup doc.

### Template

```markdown
---
type: operations
description: "{at most 300 characters: what this document settles and where its boundary sits}"
---

# Setup

**Goal:** take a fresh machine from clone to a verified first run of {what runs: e.g. "the product Worker on :{port} and the web shell on :{port}, signed in as the dev fixture"}. Done once per machine; the daily loop is [`local-development.md`]({local-development}.md).

{Optional audience fork: "Only running {the CLI / one package}? Stop after Installation and see [`{procedure}`]({path})."}

## Stack

| Layer | Technology | For | Version pinned by |
| --- | --- | --- | --- |
| {Runtime} | {Node} | {what runs on it} | [`.nvmrc`](../../.nvmrc) |
| {Server} | {framework · data layer} | {what it serves} | [`{package.json}`](../../{path}/package.json) |
| {Client} | {framework · build tool · styling} | {which surfaces} | [`{package.json}`](../../{path}/package.json) |
| {Tests} | {runners} | {unit · e2e} | {manifest} |

Why these were chosen: [{system overview}](../architecture/{overview}.md).

## Prerequisites

| Tool | Version from | Check |
| --- | --- | --- |
| {Node} | [`.nvmrc`](../../.nvmrc) | `node -v` |
| {pnpm} | `packageManager` in [`package.json`](../../package.json), via `corepack enable` | `pnpm -v` |
| {Docker / Rust / wrangler / …} | {pinning file, or "any current"} | `{command}` |

- **Access:** {repo, secrets store, cloud account, registry token, and who grants each}.

## Before cloning ← delete if none

- **{Trap}:** {what a fresh clone hits and the one command that clears it, e.g. `git lfs install`, `git submodule update --init`}.

## Installation

\`\`\`bash
git clone {url} && cd {repo}
corepack enable
{pm} install                  # expect: {what a clean install ends with}
\`\`\`

## Environment files ← delete if none

| File | Copy from | Fill | Values come from | Required to boot |
| --- | --- | --- | --- | --- |
| `{path/.dev.vars}` | `{path/.dev.vars.example}` | {keys, or "every blank"} | {vault, the owner, a generated local value} | {yes / no} |

The contract is [`{example file or typed Env}`](../../{path}). Read it before concluding a value is missing. {Which of these files are gitignored.}

## Local services ← delete if none

| Service | Start | Port | Ready when |
| --- | --- | --- | --- |
| {local DB / emulator} | `{command}` | {port} | `{probe}` returns {expected} |

## First run

1. Prepare local state.

   \`\`\`bash
   {pm} --filter {unit} {migrate-and-seed}   # expect: {output}
   \`\`\`

2. Start the {backend}.

   \`\`\`bash
   {pm} --filter {unit} {dev:server}         # expect: listening on :{port}
   \`\`\`

3. Start the {frontend / CLI}.

   \`\`\`bash
   {pm} --filter {unit} {dev}                # expect: {URL}
   \`\`\`

4. Open `{http://localhost:port}` and {sign in as `{seeded user}` / reach `{first screen}`}.

## Verification

- `{curl -fsS http://localhost:{port}/{health}}` returns `200` {and `{body}`}.
- `{doctor or status command}` reports {expected}. ← keep whichever exists
- `{pm} {test}` passes from this cold shell ({expected tail}).

## Reset ← delete if none

| Command | Deletes | Prompts? |
| --- | --- | --- |
| `{pm} {reset-script}` | {local state at `{path}`} | {yes; `{FORCE=1}` skips / no} |

## Common failures

- **`{exact error text}`.** {Cause.} Fix: `{command or file edit}`.

## Next

The daily loop and its per-package traps: [`local-development.md`]({local-development}.md). Deploying: [`deployment.md`](deployment.md). Standing rules: [`CLAUDE.md`](../../CLAUDE.md).
```

### Baseline scaffolding behavior

The baseline scaffolds `docs/operations/setup.md` when it is missing and the repo has a lockfile and at least one runnable root script. It fills each section from the repo:

- **Stack:** from the workspace manifests, one row per layer.
- **Prerequisites:** from the pinning files.
- **Installation and First run:** from the root README's getting-started block (if one exists) and the root scripts.
- **Environment files:** from every `*.example` / `.dev.vars.example` in the workspace.
- **Verification:** from a health route, a doctor command or the test script.

It leaves Common failures empty until a failure is root-caused, and it drops every section it cannot fill concretely.

**It never overwrites an existing `setup.md`.** If the repo already has a first-run procedure under another name, the baseline proposes the rename with a `note:` row and does not create a second file.
