# サンドニエット SANDONIETTE — Build Brief 1: The Puppet, The Food, The Samurai

## STATUS (update this as you go — a cold session reads here first)
- **M0 ✅** vite+ts+phaser(3.90)+Matter skeleton, typecheck green, harness (`window.__s`)
  installed. Box drops from y=80, settles in **68 frames**, rests **y=608.05**
  flush on the floor (gap −0.05px, `landed:true`). Verified in-browser + screenshot.
- Stack: Phaser Matter, `autoUpdate:false` — sim steps only via `stage.stepSim(dt)`,
  so the harness drives frame-by-frame. Render contract: raw Matter body = truth,
  flat silhouette `Prop.view` follows it (`Stage.addBody`). `Rng` = mulberry32.
- Assets: `scripts/gen-assets.sh` (2D flux only) firing on MODELBEAST →
  `public/assets/img/`. Log: `~/.jobs/sandoniette-assets.log`. Game never blocks on art.
- **Next: M1** puppet (`sim/marionette.ts`) — the dangle.
- Run: `npm run dev` (:5173). Harness verbs live in `src/dev.ts`.



*"SHUBATTO! SANDO-NI-ETTE!!!" — read this file and execute it. It is written to
work cold: a fresh session with no other context should be able to build this
game from here. Sibling project: `~/Documents/toastsim` — do NOT touch it, but
DO steal its discipline (this brief tells you exactly what to steal).*

## 0. What this game is

A 2D shadow-puppet kitchen. You are a **silhouette marionette chef** operated
by strings — and only by strings. You cut comical food, stack it into
sandwiches, carry the dish across the stage, and serve it to your one
customer: a giant, permanently unimpressed **samurai**, glaring down at the
kitchen like a health inspector from hell. Do badly — drop things, mangle the
cut, stack a leaning tower, serve late — and his katana unsheathes a little
more. At maximum: **SHUBATTO** — one lightning slash, your strings are cut,
and the chef collapses into a floppy ragdoll amid flying ingredients. Restart.

The comedy IS the physics: puppeteer lag, pendulum swing, food with mass.
The difficulty is "cunty hard but fair" — Loot Cycle Inc. tension, not
Sekiro. Every failure must be readable as YOUR wobble, never RNG.

Fictional inspiration stack (for taste, not code): Loot Cycle Inc. (physics
triage under a ticking fail-state), Ratatouille (tiny chef, big kitchen),
1997 bargain-bin PS1 import energy (Pepsi Man voice, broken-Engrish UI).

## 1. The design law (inherited from TOASTSIM, adapted)

1. **One game, not minigames in a trench coat.** Cut, carry, stack, serve are
   all the same verb — *move a pendulum with intent* — scored by the same
   judge. A new mechanic must ride the string sim or improve it for everyone.
