# Working in this repository

## The three commands

```bash
uv sync --all-packages && uv run pytest envs/wordle_five/tests && uv run python envs/probe.py
pnpm install && pnpm check && pnpm build
uv run python envs/verify_fixtures.py
```

## Directories an agent adding a demo may touch

Exactly three, plus one line of content:

- `src/demos/<slug>/`
- `envs/<pkg>/`
- `public/traces/<slug>/`
- one entry in `src/content/verticals.ts`

Anything else is a shared file and a merge conflict waiting to happen. If you
believe you need to edit the shell, the header or the router, you have found a
contract bug — read `CONTRACT.md`, rule 6.

## Traps that have already cost time here

**pnpm 11 does not read the `pnpm` field in `package.json`.** Settings live in
`pnpm-workspace.yaml`, and an unapproved build script makes `pnpm install`
*exit 1* rather than warn.

**A language's built-in RNG is not portable.** `random.Random(seed)` and any
JavaScript PRNG will disagree, so the same seed picks different words on the two
sides and every permalink silently shows a different puzzle than the run it
claims to replay. Both sides use FNV-1a over the decimal seed. `Math.imul` on
the JS side is load-bearing — a plain multiply overflows into a double.

**Scoring must be two passes.** Every green resolves before any yellow. One
pass gets `SASSY`/`BASIS` wrong, and 21.2 million pairs are hashed in CI
precisely so that cannot ship.

**`consistency` is scored over turns SPENT, not guesses accepted.** Counting
only legal guesses hands a free 1.0 to a policy that plays one word and jams the
parser five times: one guess, no contradictions, perfect score.

**`economy`'s denominator is the shipped solver's depth, not a depth-optimal
search.** Entropy-greedy is not depth-optimal, so grading it against a true
optimum makes the oracle rung fail its own probe assertion on some seeds.

**The production CSP has no `worker-src`,** so it falls back to
`default-src 'self'`. A blob-backed worker (Vite's `?worker&inline`) is blocked
in production only, with no console error — the solver simply never boots. CI
greps the bundle for `blob:` for this reason.

**Caddy's `bind 10.0.0.2` on cloud-2 is mandatory and its absence is silent.**
Without it the site answers an empty 200 behind a valid certificate, and a
`--resolve` check from cloud-2 itself still passes. Verify from a third machine
against the real hostname.

**spark-1 serves one model and is single-stream.** Capture is a serialised
queue, not a parallel one. Book it before starting a long sweep.

## Style

Comments explain *why*, and only where the reason is not evident. Never narrate
what the code says. Prefer one honest number over three adjectives.
