Commit Graph

2 Commits

Author SHA1 Message Date
type-two
b76820b88e Heat sources M-H1/M-H2: the knob is not the heat
New bone src/sim/heatsource.ts: a HeatSource sits between the knob and
the browning integrator — the dial sets target, output chases it on a
fuel-specific lag, every station reads output. Five fuels (gas/electric/
induction/charcoal/radiant) + appliance SKUs (oven_gas, oven_fan,
cheap_electric). roasting.ts retrofit is golden-preserving: optional
src?, reads src.output ?? power, byte-identical when absent.

Verified in-browser:
- GOLDEN: no-src oven = mean 0.698 / blister 1.0 / collapse 0 (== HEAD).
- M-H1 chase 3->9: gas 0.433s, electric 6.0s, induction 0.133s (distinct).
- residual after 9->0: gas 0@1s, electric 6.6@6s, charcoal 9@30s.
- M-H2: gas oven stdev 0.1207 vs fan 0.0016 at equal mean 0.6001 (75x).
- day 14 bruschetta now roasts on oven_gas, still grades S (9.62); oven
  HUD reads 'GAS OVEN 8 · blue flame', tomatoes visibly hot-at-centre.

Doctrine: OPUS-BUILD-BRIEF-HEAT.md (the cross-cutting heat & environment
doctrine — 'you never cook the knob, you cook the lag'). Harness:
t.heatChase/residual/ovenStdev/ovenGolden/ovenDemo.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 14:16:54 +10:00
type-two
cc39478a6e M15 bruschetta capstone: roast → assemble → the sog finger-press, day 14 end to end
The multi-station order, on the proven bones. Cut the sourdough (M10) →
toast (M1) → roast the tomatoes (toaster generalized) → assemble (garlic rub,
topping point-set, sog clock) → serve. Judge groups The Bread / The Topping /
The Bench. Planning is the mechanic: the temperature clock drifts the two
cheeses on real service time.

What I built on top of the partial work (sims + judge criteria + lines were
already in place and good — kept them):
- Temperature clock WIRED (sim/tempclock.ts): TempClock armed on bruschetta
  days, steps every tick while timing; a per-perishable fridge/bench toggle UI
  (game.showTempToggle) that reads the judge's own readiness word live; a
  toggle costs 8s service (reopening the fridge has teeth). Ticket hints:
  "get the brie out now", "leave the parmesan in".
- Roast tomatoes WIRED: OvenView routed after the toast lands on bruschetta
  orders (kitchen.onPopped branch → enterOven). Browning Field reused, blister
  past 0.6, collapse to sauce past 0.9. oven_tray.glb is absent → loadProp
  catch-fallback to the procedural tray (graceful, as designed).
- Assembly scene BUILT (scenes/assembly.ts, new): own toasted garlic-rubbed
  slice; G rubs the clove (one-swipe coverage), 1/2/3 drop tomato/cheese/basil
  as a point set; ENTER serves. Distribution scored by Clark-Evans (the rind
  code, fed the topping list); balance by mass-per-area band.
- Sog clock: rate x wet-mass x moisture / thickness — the doorstop resists,
  the thin slice surrenders; the judge presses a finger ("It bends. Toast
  should not bend.").
- Day 14 order added; enterOven/enterAssembly/serveBruschetta follow the exact
  runNextPrep/enterPrep/enterJuice machinery. Scorecard grouped
  bread/topping/bench via judge()'s existing group path.
- Harness (src/dev.ts): t.roast(s,power), t.rub(passes), t.assemble({...}),
  t.sogWait(s), t.tempToggle(id), and t.bruschetta() happy path returning the
  full grouped stats.

Fixes to the partial work: judge.ts shadowed the `order` param with a local
`const order` (rename → groupRank; would not typecheck); oven.ts material
opacity access typed as Material|Material[] (cast once).

Tuning (measured headless — the pure sims run without three.js):
- assembly SOG_RATE 0.006 → 0.004: doorstop (0.31) under a normal wet load
  (5 tomato ~2.1) served promptly (8s) stays CRISP (sog 0.21); dawdled 30s goes
  soft (0.77); a thin slice (0.12) bends inside ~15s. The bakery loaf matters.
- assembly MASS_BAND [1.1,2.1] → [2.5,4.5]: the old band scored a normal build
  (5t+4c+3b = 3.82 total mass) as an off-band 0; the band now sits around a
  proper generous topping. Sparse ~1.3 reads bare, a heap ~6.6 slides off.
- roasting RATE 0.042 kept; comment corrected to the measured window (power 6:
  stalls wet to ~26s, blisters 28-36s, collapses past ~40s).
- brie softensInSec 80 (from partial work) kept: out at order start, spreadable
  by assembly.

VERIFY (dev build; run in the console):
  t.start(); t.bruschetta()
Expected (headless sim + live path):
  roast:        {mean ~0.70, blister ~1.0, collapse 0, score ~1.0}
  distribution: ~1.0   (Clark-Evans R ~1.25, evenly strewn)
  balance:      {mass ~3.82 in band [2.5,4.5], score ~1.0}
  sog:          {value ~0.21, word "crisp", score ~0.99}
  rub:          {coverage ~0.35-0.5, score ~1.0}
  temp:         {score 1.0 (brie taken out, parmesan left in)}
  bench:        ~1.0   (clean roast, no slump)
  grade A/S, total ~9

Sub-mechanic checks (after t.bruschetta up to the oven, or standalone):
  t.roast(26,6)  → mean ~0.56, blister ~0     ("under-roasted")
  t.roast(42,6)  → collapse ~0.69             ("collapsing to sauce")
  skip t.tempToggle('brie') → Temperature 0, worst "brie fridge-hard"
  thin slice + t.sogWait(15) → sog ~1.0, "it bends"

typecheck clean (tsc --noEmit), vite build clean. node_modules is a symlink to
the main checkout and is deliberately NOT committed (dir-only .gitignore rule
misses the symlink; staged the 12 source files explicitly).

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