toastsim/OPUS-BUILD-BRIEF-2.md
type-two d5ed20cacb M15 bruschetta merged: Stage 3 complete, Brief 2 fully executed
Live gate: happy path S (9.63) — roast 0.698/blister 1.0, distribution
R 1.25, balance 3.82, sog 0.217 crisp, rub 43%->1.0, temp 1.0. Failure
paths named: 48s@7 roast collapses to sauce (B, roast 0, bench 0);
skipped brie toggle temp 0; thin slice + 30s dawdle 'it bends (1.00)' —
the finger-press line fires. Every M10-M15 milestone in Brief 2 is now
shipped and gate-verified; next: MASTERPLAN P2.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 15:30:57 +10:00

235 lines
26 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# TOASTSIM — Build Brief 2: The Prep Bench
**For: Claude Opus 4.8, executing in Claude Code on this Mac (M3 Ultra, cwd `/Users/m3ultra/Documents/toastsim`).**
**Approved by the owner. Asset generation via MODELBEAST is free and pre-authorized. Commit + push per milestone to `git@gitea.partly.party:monster/toastsim.git` (SSH remote already configured; HTTPS has no credentials).**
---
## 0. Where you are standing
M0M9 are shipped and verified: toasting (browning field), spreading (knife-angle rheology, butter/PB/MITEY/marmalade), the judged scorecard with a claymation judge, the tangled cutlery drawer, day loop with orders, oil-separation stirring, rind distribution scored by ClarkEvans, and a fresh-eyes pass (M9) that locked the cheat panel and added live readouts. **Read `README.md` and the git log first — the commit messages are design documents.**
**M10 (the bakery) is code-complete but NOT fully verified — that is your first job (§2).** It adds: bread brands with mechanical quality (`bread.ts` BRANDS), unsliced bakery loaves, a slicing minigame (`sim/slicing.ts` + `scenes/slicer.ts`), the bread knife and the legendary **The Best Thing** in the drawer, day 9, and cut quality feeding the toaster (wedge slices brown unevenly).
### How to run and verify (non-negotiable discipline)
- `npx vite --port 5173 --strictPort` in background; open via the Browser pane.
- **The embedded browser never fires rAF** (document.hidden is true) and can report `innerWidth 0`. The game survives both, and the dev harness exists because of them: `window.__t` (dev builds) drives the real input path deterministically — `t.run(frames)`, `t.tap('Space')`, `t.point(x,y,z,down)`, `t.toast(secs,power)`, `t.dip()`, `t.spread()`, `t.swirl()`, `t.stats()`, `t.look()`. Call `t.app.resize()` once after load. Extend the harness for every new minigame you build — if you can't drive it headless, you can't verify it.
- Verify by **playing and reading numbers the judge computes**, then screenshot. Never trust "it compiles". Every milestone: typecheck, build, played-through evidence, console clean, commit with a message that records what you *measured*, push.
### Known traps (all cost real debugging time once — don't pay twice)
- `THREE.Timer.connect(document)` freezes dt when hidden — don't reintroduce it.
- `camera.aspect = 0/0` poisons projection forever; `App.resize()` guards it — keep the guard.
- `mesh.rotation.x = -PI/2` on extrusions lays profiles out in opposite directions — use `extrudeFlat()` (geometry-level) in `cutlery.ts`.
- Rapier wants `{x,y,z,w}` quaternions, not arrays; array = NaN = wasm panic.
- Lathe profiles touching the axis = starburst normals. Build from primitives.
- Colours: palette is authored sRGB. `new THREE.Color(...floats)` reads as linear → pale. Use `.setRGB(..., THREE.SRGBColorSpace)`.
- ACES desaturates >1.0 — keep lighting products under 1.0 or yellow becomes cream.
- Shared materials can't do per-piece emissive — clone if you need highlights.
## 1. The design law (what made M0M9 good — keep obeying it)
1. **Analog inputs, judged outputs.** Every mechanic is a continuous gesture the player can be *better at*, and every score is a statistic computed from the same state the gesture mutated. The scorecard always points at something real.
2. **Mistakes travel.** A bad cut toasts unevenly. Dawdling cools toast, which hardens butter. Never punish twice at the same station — punish downstream, where it's funny.
3. **Feedback before the verdict.** Live readouts use the judge's own functions (see kitchen's `amount` readout). The verdict may be brutal; it may never be a surprise.
4. **Statistics with names.** ClarkEvans for scatter, mass-weighted mean±stdev for consistency, CV for evenness. The judge quotes the number; the number is honest.
5. **Silhouettes are gameplay** → procedural geometry for anything the player must identify or that carries physics. Generated GLBs for hero props and vessels.
6. **The judge is the reward.** Every new criterion ships with ~5 bad lines + 3 good lines in his voice: dry, specific, never cruel except about MITEY.
## 0.5 STATUS — updated 2026-07-17, read this before §2
**M10 is DONE, verified and pushed** (commit `e5be266`). §2 below is history; skip it.
**M11 is code-complete and verified, committed as WIP** — see §3, and the notes here.
What M11 landed (all typechecked, all played headless via the harness):
- `sim/ingredients.ts` — the nine-ingredient cast with the full profile from §3.1.
- `sim/cutting.ts``CutSession`, patterns halve/slices/wedges/dice, slip physics,
juice ejection, `pieceCV`. **Note:** CV for `slices`/`halve` runs over piece WIDTHS,
not volumes — on a round ingredient the end slices are thinner by geometry and a
perfect cut still scored CV 0.40, which is not a skill test. Wedges/dice keep volumes.
- `sim/mess.ts` — the judged mess field + solids list + mass-conserving wipe.
- `scenes/prep.ts` — the bench: aim/bite/saw, seams, pieces fan out at the end,
juice stain texture, instanced solids, live `bench: clean/smeared/a crime scene`.
Session end does NOT auto-exit — ENTER hands off, so you can wipe first.
- `judging.ts``PrepResult`, `cutEvennessCriterion` (The Pieces, w 1.3),
`benchCriterion` (The Bench, w 0.9), `clarkEvans` extracted for M15's toppings,
criteria tagged with `group` and the scorecard grouped by station.
- `lines.ts` — 8 lines each for `cutcv` and `bench`.
- Harness: `t.prep(ing, pattern, knife)`, `t.chop({pos, dy, wobble, press})`,
`t.wipe(passes)`, `t.prepStats()`, plus `t.start()`, `t.day(n)`, `t.pick(id)`, `t.slice()`.
Measured on the bench (repeat these before changing tuning):
- Even deliberate slices (dinner knife, 0.045 u/frame) → CV ~0, bench mass 0.51.
- Sloppy positions + frantic speed (0.3 u/frame) → CV 0.72, mass 18.8, 16 solids, "a crime scene".
- Press a dinner knife on a tomato without sawing → it skates: 1 slip, aim jumped 0 → 0.12.
- Same press with The Best Thing → bites in 7 frames, 0 slips (sharp edges sink on downforce).
- Wipe two passes → mass 0.51 → 0.09.
**UPDATE (2026-07-17, later): M11 is DONE** (`d7ce7b7`) — days 10/11 route through the
bench (dice onion / wedge tomato), dice & wedges driven and measured (even: CV≈0 both;
bad aim: CV 0.74; sloppy saw: mass 31 "a crime scene" — aim and tidiness are separate
judged skills). Scorecard shows THE PREP → THE TOAST → THE SPREAD end to end.
**M12 core is BUILT** (`sim/juicing.ts`, `scenes/juice.ts`, `dev-juice.ts` via `__tj`):
verified — Pyramid 3revs@0.7 = 72.8% + 4 escaped pips, Juicernaut 100% + all seeds
caught, butchered halve caps at its 0.6 theoretical (no double punishment), mash = 8.5%
+ pulp + forced seeds, reversals pay (77.1% vs 72.8% equal-sweep). NOT YET ROUTED:
no order sends you to the juice station — integration point is `juiceResult()` /
`juiceCriteria()` / `JUICE_LINES` in `sim/juicing.ts` (documented in-file). Remaining
polish carried forward: pieces fan-out still cylinders (wrong for dice, non-blocking);
wipe still distance-disambiguated, needs a cloth tool; MASTERPLAN.md now sits above
this brief — read it first. Also in the cast since M11: mushrooms ×3 (slip showcase,
`'mushroom'` silhouette), bell pepper (seed-scatter emergent win). See §7.5.
**STAGE 2 DONE (same day): M13 + M14 + juice routing all merged and gate-verified.**
M13 grater (`__tg`): confident strokes 0.61 pile / scrubby spray 0.095+stray / soft
block clogs solid / lazy zest 62% pith; reviewer retune — knuckle roll has a 0.4
pressure floor now (10/10 gentle runs bloodless, 10/10 hard runs bleed; easing off is
a defense, not a smaller lottery). M12 ROUTED: day 12 juice order end-to-end, THE
JUICE on the scorecard (72.8%, 4 pips through the Pyramid). M14 onions: day 13
stop-line dice — proper stop 0.846 depth scores The Dice 1.0; sawing through the root
scores 0 at IDENTICAL CV (technique and evenness told apart); sting 0.098 after a
quick dice → 1.0 after 10s idle, screen genuinely unreadable (screenshot-verified).
Remaining from this stage: microplane grater defined but unused; day 12's halve is a
clean default (cut-your-own-orange chaining is future content); pieces fan-out still
cylinders.
**STAGE 3 DONE: M15 BRUSCHETTA SHIPPED AND GATE-VERIFIED (2026-07-18).** Day 14
end-to-end: brie out early (8s toggle cost), doorstop cut, toast, roast (browning
Field + blister past 0.6, 42s@7 = collapse 1.0 into sauce), garlic rub, Clark-Evans
topping (R 1.25 "evenly strewn"), sog clock, serve. Happy path grade S (9.63) live.
Failure paths discriminate and NAME the sin: over-roast → B with roast 0 + bench 0
(the spill); skipped brie → temp 0 "brie soft" missed; thin slice + 30s dawdle →
sog "it bends (1.00)" and the line "This has the structural integrity of an
apology." Reviewer retunes carried: SOG_RATE 0.004 (doorstop defends), MASS_BAND
2.5-4.5 (generous topping is the band). Known harness wart: standalone t.roast()
before the oven opens reads a stale session — drive failure paths through the full
sequence. §2-§7 of this brief are now ALL DONE; next work comes from MASTERPLAN P2
(FIRE: the pan, the steak, the salt system) — cut Brief 3 from it when starting.
**Asset farm status (was BLOCKER, now half-fixed):** John freed 50GB on m4pro, but the
cleanup took m4pro's venvs with it (`venvs/mflux`, `vendor/hunyuan3d-mlx/.venv`) — jobs
routed to node m4 die in ~1s. `gen-assets.sh` now finds the client at `~/MODELBEAST`,
reads `MB_TOKEN` from `~/Documents/backnforth/.env`, targets the m3ultra queue, and
retries every stage 10× with 60s pauses so healthy nodes pick jobs up (ponytail-marked;
strip once m4pro is rebuilt or pulled from the pool — owner-only, needs John). All 7
bakery GLBs + the prep cast are landing; add mushroom/pepper/P2 prompts once the
running batch finishes. Everything still degrades gracefully via `loadProp` catch.
---
## 2. M10 finish line — verify the bakery (DONE — kept for the record)
Everything compiles and day 9 reaches the drawer with the right ticket (verified: chips show `the bakery loaf / doorstop — slice it yourself`, timer reads "they are waiting", and the drawer contains `bread_knife` AND `the_best_thing`). Remaining:
1. Play the slicer end-to-end via harness: pick the bread knife from the drawer (drag it above the rim, hold 0.45s), aim ~0.31 world units (doorstop = 2636mm at 110mm/unit), saw with vertical strokes (~0.05 units/frame stays under the squash threshold), confirm `onSliced` fires and the kitchen receives the cut.
2. Assert the couplings: a deliberate wobble cut (add dx while sawing) must show browning-evenness degradation after toasting (`stats().browning.stdev` visibly higher) and "a bit wedged" in The Cut row. A slam cut (fast dy) must arrive pre-damaged (Integrity).
3. Wrong-tool slicing: take the *soup spoon* to the loaf — confirm it's miserable but possible (saw eff 0.3) and the judge's Cut row + Utensil tell the story.
4. Tune feel: `STROKE_GAIN`, `WOBBLE_GAIN`, `SPEED_CAP` in `slicing.ts` are first guesses. A clean bread-knife cut should take ~46 deliberate strokes; The Best Thing ~3 and nearly wobble-proof. Cheap-brand WONDERSLICE toast (day 1) should visibly brown patchier than Crustworthy (day 8) — screenshot both.
5. The slicer scene has placeholder-quality visuals (flat crust box loaf, no cut seam). Minimum bar: a visible cut line that deepens with progress, the cut slice separating and falling flat at the end. Nice-to-have: crumb texture on the cut face (reuse `bread.crumb` colour).
6. `verify → commit → push` as **M10**.
## 3. M11 — The prep bench core (build once, everything else rides it)
A second bench (like the slicer scene) where ingredients get cut, and the machinery every later milestone uses.
### 3.1 Ingredients
`src/sim/ingredients.ts`:
```ts
interface Ingredient {
id: string; name: string;
shape: 'sphere' | 'ovoid' | 'block' | 'wedge' | 'bulb';
size: number; // world units, 1 ≈ 11cm
skin: { resistance: number; slippery: number }; // tomato: low res, HIGH slip
flesh: { resistance: number; moisture: number; layered?: boolean }; // onion: layered
seeds?: { count: number; pocket: 'core' | 'distributed' };
sting?: number; // onions
temp?: { state: 'fridge' | 'bench'; softensInSec?: number; needsSoft?: boolean; needsCold?: boolean };
gratable?: { yield: number; pithDepth?: number }; // citrus zest / hard cheese
roastable?: boolean;
}
```
Starting cast: `tomato` (slippery, wet), `roast_tomato` (wetter, softer), `orange` (ovoid, seeds, zestable), `onion` (layered, sting 1.0), `garlic` (bulb, small), `parmesan` (block, needsCold, gratable), `brie` (wedge, needsSoft), `cucumber`, `avocado` (seed = one giant pit, flesh softness varies by *ripeness* roll — a free lottery joke).
### 3.2 Generalized cutting (`src/sim/cutting.ts`)
Extract and extend the M10 slicing sim. A `CutSession` = ingredient + knife + a **pattern**:
- `halve` — one cut; scored on evenness of the two halves (offset from centre + wedge).
- `slices(n)` — repeated cuts; pieces recorded as widths → **evenness = coefficient of variation** of piece volumes. CV < 0.08 "even", > 0.25 "a lottery".
- `dice` — two-phase (see onions, §6).
- `wedges(n)` — radial; for oranges/tomato wedges.
New physics on top of M10's progress/wobble/squash:
- **Slip**: on the first bite, if `skin.slippery * downforce > grip`, the ingredient *skates* — the cut position jumps, juice sprays (→ mess field), and you re-aim. Grip comes from saw motion (a moving serrated blade bites; pressing a chef's knife straight down on a tomato is the canonical failure). The Best Thing and sharp knives bite at low downforce.
- **Juice**: every cut through `flesh.moisture > 0.4` ejects juice proportional to moisture × speed, sprayed along the stroke direction (→ mess field §3.3). Slow deliberate cuts leak less. Roasted tomatoes are basically water balloons — the joke is intended.
- Pieces are physics bodies on the board when it matters (dice results tumble; reuse drawer's compound-collider discipline — primitives, never trimesh).
### 3.3 The mess field (`src/sim/mess.ts`) — the bench is judged now
A `Field` over the cutting-board UV (reuse `core/field.ts` wholesale) + a particle list for solids (seeds, skins, zest, crumbs — reuse the rind instancing pattern from `slice.ts`).
- Writers: juice spray, seed ejections, onion skins, grated overflow, squashed pulp.
- Wipe tool: hold-and-drag a cloth (same brush machinery as spreading, in reverse). Wiping costs service time. Wiping juice *spreads it thinner first* (mass-conserving brush: same trick as butter) before absorbing — one pass smears, two passes clean. That's the feel.
- Judge criterion **The Bench** (weight ~0.9) on any prep order: mess mass + spread fraction, worded by the worst contributor: "There is juice on everything. Everything." / "Somebody's seeds are on my floor." Good: "A clean board. You'd be surprised how rare."
- Live readout in the prep HUD: `bench: clean / smeared / a crime scene` — judge's own thresholds.
### 3.4 Scoring plumbing
Orders grow a `prep?: PrepStep[]` list; the judge gains a per-step criterion builder (cut evenness, yield, pith, sting-time, bench). Group the scorecard visually by station (Prep / Toast / Spread) — the card is getting long, and grouping keeps it legible.
## 4. M12 — Oranges and the ribbed pyramid
**Flow: (zest first, if ordered §5) → halve → juice → serve the glass.**
- **Halving** uses `halve` pattern. Cut-quality → **theoretical yield**: `yield = base × (1 2|offset|) × (1 0.5·wedge)`. A perfect halve leaves 100% reachable; a 60/40 butchering caps you at ~70% and the judge will say why: "You have cut a lid and a bucket."
- **The juicer** (hero prop: old-school ribbed glass pyramid — generate it). Minigame reuses the *jar-stir swirl detection* (kitchen.ts) verbatim: place the half (drag onto cone), then **press + twist**. Twist sweep extracts (`extracted += sweep × pressGrip`); pressing hard *without* twisting tears pulp (mess + pulp-in-juice) and sprays. Yield bar fills toward the theoretical max — the live readout says `62% of what this orange had`. Rotate rhythm matters: reversals of twist direction give a small bonus bite (real reamers work this way; it also just feels right).
- **Seeds.** Each orange carries `seeds.count` (37). The Pyramid Classic (starter) lets seeds through into the glass: visible, clickable — fish each out at a time cost, or serve pips and eat the criterion: "There are three pips in this. I counted. I shouldn't have been able to."
- **Juicer tiers** (progression, introduced by day like brands): `Pyramid Classic` (day 12) → `Ribbed Deluxe` (seed moat catches ~80%) → **`The Juicernaut`** (legendary, day ~20 procedural rare: catches everything, +10% yield, and the judge notices: "Is that a Juicernaut. Sit down.").
- Judge criteria: **Yield** (vs theoretical, weight 1.3), **Pips** (1.1), **The Bench**, plus the halve's Cut row. Serve = the glass on the pedestal instead of toast — `JudgeView.show` needs a `subject` abstraction (toast mesh | glass mesh). Keep it small: a presented `Object3D` + criteria list.
## 5. M13 — The grater
One prop (box grater generated; microplane optional later), two customers:
- **Zest**: before juicing, some orders want `zest of one orange`. Hold the orange against the grater, rhythmic vertical drags = zest particles (distance-based shedding — the marmalade-rind pattern, reused exactly). **The pith line**: each surface region has `zestDepth`; over-grating a region past `gratable.pithDepth` sheds white pith particles into the pile — visible (white among orange), and the judge's **Pith** criterion counts the ratio: "This zest is half pith. It will taste like a headache." Scroll wheel rotates the orange to expose fresh skin — the skill is *rotation discipline*, same muscle as rind-scatter.
- **Cheese**: parmesan (needsCold) grates clean IF cold — it softens on the bench in real time, and soft parmesan (or brie, ever) **smears**: output drops, the grater clogs (a clog meter; clearing it costs time), mess accrues. The inversion of butter: butter wants warm, cheese wants cold, and both run on the same temperature clock (§7).
- **Knuckles**: grating speed over a threshold rolls a knuckle graze — small Integrity-style penalty on the *player* (brief red flash, mess spot, and a line: "That's your knuckle in there. I'm not scoring the protein.").
- Judge criteria: **Zest/Gratings amount** (band, like spread amount), **Pith**, **Clog/mess**.
## 6. M14 — Onions (the technique test)
- **The sting.** While an onion is cut open, sting pressure rises (rate × `sting` × cut aggression — crushing cuts sting more, so blunt knives and squash-heavy strokes are literally tear gas). Rendered as a growing edge-blur + welling vignette (CSS overlay, cheap). Vision cost is REAL: it blurs the cut line and the stop-line marker. Options with costs: wipe eyes (button: clears it, 4s, and if you've been handling onion it *doubles* it first — once, for the joke and the lesson), push through, or work fast and clean. Sharp knife + low squash = slow sting. The Best Thing is, again, the best thing.
- **The proper dice** — exactly the technique the owner described:
1. Halve stem-to-root.
2. Lay flat. Radial/parallel cuts toward the stem **that must STOP short of it** — a visible stop-line at ~85%; a cut that goes through the stem releases the layers early (pieces fall apart into slivers — layered flesh means the CV of your final dice explodes). Undercutting leaves connected chunks (also CV chaos, other direction).
3. Rotate 90° (scroll wheel), **criss-cross** cuts → dice falls in cubes.
- Scored on **Dice CV** (the piece-evenness statistic from §3.2 — this is where it shines), stem discipline (through-cuts counted), sting time endured, bench state (onion skins are mess solids).
- Judge lines: "You went through the stem. The onion became confetti. You served confetti." / good: "Square. Actually square. I'm keeping one."
## 7. M15 — Bruschetta (the capstone that uses everything)
The multi-component order. The ticket becomes a small recipe card; service time weight rises; **planning is the mechanic**:
- **Temperature clock** (generalizes butter softness): ingredients have bench-vs-fridge states drifting on real time. Brie must come out EARLY (soft enough to spread by assembly time); parmesan must stay in UNTIL needed. The ticket hints ("get the brie out now, love") and a small fridge/bench toggle UI per perishable — decided at order start and revisitable at a time cost.
- **Roast tomatoes**: the oven is the toaster generalized — reuse the browning Field on tomato halves (they blister rather than brown: same field, different colour ramp + a `blister` speckle past 0.6). Overdone = collapse into sauce (moisture → mess when handled).
- **Assemble**: your OWN sliced sourdough (M10), toasted (M1), rubbed with garlic (one swipe mechanic — a light 'spread' with a garlic clove, coverage judged lightly), topped: tomato pieces + cheese + basil. **Topping distribution = ClarkEvans again** (the rind code takes a point list — feed it topping placements). **Balance**: total topping mass per slice area band, "thicker but even and balanced = more score" — mass band × distribution, the user's exact ask.
- **The sog clock**: wet toppings on toast start `sog` rising (rate × topping moisture ÷ slice thickness — thick doorstop slices resist sog: ANOTHER reason the bakery loaf matters). Serve before soggy; the judge presses a finger on it, on camera: "It bends. Toast should not bend."
- Judge screen groups: The Bread (cut+toast) / The Topping (distribution, balance, sog) / The Bench. This is the order type where an S feels like a diploma.
## 7.5 The pantry backlog (John's asks, 2026-07-17 — spec'd, not scheduled)
Each of these rides existing bones. None block M12M15; pull them in when their host system lands. Already in the cast as of today: three mushrooms (button/portobello/king oyster — the slip system's showcase) and bell pepper (waxy skin, seed core; hollowness deferred).
- **Capers + wasabi — the dosing danger class.** Tiny and SUPER salty / hot; the mechanic is restraint. One "pinch/dab" gesture on the spread system with a brutal dose curve: under = judge doesn't notice, right = flavour bonus, a hair over = order ruined ("There is a WAR CRIME on this crostini"). Same curve, two condiments — build the dose scalar once. Rides: spread mass tracking + judging bands.
- **The mustard family.** Heaps of types (hot english, dijon, wholegrain, american...) = the jar/drawer system generalized, like the PB crunchy/smooth split but wider. Each order names or implies a type; wrong-mustard is a pairing miss, not a technique miss. Rides: drawer + jar + ticket hints.
- **Rib eye — the crown jewel, its own milestone.** Fresh cooked, and it BLEEDS: (1) cook right — the browning Field on a steak, two sides, doneness = interior ramp not surface; (2) REST it — cut too early and juice mass floods the board (moisture starts ~0.95 and decays toward 0.6 over a real-time rest clock; the mess field already knows what to do with the flood); (3) cut right — across the grain; give the ingredient a `grain` axis and score cut-angle against it, wobble tears rather than slices; (4) serve with the RIGHT mustard (pairing above). Judge groups: The Cook / The Rest / The Cut / The Pairing.
- **Baked stuffed potatoes.** The oven (M15's toaster-generalized) plus one new gesture: cut the lid (cutting sim, one careful shallow cut — depth matters for once), scoop (inverse of spreading — remove mass evenly without breaching the skin, coverage judged), mix scoopings with cheese/toppings, restuff, rebake. Skin breach = structural fail the judge pokes. Rides: oven Field, spread-coverage math run backwards, mess.
## 8. Assets — MODELBEAST (all free, all local; fire jobs FIRST, code while they run)
`scripts/gen-assets.sh` has the pipeline (`gen_3d name seed "prompt"`; flux → bg_remove → hunyuan3d, ~6 min each, GPU lane serial). **The M10 bakery batch partially failed**`sourdough_loaf` meshed but produced no GLB, and `batard_loaf`, `cutting_board`, `tomato_vine`, `roast_tomatoes` failed at flux. First: check `./mb jobs` (source `~/Documents/MODELBEAST/data/agent.env`) for the error text, re-run with bumped seeds (+1) and slightly reworded prompts; add a retry-once into `gen_3d`. Never spam retries on `queued` — the lane is serial and shared.
Then queue the prep-bench cast (fixed seeds, style token as in the script): oranges ×2 (whole / halved showing segments), ribbed pyramid juicer (hero — prompt the ribs explicitly), juice glass, box grater, brown onion, garlic bulb, basil sprig, olive-oil bottle, oven tray, Ribbed Deluxe + Juicernaut juicer variants, plus 2D: bruschetta title-hero, "WONDERSLICE" bag art for the ticket. Anything the player must *identify by shape* or that carries physics (onion halves, dice pieces, orange halves on the reamer) stays **procedural** — silhouettes are gameplay, and meshers hate thin/segmented geometry.
## 9. Milestone order, and the bar for each
M10-verify → M11 → M12 → M13 → M14 → M15, one commit+push each, message = what you measured. For each: harness helpers first, then the sim (pure, in `src/sim/`), then the scene, then judge criteria + 8 lines, then live readouts, then played-through verification with numbers in the commit message, then screenshots. If a milestone's *feel* is wrong, fixing feel beats advancing — the loaf, the reamer twist and the onion stop-line are the three feels that must be RIGHT.
Pre-authorized judgment calls: all tuning constants; cutting scope inside a milestone to protect feel; procedural-vs-generated per asset; deferring the microplane, avocado, cucumber if the core five (orange, onion, tomato, parmesan, brie) land better without them. Do NOT add paid/cloud dependencies, accounts, or telemetry. Do not touch `~/Documents/MODELBEAST` except through `mb`.
The bet is the same as last time: the bones are good, so build each new station on the bones — Fields, point-statistics, distance-shedding, swirl detection, dwell commits, the harness — and the game stays one game instead of six minigames in a trench coat.