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>
235 lines
26 KiB
Markdown
235 lines
26 KiB
Markdown
# 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
|
||
|
||
M0–M9 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 Clark–Evans, 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 M0–M9 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.** Clark–Evans 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 = 26–36mm 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 ~4–6 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` (3–7). 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 = Clark–Evans 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 M12–M15; 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.
|