# Working in this repository ## Adding an environment Read **`.claude/skills/new-environment/SKILL.md`**. It is the recipe and, more importantly, the judgement: which gate means "iterate" and which means "this environment does not work". The pipeline that runs it is `.claude/workflows/new-environment.js`. The short version: the specification comes from `src/content/verticals.ts` (`rank: 1` is the one to build next), it gets written up and attacked before any code, the probe ladder is what proves the reward measures something, and the conformance digest is what proves the browser and the environment are playing the same game. ## 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//` - `envs//` - `public/traces//` - 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.