Document the contract and the traps

CONTRACT.md states the twelve rules and, for each, the failure it prevents.
AGENTS.md lists the three directories a demo agent may touch and the seven traps
that have already cost time here — pnpm 11's settings move, the non-portable
RNG, the two-pass rule, the two reward-denominator subtleties, the silent CSP
worker block, and Caddy's silent bind.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019mt6sHQHEnEYrJZvoMCJSB
This commit is contained in:
karti-ai
2026-08-28 15:48:20 -07:00
parent 408ce4a525
commit 2c2dcad9fd
4 changed files with 491 additions and 7 deletions
+64
View File
@@ -0,0 +1,64 @@
# 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.