2. **The judge is the voice.** The samurai is toastsim's judge generalized:
   named criteria with bands, a line bank with 8+ lines per criterion, the
   scorecard as the emotional payoff. He speaks subtitled Japanese barks with
   deadpan Engrish translations — *"SANDO... ga... yugande iru."* ("The
   sandwich. It leans.")
3. **Physics is the comedian, the sim is honest.** No canned animations for
   gameplay outcomes — if the tower falls, it falls because the center of
   mass left the base. Comedy from real consequences, always.
4. **Nothing ships unverified.** The dev harness drives REAL input
   deterministically and every milestone commits with measured numbers.
   This is toastsim's single most valuable organ. Transplant it first.
5. **Failure must be readable.** The player must be able to say what they did
   wrong from the wreckage alone. If a fail reads as random, it's a bug in
   the DESIGN even when the code is correct.

## 2. Tech decisions (made — don't relitigate)

- **Vite + TypeScript + Phaser 3 (Matter physics build)**. One engine
  dependency; Matter gives joints/constraints for the marionette and food.
  Same repo shape as toastsim: `src/sim/` pure logic, `src/scenes/`
  presentation, `src/dev.ts` harness, `npm run typecheck` must stay clean.
- **The strings are Verlet-ish chains of small Matter bodies + pin
  constraints** (stiffness < 1 so they stretch visibly under abuse). If
  Matter's constraint jitter fights back, fall back to pure-math Verlet
  ropes rendered as polylines with only the ENDS as Matter bodies —
  pre-authorized judgment call.
- **Silhouettes that carry physics are procedural polygons** (flat black
  vector shapes with visible pin-joints, like Balinese/Japanese shadow
  puppets). Generated art is for backdrops, food faces, the samurai, title —
  never for collision shapes. (Toastsim law: silhouettes are gameplay.)
- **Determinism**: fixed-dt stepping (`app.step(dt)` equivalent — Phaser's
  `update` driven manually in harness mode), seeded RNG only. No `Date.now()`
  in sim code.

## 3. The bones to build (in order; each is a pure module + a scene + harness verbs)

### 3.1 `sim/marionette.ts` — the puppet
Classic marionette rig: a **control bar** (the player's proxy) + strings to
head, two hands, two knees. Mouse position = bar position; mouse tilt comes
from horizontal velocity; hold LMB = lower the bar (crouch/reach), release =
lift; RMB or Space = the GRIP (both hands clamp). That's the whole control
surface — depth comes from the pendulum, not the input map.
- Puppet body: 9 segments (head, torso ×2, upper/lower arms, thighs/shins as
  single limbs to start), pin joints with angular limits.
- **The lag is the game**: strings transmit force with ~0.15s of felt delay
  (stiffness/damping tuned, not a literal buffer). Walk = rhythmic bar sway
  that gets the legs swinging. It must feel like Getting Over It's honesty
  with Octodad's dignity.
- Harness: `__s.bar(x,y)`, `__s.sway(freq, amp, secs)`, `__s.grip(on)`,
  `__s.pose()` → joint angles, `__s.settle()` → frames-to-rest.
- **Exit bar (M1)**: pendulum settle time measured; a scripted sway walks the
  puppet 6 stage-units without face-planting; grip picks a box off the floor
  10/10 times in harness. Feel: dangle a hand over a table edge — it should
  make you smile before any food exists.

### 3.2 `sim/food.ts` + cutting
Food = 2D convex-ish polygon bodies with mass, friction, a `face` sprite,
and toastsim-style material params: `skin.resistance`, `flesh.moisture`
(wet food SPLATS — a 2D mess decal + slippery floor patch), `wobble`
(jelly-class food jiggles as soft constraint clusters).
- **The cut**: cleaver in gripped hand; a cut commits when blade-edge speed ×
  angle-through-food crosses threshold (a slow push SQUASHES — moisture
  splats, no cut). Pieces are real bodies with the parent's face split
  across them. Cut quality = blade angle + speed + follow-through, scored as
  **clean / ragged / crime** (crime = squashed).
- Harness: `__s.spawnFood(id, x)`, `__s.swing(from, to, secs)`,
  `__s.cutStats()` → pieces, area CV (toastsim's pieceCV, straight port of
  the idea), splat mass.
- **Exit bar (M2)**: a practiced harness swing produces 2 clean pieces with
  area CV < 0.1; a slow push produces 0 pieces + splat > 0; a wild fast
  swing sends a piece flying offstage (and that's CORRECT).

### 3.3 `sim/stack.ts` — the sando
Sandwich = ordered layers stacked by carrying pieces onto a plate zone.
Stability is honest Matter stacking, but SCORED: center-of-mass drift per
layer, overhang, lean angle. The order ticket names the target
(bread-filling-bread minimum; later: the katsu sando, the "tower" specials).
- Harness: `__s.stack()` → layers, lean°, COM drift; `__s.nudge(force)` for
  the earthquake test.
- **Exit bar (M3)**: 3-layer sando stands; lean° reported; nudge test
  topples a bad stack and not a good one; toppled food splats/rolls.

### 3.4 `sim/samurai.ts` — the judge + the fail state
The anger meter is toastsim's judge wearing armor. **Anger events**: drop
(+small), splat (+medium), crime-cut (+medium), served-wrong (+large),
served-late (+ramps), offstage food (+large). **Cooldown**: a clean serve
sheathes a little. Meter states = katana unsheath stages (0/25/50/75%:
eyebrow → hand-on-hilt → 10cm of blade → full glare, screen vignettes).
- **THE STRING CUT**: at 100% — freeze 300ms, slash line across the screen,
  ALL string constraints deleted in one frame, ragdoll collapse, ingredients
  launched with the impulse of the slash, gong. Score card over the wreckage.
  It must be fast, unfair-FEELING but earned, and funny every single time.
- Serve = dish carried into the counter zone + a bow (hold still 0.5s — the
  dwell commit, straight from toastsim).
- Scorecard groups: **THE CUT / THE SANDO / THE SERVICE / THE FLOOR** (the
  floor = mess+drops; the bench criterion reborn). 8+ lines each, bilingual
  bark + Engrish subtitle. Grade S–D; S = "…umai." and a single approving nod.
- **Exit bar (M4)**: full loop playable — order in, cut, stack, carry, serve,
  scored; harness plays a clean run (B or better) and a disaster run (strings
  cut) deterministically; numbers in the commit message.

### 3.5 Orders, days, escalation (M5)
Ticket rail like toastsim's days: Day 1 = one open sando. Escalate
ingredient count, wetness (tomato before you've earned it is cruelty),
required cuts, and the samurai's patience ramp. **One new pressure per day,
never two.** Day N bar: each day introduces its thing and is beatable by the
harness with a scripted-but-honest run.

### 3.6 The juice (M6)
Title screen: giant brush-stroke 「サンドニエット」, the voice line (text
crawl + gong if no audio yet: **SHUBATTO!! SANDONIETTE!!!**), attract-mode
ragdoll. Shamisen sting + slash whoosh + wet splats (Web Audio, synthesized
or CC0 — no paid assets). Unlockable strings as difficulty modifiers —
**rubber** (bouncy, +lag +comedy), **chain** (heavy, slow, precise), **silk**
(tighter, for cowards) — pre-authorized to defer if M5 lands better without.

## 4. Assets — MODELBEAST, 2D ONLY (this game needs no meshes)

Steal `scripts/gen-assets.sh` from toastsim as the template, keep only the
flux path (`run_flux` + `run_cutout`). **Machine facts current as of
2026-07-17** (verified the hard way): client lives at `~/MODELBEAST` (NOT
~/Documents), token is `MB_TOKEN` in `~/Documents/backnforth/.env`, export
`MB_HOST=http://100.89.131.57:8777` (m3ultra queue). Node m4's venvs are
dead — every stage retries up to 10× with 60s pauses until a healthy node
takes the job; keep that loop until told m4pro is healed.

Style token (fix it, reuse it everywhere):
`"japanese shadow puppet theatre, flat black silhouette with cut-out
details, warm washi paper backdrop, edo period, high contrast, hand-crafted
paper texture"`.

Batch 1 (fixed seeds, start at 301): washi/shoji stage backdrop, wooden
stage floor boards, the samurai in 5 anger states (SAME seed, expression
phrase varies — the toastsim judge-portrait trick), title brushwork art,
paper order tickets, food FACES (bread, tomato, cheese, katsu, egg —
comical, slightly alarmed), splat decals, the katana slash frame.
Fire asset jobs FIRST, build procedural while they render, `loadProp`-style
catch-fallbacks so the game never blocks on art.

## 5. What to steal from toastsim, literally

Open these files and port the PATTERN (not the code — different engine):
- `src/dev.ts` — the harness: real input driven deterministically, helpers
  returning stats objects, `window.__s` installer. THE most important file.
- `src/sim/cutting.ts` — pieceCV and the cvWord ladder; material params.
- `src/game/judging.ts` + `lines.ts` — criteria bands, weighted groups, the
  line bank shape and its voice discipline.
- `OPUS-BUILD-BRIEF-2.md §0` — the verify discipline and "known traps" habit:
  keep a STATUS section at the top of THIS file updated as you go, so any
  session can pick up cold.

## 6. Milestone order and the bar

M0 bootstrap (vite+ts+phaser+harness skeleton, typecheck green, a Matter box
drops onto the stage floor) → M1 puppet → M2 cutting → M3 stacking → M4
samurai + full loop → M5 days → M6 juice. One commit per milestone, message
= what you measured. Screenshots when visuals change. If a feel is wrong,
fixing feel beats advancing — the three feels that must be RIGHT: **the
dangle** (M1), **the clean chop** (M2), **the string cut** (M4).

Pre-authorized judgment calls: all tuning constants; Verlet fallback (§2);
segment count on the puppet; deferring string unlockables; cutting a food
type that won't behave. Do NOT add: paid/cloud dependencies, accounts,
telemetry, or 3D. Do not touch `~/Documents/toastsim` or `~/MODELBEAST`
except through `mb`.

The bet: toastsim proved the bones (harness, honest sim, judge voice, free
assets) make a game feel handmade and alive. Sandoniette bets the same bones
survive transplant into slapstick — that "verified physics comedy" is a
genre, and we can build it in milestones without ever shipping an unfunny
fall or an unfair death.
