1@README.md 2 3## Notes for agents 4 5**Moving to newer Strudel is a rebase, not a merge.** `git fetch strudel main && git branch -f strudel-upstream strudel/main`, rebase `strudel-llm-rebased` onto it, then rebase `jevstrudel`'s own commits (`git rebase --onto strudel-llm-rebased <old strudel-llm-rebased> jevstrudel`). The fork's `Header.jsx` changes live in `website/src/repl/components/panel/Panel.jsx` since upstream deleted that file (2026-09-24). After it, rehash pnpm (`nix/CLAUDE.md`). 6 7**Never push to `strudel` or `strudel-llm`.** Their push URLs are set to `NO_PUSH` on purpose. 8 9**Songs load samples from one relative URL, `jev-samples/strudel.json`.** The site generates it (`website/src/pages/jev-samples/strudel.json.js`), live in the dev server. Never point a song at an absolute or localhost URL again — the static site has no sampler (the separate one on :5432 was removed 2026-09-24). 10 11**Everything jevstrudel adds lives in the jev panel, on the left** (`LeftPanel` in `website/src/repl/components/panel/Panel.jsx`: songs, activity, listening, you, mcp, data; any display name opens that account's profile there, `website/src/jev/NameLink.jsx`), except Jev at work on the playing song, which is the pane under the editor (`website/src/jev/JevPane.jsx`: the booth and the mixer, skeletons until a song plays); the header and the right panel are Strudel's, except ⏭ next and autoplay beside play. **The mcp tab and `/ai/` render one guide, `website/src/jev/ConnectAi.jsx`;** edit it there. **Its data tab shows everything the site stores, secrets aside** (`website/src/jev/DataTab.jsx`, `worker/src/data.ts`): a new store or table joins it. **Its songs tab (the welcome tab) is the song browser.** `website/src/jev/songs.mjs` indexes `songs/*/*/` at build time; a new song appears in the dev server on reload, and on the public site after a deploy. 12 13**Claude plays through the dev server.** `.mcp.json` runs `nix run .#mcp`, a stateless stdio proxy (`tools/mcp/`) to the MCP hub in the jevstrudel Worker, which `buck2 run //:dev` runs under `wrangler dev` (`worker/`). Tool edits in `worker/src` reach Claude Code by themselves (`tools/list_changed`). Starting the dev server is Claude's job: if a strudel tool says it is not running, run `nix develop -c buck2 run //:dev` as a background task and read its output through the task — not detached, not redirected to a file (the user, 2026-09-24). It stops with the session; the next session starts it again. Nothing else needs restarting. Tools take an optional `session_id`, needed only when several tabs are open. 14 15**Never `git stash` or checkout files in the main tree while the dev server runs.** Vite can cache a module mid-swap and keep serving it after the file is restored: the page then fails to hydrate ("does not provide an export named …") on every load until the file is touched (2026-09-25). Compare against HEAD with `git show HEAD:path` instead. 16 17**`.mcp.json` is read only at startup.** It has one server, `strudel`, the stdio proxy; keep it stdio, since an SSE or HTTP entry fails for the whole session if the server isn't up when Claude Code starts (2026-09-24). The proxy talks to the local dev server by default; its `use_environment` tool switches it to the public site's hosted MCP (`prod`) and back (`dev`), and the tool list changes to match. The first switch to prod in a session opens the site's passkey sign-in and consent in the user's Windows browser and waits for it; prod's tools reach only the user's own signed-in tabs on the public site, in the sandbox, and publish under their name. The sign-in lives in the proxy's memory only and is revoked when the session ends (`tools/mcp/`). 18 19**The site is on 4322 (`website/astro.config.mjs`), the Worker on 4323 (`worker/wrangler.json`).** 8080, upstream's default, is Gerrit on this host. The dev server serves from `/`, not `/strudel/`, and its sample map points at `/@fs/…` paths; that is Vite serving the working tree, not a bug. 20 21**A missing sample is silent.** A sound whose file 404s just doesn't play, with no error back through MCP. Check that `curl localhost:4322/jev-samples/strudel.json` lists local sounds, and that dirt-samples names exist (`gh api repos/tidalcycles/Dirt-Samples/contents`) — `oh` does not, `808oh` does (2026-09-24). 22 23**Check songs in a silent headless tab, never the user's.** Open `http://localhost:4322/?session=probe` headless (Playwright) and pass `session_id: 'probe'`: `?session=` pins a tab's id across reloads, and the user's tabs have random ids that play out loud. `get_logs` then shows the song's warnings; raising the tempo in `setcps` runs a whole song in seconds, since warnings depend on the patterns, not the speed. 24 25**`play_code` stops before it plays,** so a song whose sections are absolute-cycle masks always starts at cycle 0, and it returns the evaluation error if the code fails. 26 27**Every deploy gets a release note first.** Add a `## YYYY-MM-DD.N — title` entry at the top of `RELEASES.md`, a line per change a listener would notice; the site shows it in its update prompt, and `nix run .#deploy` refuses when the newest note is already live (`tools/deploy/release-check.mjs`). 28 29**Listen on localhost before deploying.** The user hears changes on the dev server with their own key and says when to `nix run .#deploy`; the public site has listeners. 30 31**Songs go in `songs/<theme>/<song>/`, spec first.** See `songs/CLAUDE.md`. Don't repeat the theme in the song folder name (`songs/jev/typesafe-jungle`, not `songs/jev/jev-typesafe-jungle`). 32 33**Review a song with the `song-review` workflow** (`.claude/workflows/song-review.js`) before the user listens: `Workflow({ name: 'song-review', args: { song: 'jev/<song>', apply: false } })`. Four adversarial lenses (concept and facts, music and mix, code and form, Jev and cost), each finding verified by a skeptic, then a ranked plan; `apply: true` makes the fixes spec-first on a worktree branch. Its checklists hold what each song so far got wrong: add a lesson there when a review pays for one. 34 35**Parallel song agents: one writer for shared state.** Forks in worktrees make a song each and report their `samples/README.md` rows; the parent merges, records the rows, updates `songs/jev/README.md`, and is the only one that plays in the browser (one session, one song at a time). Give every fork a sample-name prefix so new sounds cannot collide. The worktree command guard refuses `nix develop -c bash`, runtime-built commands and the literal word "eval" — forks run `speak`/`vocode` from `nix build .#speak .#vocode` store paths via script files (2026-09-24).