Commit Graph

77 Commits

Author SHA1 Message Date
type-two
d1645bae6e THE FRYER: day 24 — twice, love. once for cooked, once for golden
The pot of oil, from the atlas: oil is the fear mechanic and the double
fry is the physics, not the flavour text. Cold oil soaks (grease). Hot
oil seals — and the seal is REAL: past ~0.8 gold the crust shuts and the
inside stops listening, so one screaming-hot fry gives you 'firm in the
middle — the seal beat the cook'. The honest path is the card's path:
blanch in the shimmer until fluffy, REST on the rack while the steam
escapes (dryness is what golds — the oil spends it, the rack rebuilds
it), then back in HOT for the colour. The basket knocks the oil down a
full degree when it lands, because thermal mass does not care about
your schedule.

THE SPIT is the knuckle lesson with a floor: raw chips in oil past 8.4
throw it back, and a spit only burns the hand PARKED OVER THE POT — the
pointer is your hand, and standing back costs nothing. The judge's row
reads 'it spat 13 times; you did not flinch' or names exactly what the
hovering cost you.

First cut had two dead spots the adversarial pass caught: the seal was
too weak (a single hot fry still cooked through for 9.1) and the spit
window was unreachable (the basket-knock kept the oil under SPIT_AT for
the whole raw phase). Rebalanced: seal 1.25x, gold rate up, spit at 8.4.

Verified: double-fry 10.0 ('proper gold · fluffy inside · dry. drained.
correct · 2 fries — as the card says'); one-hot-fry-hand-hovering 7.6
with 13 burns. Day 24 on the card, procedural arm, ghost demo slot 24,
hero_chips on the scorecard, Ray's portrait already on the ticket.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 03:46:37 +10:00
type-two
782971ee83 THE CARD: the recipe stops being narrative and becomes law
The multi-cycle timer doctrine from the atlas, first station: the air
fryer's cursed card ('180° until colour · SHAKE · 190° to golden') is
now DATA — deg1, deg2, and a half-time shake — and the sim keeps the
ledger to judge it by: heat-weighted seconds at every knob position,
split into pre-shake and post-shake phases, plus the mean browning at
each shake so 'half-time' is a real place (0.15–0.5) and not a vibe.

A new row on the scorecard, The Card: 40% ran phase one at its degrees,
40% phase two at its, 20% shook in the window. Ignore it entirely and
the detail reads 'no shake. the card wept'; right temps but a mistimed
shake is its own sentence. Procedural wing days now deal varied cards
(170/180 into 190/200).

The best bug of the batch: the verifier caught MY demo driver setting
knob 8 for the finish — that is 200°, and the card says 190°. 'roughly
the card, squinting.' The judge was right and the demonstrator now
obeys: 'as written', 9.5/10.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 03:38:05 +10:00
type-two
29f6b25c5b THE AIR FRYER: day 23 — air is the ingredient
John's spec from the atlas, honored verbatim: the basket is a small 2D
tray and ARRANGEMENT is the skill. Six wings arrive DUMPED in a pile;
every piece wants a clear gap to breathe (Clark–Evans inverted — the
same spacing statistics that reward an even topping spread here reward
margins). Smothered wings brown slow and keep a PALE face the judge can
see; steam is a LOCAL sin — a corner pile in an empty tray still steams
(the first cut used global fill fraction, which six wings could never
trip; the adversarial pass caught the dead mechanic and the smother
statistic replaced it). The drawer pauses the cook and bleeds the heat —
hovering costs. The SHAKE is the egg-crack grammar: hold F, release; a
sane charge turns the pale faces to the wind, a full-send puts five
wings on his floor and the corner eye goes horrified.

New station, full rails: sim/airfryer.ts (pure, seeded), the top-down
scene with the sliding basket (snap-out on reset — a stale docked tray
was eating the demo's opening drags), judgeAirfryer (THE CRISP golden
band / THE TURN pale faces / THE AIR steam / THE COUNT floor wings),
day 23 on the card with the cursed recipe card, a procedural endless
arm, fan-whir foley that dies when the drawer opens, ghost demo slot 23,
and the wing man's portrait (high-five not coming).

Verified: honest lattice play 9.5 twice back-to-back; the lazy pile
3.6 ('a steamer wearing a fryer badge', raw, never shaken); full-charge
shake loses 5 of 6 with the horrified eye.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 03:17:49 +10:00
type-two
d0fc805705 THE HORIZON: six stations get a room to stand in
Six blurred backdrops off the FLUX lane — dusk backyard for the grill,
copper-hung splashback for the pan, steaming stockpot shelf for the
poach, butcher's tile for the steak board, pantry shelves for the eggs,
steel cold room for the fridge — and a lesson in frustums for their
integration. A far wall plane (the kitchen splashback trick) is
invisible to these stations' pitched-down cameras: at 45 degrees of
downward tilt nothing above the horizontal ever enters the frame. So
the art ships as the scene BACKGROUND instead — screen-space, swapped
in on enter() and restored on exit() — and the oversized 24x12 bench
slabs that covered every pixel shrank to real bench size so the room
can peek around them. The stations went from furniture in fog to
furniture in a kitchen.

Mechanics untouched by the slab diet — the drag planes are infinite
THREE.Planes, not the meshes: grill/pan/fridge/steak drivers all pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 02:52:58 +10:00
type-two
d998b74abc THE FACES: every regular gets a portrait, the jar gets three, the dish gets one
Twenty-two assets off the Cloudflare FLUX lane (scripts/gen-art-batch.sh
— idempotent, fixed seed per character, ~5s each, barely a dent in the
10k daily neurons):

- Thirteen regulars on their tickets, clipped into the corner like a
  loyalty card photo: Deidre's floral cardigan, Ray's flat cap, the
  child on tiptoes with a fistful of coins, the bloke from two doors
  down in his dressing gown, the delivery mid-yawn at 6am, the lunch
  rush as three impatient torsos, the pavlova order in her pearls. The
  inspector's ticket uses his own portrait; a name with no face made
  simply shows no frame (onerror removes it).
- THE MOTHER's jar rendered in all three states on the title button:
  bubbling over the rim, sulking under grey liquor, and dead with a
  tiny white surrender flag planted in the crust.
- Six dish heroes on the scorecard beside the ask: the skewers, the
  pan, the sliced rib eye, the whites bowl, the poached egg mid-drip,
  the benedict.

Verified live: pavlova portrait on day 20's ticket, benedict table on
21's, jar_sulking at gap 3, hero_eggs on the eggs scorecard.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 02:37:15 +10:00
type-two
720e4dd5df THE CRITIC'S FACE: he brings his own portraits now
Five moods generated on the Cloudflare Workers AI FLUX lane (10k free
neurons a day — scripts/cf-flux.py is the tap): a gaunt man in a black
three-piece suit, burgundy scarf, small round glasses, tiny notebook and
fountain pen, same claymation family as the judge and unmistakably not
him. Fixed seed, expression-only prompt deltas, so he is the same figure
across all five — the horrified one recoils mid-note, the impressed one
wears the small smile of a man betrayed by a good meal.

The grayscale filter is gone: the scorecard and the corner eye both
route by critic day (critic_* / judge_* prefix), demos and dailies stay
the judge's own. Also fixed 'the the count betrayed you' — his rows
already start with THE and he would never stammer in print.

Verified live: day 20 corner eye critic_neutral, scorecard
critic_impressed at 10.0 and critic_disappointed at 4.9 (C stamp, his
lines), day 21 back to the judge everywhere.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 02:25:40 +10:00
type-two
9ca713b5c9 THE RUSH, made honest: only the pair reaches the book
Adversarial pass over the freshly-threaded recordVerdict pipeline found
three real ordering bugs on rush days, all of the same species: ticket
one's RAW verdict was reaching systems that should only ever see the
pair. The streak moved twice per rush (and an S on ticket one extended
it even when the pair collapsed), the regulars' book logged two visits,
and — the exploit — the Shadow star was awarded off ticket one's raw
grade: ace the first ticket, flub the second, keep the S★ on a day the
ledger records as B.

The rush stash/combine now sits directly after the per-dish adjustments
(Critic and Mother judge each DISH — that part was right) and before
streak, book, star and daily, so ticket one returns early having touched
nothing, and the pair flows through everything else exactly once.

Also fixed the falsy-zero trap the stash simplification introduced: a
0.0 ticket one made `this.rushFirst ?` read as "no rush", re-dealing
the SAME order for ticket two — all three checks are `!== null` now.

Verified live on day 25: after ticket one, streak/visits/ledger byte-
identical to before it; after the pair, one visit, 41s decay 7.6→7.3,
one write.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 02:03:37 +10:00
type-two
3ecb6b59a3 THE RUSH: two tickets on the rail, and the pass is a clock
Endless days past the card now deal a rush about one day in four
(deterministic by day number, never on a Critic day — one cruelty at a
time). Ticket one serves into silence: 'on the pass. The second is
already up — NEXT. Cook.' Nothing is written. The same day re-deals from
a twisted seed — a different order, still reload-proof — and while you
cook it, ticket one is dying: -0.5 per minute on the pass, capped at
1.5. The pair closes as ONE ledger line, the mean of what you served
and what survived the wait.

The write block behind every verdict (total, best, save) lived in nine
identical copies at nine call sites; it now lives once, inside
recordVerdict, which is what made deferring ticket one a three-line
change instead of a surgery.

Verified live on day 25: rush banner, ticket one 7.6 parked unwritten,
TICKET TWO banner on the re-deal (a prep day — different order
confirmed), 155s on the pass decayed 7.6 to 6.3, pair scored 6.7,
written once. Rush state clears on setDay, startDaily, and day advance.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 01:51:01 +10:00
type-two
ef66e1aa7a THE MOTHER: a sourdough starter that lives in your save and holds a grudge
After five days survived, a jar appears on the title shelf. She wants
feeding — one click — every new day you reach, and the stamps are maxDay
based so replaying old days for the S never starves her. Fed within a
day, toaster-days earn +0.2 and the line 'The starter is lively — the
crumb held. (She did that, not you.)'. Three days forgotten and the jar
sulks, and the crumb knows. Five and she goes still: the button becomes
a funeral, and one click begins THE MOTHER (second of her name) — she
forgives, mostly. The deaths counter never resets. Station days (grill,
pan, cold, steak, eggs, poach) are honestly out of her reach.

Also hardened loadSave against non-finite numbers: NaN and Infinity
stringify to null, and one bad number was enough to rot every derived
stat downstream. Every numeric field now refuses them on the way in.

Verified live: jar born fed at maxDay, starved to a funeral, resurrected
as second-of-her-name (deaths=1 persisted), lively bonus fired on a
bruschetta (9.6 -> 9.8 with the line), sulk line at gap 3, save numbers
intact after the full arc.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 01:45:44 +10:00
type-two
9eb1c2db3a THE THUMBS: phones get their keys back
A touch strip for devices with no wheel and no keyboard — which is most
of the phones the SHARE links land on. Each button IS its key: pointer
down dispatches the real keydown, pointer up the real keyup, so the
holds (crack charge, spoon drain) behave exactly like the physical key,
and the heat arrows synthesise real wheel ticks at the canvas. Drags
already worked — the input layer was pointer-events from birth.

Only appears on touch devices (desktop cooks keep a clean bench; __tp()
summons it for the curious), hides while the title is up, and the small-
screen pass folds the rest: the title stacks and stops overflowing, the
ledger fits, the judge's eye steps up out of the strip's way.

Verified at 375x812: no horizontal scroll, buttons drive the real input
path (heat arrows walked the poach knob 0 to 7 through the button
handlers), pad hidden at title / shown in service, day-21 pot playable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 01:37:37 +10:00
type-two
146c6f36c1 THE CRITIC: every 10th day, the flaw is the headline
Days 10, 20, 30... he seats himself. The ticket warns you in red, the
scorecard portrait drains to grayscale, and the scoring changes shape:
the worst row on the card counts DOUBLE. Perfection survives him
untouched (doubling the worst weight of a flawless run moves nothing) —
anything less gets leaned on, exactly where it hurts. His lines are his
own: an S gets 'I came to write a takedown. I cannot. This is the worse
outcome — for me.'; an A learns that 'competent' has never once appeared
in a good review; a failure gets mercy in print, which is worse.

The Daily is exempt (same deal for everyone means the same judge), and
demonstrations reset the flag — he doesn't review his own cooking.

Verified live on day 20: banner up, grayscale portrait, critic line on
the card, day 21 clean; flawless eggs still 10.0 by construction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 01:32:36 +10:00
type-two
a8ae26f2d4 THE SHADOW: hard mode is him saying nothing
Once you have pulled a single S, the title offers THE INSPECTOR'S SHADOW:
arm it and the kitchen's words go dark for the whole run — no state
words, no wobble words, no browning gauge. The ticket still asks and the
controls hint survives, but the read is the face in the corner, the
sound of the pan, and the food itself. Every diegetic channel we built
was practice for playing without the subtitles.

Take an S with the words off and the ledger chip earns a star (S★), the
save remembers it (shadowed, per day, survives loadSave's corruption
filter — which was quietly dropping unknown fields; fixed), and he gives
you the only compliment he has: 'In the silence, too. I have nothing
left to teach you. ★'

Verified live: toggle arms, body.shadow hides the words (computed
visibility checked), S-in-silence writes shadowed.day20, star renders on
the chip after reload.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 17:46:44 +10:00
type-two
2a01a6e3a0 THE EYE: he watches you cook now
A small portrait in the corner of every service — not a meter, a FACE,
which is the oldest diegetic read there is. He drifts neutral, and the
kitchen pokes him at the moments that matter: a flip earns an intrigued
lean-in, burnt butter a flat disappointment, the smoke alarm and the
rotten egg get the full horrified stare (held four seconds for the egg —
he heard that), a clean separation impresses him, a flare-up on the
coals gets the little fire he pretends not to enjoy, and an egg left in
rolling boil is watched with open disappointment until you fix it. He
hides for the title (he doesn't watch you read the menu) and hands over
to the full portrait on the scorecard.

One widget (src/ui/eye.ts), fed one-liners from four scenes; moods decay
back to neutral on their own, same starvation doctrine as the foley beds.

Verified live: neutral through service, intrigued on flip, hidden at
title and scorecard, portrait swap confirmed in the real click-through.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 17:41:49 +10:00
type-two
c76b62dfa9 THE FOLEY: the kitchen narrates itself, and the bench radio only knows one station
Continuous beds join the one-shot synths: a bed is a looping noise voice a
scene FEEDS every frame, and it starves to silence on its own when the
feeding stops — no scene has to remember to turn sound off. The pan's
sizzle follows butter and heat (burnt butter dulls the voice, 5600Hz down
to 2400); the grill's follows the heat actually UNDER the meat, so the
cool side goes quiet; the pot rumbles up with the water and throws
bubbles — 0.15/s cold, 3/s in the shiver, 13/s at a rolling boil, each
one a little upward chirp because that is what a collapsing bubble does.
The sound IS the thermometer, same doctrine as the words.

And R, anywhere: the bench radio. 84 BPM, Am-F-C-G through a bandpass
shaped like a three-inch speaker, tape wow on every note, a hash-picked
pentatonic melody that never changes — the same tune every service, the
way a real kitchen radio only knows one station. Hiss included.

Verified: radio toggles with the word, pan/grill/poach days run clean
through the live bed paths with the context awake.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 17:36:13 +10:00
type-two
322aeec16d THE GHOST: '▶ watch him do it' — every day driver replays as a live demo
The dev harness ships in production now, because it turned out to be the
best teacher in the building. Any ticket whose day has a registered driver
grows a '▶ watch him do it' button: the same deterministic script that
verifies the day in CI performs it live on the real scene, real input,
real judge — then the judge waves it off ('A demonstration. None of it
counts. Now you.'), the save restores from a snapshot, and the day deals
again for the player to play for real.

The hard part was time. A demo must ride the app's own rAF loop to be
watchable — but hidden tabs pause rAF AND throttle setTimeout down to one
fire per MINUTE, which froze the show mid-egg. F(n) now has three gears:
sync (verification), rAF-riding (visible demo), and fast-forward when
document.hidden — no audience, no theatre; he finishes the dish while
you're away. Also fixed a straggler from the async conversion: the
egg-day float loop fired six un-awaited drags concurrently, so nothing
floated, the liar egg passed as safe, and the demo cracked it into the
bowl (5.6/10 with a fart). Awaited, the glass does its work.

Verified live, hidden-tab and visible: demos score 15:9.4 16:8.0 17:10.0
19:9.0 20:10.0 21:9.9 22:9.9, localStorage untouched during playback,
NEXT restores the snapshot and replays the day for real.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 17:27:07 +10:00
type-two
56389dabab THE DAILY DEAL + SHARE: one deal for everyone, a result you can paste
THE DAILY — a button on the title deals one shared order per real date:
the seed IS the date (FNV hash), so everyone on Earth gets the same deal
today, station and all. Scored into save.daily[date] (best-of), the
campaign day counter untouched — NEXT after a daily returns you to your
regular day. The title button becomes 'THE DAILY ✓ 9.9' once played.
Judge closes with 'The Daily. Same deal for everyone today — come back
tomorrow.'

SHARE — a button on every verdict copies a one-line result to the
clipboard: 'TOASTSIM daily 2026-07-20 — 🥚 S 9.9/10 · partly.party/
toastsim' (station emoji per order; campaign days read 'day N'). The
wordle-paste for toast.

Verified live: daily deals deterministically (same date → same order,
today's is a poach), played to S 9.9, best saved, share text exact,
NEXT preserved campaign day 20, title shows the checkmark.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 16:44:41 +10:00
type-two
f74ee61bbd THE POLAROID: a shareable verdict card from the judge screen
A POLAROID button on the scorecard re-renders the dish under the
spotlight, centre-crops it into a cream polaroid frame, and stamps the
day, the grade (coloured), the total, and the judge's line of the day
in italics — 'partly.party/toastsim' in the corner — then downloads as
toastsim-dayN-GRADE.png. The judge's lines are the most quotable thing
in the game; now they travel.

Verified: PNG downloads (real capture — frame cream, photo window lit,
not the black-buffer trap; explicit re-render before toDataURL).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 04:57:36 +10:00
type-two
9c4e366b65 The benedict (day 22) + the kitchen's memory: regulars and streaks
THE BENEDICT — the first toast+poach composition: day 22 toasts the
bread in the kitchen, then onPopped routes to the poach pot instead of
the drawer (the bruschetta's oven trick, generalized); judgeBenedict
reads THE BREAD off the slice (browning/evenness/char) and THE POACH off
the egg (white/wobble/form), and The Plate sogs BOTH if you serve the
egg undrained — 'You put a wet egg on good toast. Now it is neither.'
Also in the endless rotation (electric pot past day 28).

THE KITCHEN REMEMBERS — recordVerdict on every serve path:
- Streaks: consecutive S grades tracked in the save; the judge notices
  at 3 ('I am beginning to suspect competence.'), 5 ('I have started a
  file.'), every 5 after, and eulogizes a broken one ('It was 7. We do
  not speak of it.').
- The regulars: per-customer visits/last/best in the save. Return
  tickets read 'visit №3 — last time: S' (or '...F. they came BACK.'),
  and the judge compares: worse-than-last-time and redemption both get
  a line. Delivered via JudgeView.extraLines, one-shot per verdict.

Verified live: day 22 starts at the kitchen, poaches after the pop,
combined card 9.1/10 with the architecture line; streak 1->2->3 fires
the line at three; day-20 rebooking shows the visit note.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 04:52:36 +10:00
type-two
bffee40c6f THE POACH: day 21 — the shiver, the vortex, the drop, the wobble test
The near-game, treated with respect (masterplan P3). sim/poach.ts (pure,
rides the heat-source bone): the pot chases the knob on the fuel's lag
and the poach wants the SHIVER (5.8-7.4) — cooler rags the white on the
way in, a rolling boil (>8.2) tears it the whole time. The VORTEX is the
reamer-twist at pot scale: swirl builds it, it DECAYS (swirl then drop,
not swirl then admire), and the strength at the drop is the judged form.
Two-layer set: white first, the yolk only firms once the white is mostly
there — lift in the window (white >=0.85, yolk <=0.35) or you've boiled
an egg the long way. Then DRAIN on the slotted spoon. Stirring a dropped
egg is vandalism (rags it directly).

scenes/poach.ts: WHEEL flame, circular-drag swirl (real angular sweep),
SPACE drop, L lift, hold-to-drain, ENTER serves; vortex streaks that spin
and fade, shiver-vs-boil bubbles, the white gathering tighter as it sets,
a hard yolk going pale. judgePoach: THE POACH (White/Wobble/Form) + The
Drain; the wobble line is the dossier's: 'It should tremble. Like you,
right now.' Day 21 authored + poach in the endless rotation (electric
pot past day 27).

Verified with REAL input: shiver + 91% vortex + lift-at-tremble + drain
= 9.9/10 'it TREMBLES — perfect', 'one neat comet (vortex 91%)'; still
water into a rolling boil, overstayed, undrained = 3.6/10, 'a pot of
rags', 'it does not move at all'.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 02:34:50 +10:00
type-two
717ef56150 THE EGGS: day 20 — the crack, the separation, the float test, the beat
The egg dossier lands (atlas §1.5). sim/eggs.ts (pure): a carton of six,
each egg rolling freshness off the carton's age; the FLOAT TEST reads it
out (sinks flat / stands up / FLOATS) for free; the CRACK is a strength
window (soft = nothing, window = clean, hard = shell in the whites, fish
the bits out); SEPARATION passes the yolk shell-to-shell (fast breaks it,
old yolks are weaker — the float test also tells you how gently to pass);
a ROTTEN egg cracked blind ruins the bowl: green puff + a seed-varied
synth FART (audio.fart — pitch/length/wobble per egg, the tremolo loses
its nerve halfway). D dumps a ruined bowl; the dump clears the whites but
NOT the judge's memory (rottenCracked survives — no sin-laundering).
An old carton always has at least one liar in it (deterministic; the
glass finds it — floor, not lottery).

scenes/eggbench.ts: drag-to-glass float test, drag-to-rim + HOLD-SPACE
charge crack, alternate arrows to separate, click shell bits to fish.
judgeEggs: THE EGGS (Count/Shell/Whites) + The Nose; full line bank
('There is a GLASS OF WATER on the bench. It exists for exactly this.').
Day 20 authored (pavlova order) + eggs arm in the procedural rotation
(older cartons + need 4 past day 26).

Verified via REAL input: float-all + dodge + patient passes = 10.0/10;
blind rotten crack = the beat, dump recovers whites, The Nose remembers
= 6.3. Sim bars: crack window, float words, old-yolk weakness, fishing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 01:53:08 +10:00
type-two
64bf04c0b6 The endless mode uses the whole kitchen; the ledger makes days replayable
1) Station-mix procedural days: past day 19 the kitchen deals from every
station it taught — toast ~34%, then prep cuts (technique onion dice past
21), juice (better reamers + more pips later), the grill (4 pieces past
24), the pan (electric arrives 22, induction 26), the rib eye (random
grain, electric past 23), and the fridge delivery. Each with its own
ticket voice. Deals are seeded BY DAY: a reload re-deals the identical
order (no reroll-scumming) and the harness can reproduce any day.

2) THE LEDGER on the title: one chip per reached day with your best grade
stamped on it (S green through F red), click to replay. save.maxDay
tracks furthest-ever progress so replaying day 5 for the S never locks
day 19 away; old saves migrate (maxDay defaults to day).

Verified live: 26-day sweep deals all 7 stations, deterministic re-deals;
chip click starts day 5; procedural day-26 steak (electric fuel ramp)
played end-to-end: rested, cut across a random grain, 9.1/10.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 01:27:25 +10:00
type-two
1e5d069c99 Rib-eye review fixes: rest-per-cut, no-cut guard, sear evenness, GPU leak (6 findings)
Adversarial review of the steak day found seven issues; fixed six:
- REST SCORE gamed by slice count (bug): flood summed over cuts, so
  fewer slices faked a better rest. Now scored on the PER-CUT bleed
  (moisture at the moment you committed) — cutting 1 vs 6 slices at the
  same rest is identical (verified 0.682 == 0.682); slice count is pure
  visual now.
- ZERO-CUT SERVE rewarded (design): an uncut steak scored restScore 1.0.
  steakResult now gives 0 rest with no cuts, and the board blocks serving
  until you've actually cut.
- UNEVEN SEAR masked (edge-case): The Cook used only (down+up)/2, so one
  charred + one raw face averaged to 'perfect'. sideGap now threads pan ->
  board and docks cookScore (an uneven sear isn't the right steak).
- GPU LEAK (bug): grain lines + slice seams were removed from the scene
  but never disposed across rounds. reset()/dispose() now free them.
- panIngredient could go stale onto a steak day (nit): cleared in beginDay.
- FLOOD FEEDBACK on three different scales (nit): the puddle + '% bled out'
  HUD now track the same /1.1 the judge scores on — what you see is what
  he tastes.
Left #7 (pan 'well-seared' tell sits hotter than a MEDIUM steak wants):
over-searing a medium is correctly a Cook penalty; S stays reachable.

Verified: good rest+across -> S(9.4); cut early -> 5.3 (bled); rest score
count-independent; zero-cut -> rest 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 23:29:05 +10:00
type-two
f81d1b5c82 The rib eye is playable: day 19, sear -> REST -> cut (it bleeds)
Wired steak.ts into a live day. scenes/steakboard.ts — the resting board:
the steak lands off the pan full of juice, DRAG across the grain (pale
fibre lines shown) to slice; cut it before it rests and a dark juice
puddle FLOODS the board. Day 19 routes pan-sear -> resting board -> judge
(pan's two seared faces carry the interior doneness). judgeSteak scores
THE STEAK (The Cook / The Rest / The Cut) + The Board, with a steakhouse-
grandfather line bank ('You cut it the second it left the pan. It bled out
on the board. I watched the whole thing.').

Verified live end-to-end: rested + across grain -> S (9.5); cut hot -> 'it
bled' -> 5.4; rested but along the grain -> 'chewy' -> 6.9. The three acts
each move the grade. Real-drag cutting confirmed (4 slices, flood 6.9).

Also fixed a latent crash: verdictLines could call fill() with an
undefined line when the best-scoring row (e.g. The Board) had no BANK
entry — now guarded.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 23:05:45 +10:00
type-two
848f4b3fcb The rib eye sim: rest clock (it bleeds), grain-aware cut — verified
src/sim/steak.ts — the crown jewel's three acts as pure sim. REST: a
steak comes off the pan at moisture 0.95 and relaxes toward 0.6 over ~20s;
cut it hot and the juice FLOODS the board, rest it and it's a whisper (no
UI timer — the meat tells you). CUT: a grain axis; across = |sin(cut-grain)|
tender, along = a rope; knife wobble tears instead of slices. COOK:
interior doneness vs the rare/medium/well target.

Verified via t.steakMarquee: cut hot -> flood 10.5 (rest 0); rested ->
flood 0 (rest 1); across grain cut 1.0 vs along grain 0; wobble -> 6 tears
(cut 0.2). The 'it bleeds' feel John wanted, measured.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 22:54:28 +10:00
type-two
afe2bf174b Cold-chain review fixes: reload-dodge, crash-safety, fair ceiling (6 findings)
Adversarial review of the cross-day loop found six real issues; all fixed
and re-verified:
- RELOAD EXPLOIT (edge-case): enterPan consumed + persisted fridge stock
  at day-START, so a reload mid-pan re-fetched null and DODGED the rot-cap
  (and honest players lost the ingredient). Now the consumed stock is held
  (pendingFridge) and committed atomically with the day advance in onNext —
  a reload re-fetches the same item deterministically. Verified: after
  entering day 18 the mushroom is still in save.fridge until the day turns.
- CRASH (edge-case): loadSave passed save.fridge unvalidated → restoreStore
  hard-crashed on a corrupt/unknown-zone entry. restoreStore is now
  corruption-tolerant (guards Array + known zone). Verified: garbage save
  → no crash, falls back to a fresh ingredient.
- FAIRNESS (design): The Ingredient scored linearly on quality, so even a
  perfectly-stored 'fresh' mushroom (0.77) capped the ceiling below S. Now
  scored on the freshness BAND — fresh = full marks — so optimal cold
  storage reaches S (verified 0.77 'fresh' from the coldest back shelf),
  use-soon dents (A), off/dangerous near-zero + hard-cap (F).
- word/quality mismatch: usableQuality's word now derives from the docked
  quality (a thawed item reads its real handed-over state).
- freezer thaw 0.82 -> 0.78: a 3-day-frozen mid-perishable (0.75) no longer
  out-scores the coldest fridge shelf (0.77) — the texture cost is real.
- rename-safety: FRIDGE_MUSHROOM_ID shared constant couples the two days.

Gradient verified: optimal->S, decent->A(7.4), bad->F(3.0); plain pan 8.0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 22:15:39 +10:00
type-two
3f91bedc7f The cold chain closes the loop: stock day 17, cook it day 18
The fridge persists in the save (serializeStore/restoreStore/fetch in
coldchain.ts). Day 18 'the lunch rush' pan-sears the very mushrooms you
stored on day 17 — fetched FIFO at whatever freshness your storage left
them. judgePan gains 'The Ingredient' row; a dangerous/off one HARD-CAPS
the total (no sear saves rot). Judge line bank for the fetched ingredient.

Verified live: identical cook (72%/57% sear), well-stored mushroom serves
7.4/10, badly-stored (warm door) serves the SAME sear but 3.0/10 (F) with
'The Ingredient: off the shelf — dangerous' capping it. Plain pan day 16
unaffected (8.0) — backward compatible. Day-17 delivery gains mushrooms;
Order.pan.fromFridge flag; save carries the stock.

Harness: t.coldChainArc(good?).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 21:52:27 +10:00
type-two
2085f68655 The cold chain is playable: day 17, put the delivery away
Wired coldchain.ts into a live day. scenes/fridge.ts — a front-view fridge
with zones (freezer drawer / cold back / mid shelves / warm door + glass
shelves), a delivery you drag in, items that recolour as they spoil. Day
17 'put it away': stock it right, ENTER shuts the door (3 days pass),
ENTER again and the inspector opens it. judgeFridge scores THE FRIDGE
(The Cold / Hygiene) + The Waste; hygiene-inspector line bank ('Raw
chicken dripping onto the salad. That is not a fridge, that is a crime
scene with a light in it.').

Sim fix: contamination drips across the whole fridge interior by shelf
height (raw-on-bottom is the real lesson), not just within one zone.

Verified live: day 17 routes to the fridge; good storage (raw low,
perishables cold, RTE high) grades S (10.0); bad storage (raw high in the
warm door, crammed) grades C (3.5) — 38% off, 38% dripped on, 3 wasted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 21:22:20 +10:00
type-two
a2ecfdda8f Atlas P10.5: the cold chain doctrine (spoilage/placement/rotation/freezer)
Cure's defensive twin, spec'd around the verified coldchain.ts sim. The
scene + cross-day carryover + inspector hygiene row are the remaining
build to make it playable; composes with the shop, the eggs' float test,
and the cure (same day-clock, opposite sign).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 20:56:55 +10:00
type-two
e7ed091077 The cold chain: spoilage, placement, rotation, contamination (sim)
src/sim/coldchain.ts — the temperature clock pointed the other way. Every
perishable has a freshness 1->0 that only falls, on the DAY clock (the cure
system's defensive twin); how fast is decided by the zone's cold. Plus the
three disciplines on top: PLACEMENT (fridge zones door->back warmest->coldest,
cram it and airflow chokes, raw-over-ready-to-eat drips and contaminates),
ROTATION (FIFO pull -- use the oldest or it rots in the back = waste), and
THE FREEZER (stops the clock but the thaw costs texture). Fresh -> use-soon
-> off -> dangerous.

Verified headless (t.coldZones/coldRotation/coldOverpack/coldContam/coldFreezer):
- zones (raw fish, 3 days): bench/door dangerous, fridge off, back use-soon,
  freezer fresh (0.94) -- a real placement gradient.
- rotation: FIFO 0 waste vs newest-first 2 tubs rotted.
- overpack: crammed fridge spoils milk to 0 vs 0.29 sparse.
- contamination: raw-above-salad -> unsafe (q 0.29); same-shelf -> safe (0.88).
- freezer: preserves 0.93 the bench lost (0), thaw docks usable to 0.76.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 20:55:55 +10:00
type-two
8b13ac68a8 M17 pan shipped as a playable day (16); heat brief complete
Routed the pan into a real order: day 16 'pan-sear the mushrooms on the
gas' — butter in, wait for the FOAM not the smoke, land the food, brown
both faces, baste in the noisette. judgePan scores THE SEAR / BOTH SIDES
/ THE BUTTER with a butter-snob line bank. New scorecard group THE PAN;
the cast-iron skillet GLB renders with a fuel-coloured flame.

Balance fix (the floor): food shields the butter once it lands, so a
clean cook keeps the butter at noisette instead of racing to burnt — a
full sear is now winnable. Emergent fuel character kept: induction sears
both sides dead-even (0.016), gas flares the first side so you turn it
early (0.085) — both under the 0.1 bar.

Verified live: day 16 routes to the pan, plays end to end, grades A;
judge nails it ('cooked in different postcodes... foamed not burnt, you
were watching'). Cast-iron pan + butter + searing mushroom on screen.

Heat brief M-H1..M-H6 + M-H4 all shipped and live.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 20:43:30 +10:00
type-two
c528ba49ab M17 + M-H4: the pan, born fuel-aware — butter, the flip, the baste
src/sim/pan.ts (pure) — the toaster generalized with two new things:
BUTTER as a state machine (cold->melting->foaming->noisette->burnt, keyed
on pan temp x time) and a second SIDE. The pan reads a HeatSource, so the
knob commands a target and the pan temp chases it on the fuel's lag; gas
foams fast, electric drags and remembers, induction is exact and never
flares. Per-side sear Fields (down side browns fast, up side radiant-slow
until you flip or baste); baste paints hot butter onto the up face (noisette
= bonus, burnt = bitterness); rendered fat flares a flame, never induction.

Verified headless (all M17 + M-H4 bars):
- butter foaming window shrinks with heat: 10.4s @4, 6.0s @7, 4.6s @9.
- well-timed flip -> both sides within 0.008 (bar <0.1); never flip ->
  one side 1.2 (burnt), other 0.23 (raw).
- baste in noisette flags the bonus, up-side 0.073->0.433; burnt-butter
  baste writes bitterness 0.48.
- M-H4: noisette on gas 11.0s vs electric 14.4s (~3.4s faster); induction
  output pinned at knob 8 with fat searing (never flares).

Harness: t.panButter/panFlip/panNoFlip/panBaste/panFuel.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 20:30:34 +10:00
type-two
94a5f2f293 Heat M-H6 shipped: the charcoal grill is a playable day (day 15)
Routed the grill into a real order: day 15 'chuck these on the barbie'
sends the whole day outdoors — bank the coals hot one side / cool the
other, sear each piece then move it to the lee before the fat flares,
serve. judgeGrill scores THE SEAR / THE CHAR / THE GRILL with a char-snob
line bank ('There is no char on this. There is a memory of char. A
rumour.'). New scorecard group THE GRILL + grill/bruschetta order headers.

Balance retune (the floor): RENDER_AT 0.62 lifted above the good-sear
window so a clean sear is reachable with NO flare — sear-then-pull is a
reliable win, only overcooking invites the flames.

Verified end-to-end in-browser: day 15 routes to the grill; careful
sear-and-pull grades S (9.4/10), 100% well-seared, no carbon, no flares;
leaving pieces over the coals chars to carbon (0.9/10, 31 flares). All
M-H6 sim bars still green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 16:27:54 +10:00
type-two
ff1c3deafc Heat M-H6 scene: the charcoal grill, top-down and glowing
scenes/grill.ts (GrillView) — the coal bed rendered as an additive glow
texture from the zone Field, food pieces you drag between the sear zone
and the cool lee, flares that lick up when fat renders over hot coals.
Registered in game.ts (hidden, in hideAllScenes, grillView getter).
Harness t.grillDemo drives the real scene end-to-end.

Verified in-browser: banked a bright hot-left bed, placed food across the
gradient, cooked through the real View — bed glows, food browns, flares
fire. Screenshot confirms the two-zone gradient reads at a glance.
Framing fix: glow above the grate, camera overhead, low open bowl (the
kettle GLB is a closed dome that occludes a top-down cook).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 16:14:04 +10:00
type-two
81c0151975 Heat M-H6 sim: charcoal is a Field you arrange, not a dial
src/sim/charcoal.ts — the fuel with no knob. bankCoals paints a 2D heat
bed (a screaming zone + a cool lee); zoneDecayStep only cools it; food
sears by the coal heat beneath it; a rendering piece over hot coals
flares and chars until you move it. One bed, two outcomes by placement.

Verified (t.zoneStdev/zoneDecay/grillTwoZones/grillFlareDodge):
- two-zone bed stdev 0.303 (bar >0.25) — a real gradient.
- bed ceiling monotone-decays 1.68->0.61 over 90s (sear early, hold late).
- hot-zone piece sears 0.72 while cool-lee piece holds at 0.
- FLARE DODGE: left over coals = done 2.12 / 46 flares (carbon); moved to
  the lee in time = 0.66 clean sear / 0 flares. Active play is the floor.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 16:04:05 +10:00
type-two
e6509479d2 Heat M-H3 (environment) + M-H5 noise: wind, the windbreak floor, the ZAP
M-H3 environment sim: newOutdoorEnv/addSmoke/SMOKE_ZAP. Wind perturbs a
flame's output AND steals its heat; a windbreak (sheltered) forces it
flat; induction shrugs wind off; indoor smoke trips the ZAP exactly once.
Verified: windy gas stdev 0.2635 mean 5.66, sheltered stdev 0.0065 mean
6.99, induction stdev 0, zap fires 1x indoors / 0x outdoors.

M-H5 noise half: cheap_electric hob stdev 0.1244 (>0.12) vs induction
0.0198 (<0.04) vs fan 0.0064 — cheap gear is Field noise, not a tax. The
'both reach 10/10' half waits on the M17 pan.

Harness: t.envVar, t.zapTest, t.hobNoise.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 15:55:34 +10:00
type-two
f6404586cd Heat-source asset cast: 14 GLBs (cooktops, pans, BBQ, outdoor, utensils)
gas/electric/induction hobs, camp stove, cast iron + frying pan, wok,
saucepan, kettle + gas BBQ, spatula, wooden spoon, tongs, ladle. Seeds
181-219. Props await their stations (M17 pan, M-H6 charcoal, wok night).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 15:22:27 +10:00
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
9e315bd7a9 gen-assets: heat lane — cooktops, pans, outdoor BBQ, utensils (seeds 181-219)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 13:45:40 +10:00
type-two
10b27e37fa Drawer drag: soften the grab spring + clamp held-piece velocity
Grabbing a buried piece built up spring tension against the pieces on
top, then broke free at ~9g (force cap 90*m, no speed limit) and batted
the whole pile out of the drawer. Softened the spring (K 165->55, damping
13->22, force cap 90->22) so it EASES toward the cursor, and added the
real fix: a hard velocity clamp (3.2 u/s) on the held piece so it can
never become a projectile — every downstream momentum transfer is bounded
by that one line.

Verified (harness drives the identical dragGrabbed path): a violent yank
now peaks the pile at 2.89 u/s with ZERO pieces flung out (was: everything
flew off screen); a controlled lift still raises a piece to y=2.09 over
the 1.47 rim and commits it out. Fishing still works, catapult gone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 13:31:30 +10:00
type-two
7b6005cf86 Fix: cutlery drawer swallowed all clicks (invisible full-screen panel)
The drawer's info card lives in a .drawer-panel that's inset:0 full-bleed
with pointer-events:none — but the base rule '#ui > *' re-enables
pointer-events:auto and its ID specificity (1,0,0) beats the class's
(0,1,0), so the invisible sheet covered the canvas and ate every drag.
The kitchen panel is a small positioned card so it never covered
anything; the drawer is the first full-bleed canvas-drag scene, which is
why it surfaced here. Scoped the container's rule to '#ui > .drawer-panel'
so it wins the specificity war and clicks reach the 3D pieces.

Verified: real DOM pointerdown now reaches the canvas, a piece grabs on
press and lifts (y 0.4). Harness never caught it — it injects input
directly, bypassing DOM pointer-events entirely.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 12:43:10 +10:00
type-two
a1ae1be6d6 Atlas grows: the egg dossier, the air fryer, and P12 THE CAREER
Egg dossier (feeds Brief 4/P3): the crack, shell-half separation with
yolk integrity, the float test as free information (so the rotten-egg
fart beat is always YOUR fault, seed-varied so it lands every time),
mayonnaise as a two-hand emulsion with the real rescue, the stick
blender pulse doctrine + THE ZAP (300ms skeleton flash, once a day max,
'I saw your bones, love.').

Air fryer (P6): arrangement scored by Clark-Evans INVERTED (spacing
with margins, contact shadows stay pale in the Field), the shake, and
CookPrograms — convoluted multi-step recipe cards (180 for 5, turn,
190 for 15) with no auto-advance; several programs at once is the
service clock in miniature.

P12 THE CAREER: money via tips, the paper catalogue, equipment quality
as tiers-modifying-existing-numbers (small board rolls dice onto the
floor, copper bowl genuinely whips faster, heavy pan smooths the sear
Field — noise and events, never flat score taxes), knife dulling +
honing/whetstone, a three-branch skill tree where every node is a verb
(stat-stick nodes banned), and rare primo offers the recipes notice.
Hooks stubbed from P6 on so it bolts on without refactors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 16:48:06 +10:00
type-two
3f3b073da9 THE KITCHEN ATLAS: phases P6-P11 — every medium, tool, and dish
Three new doctrines: IRREVERSIBILITY (seasoning is a one-way vector
walk — taste to see, dilute to compromise, never subtract), INFORMATION
TOOLS (thermometer/taste-spoon reveal instead of change; knowing costs
time, the judge respects a confident blind serve), SPEC CUTS (brunoise/
julienne/chiffonade/tourne as data on the existing cutting sim).

P6 water & oil (the fryer's menace), P7 the spice rack (curry capstone,
chili's 8s lag trap, bloom/toast/grind), P8 the knife roll (knife SET
as data, mandoline, melon baller, PREP DRILLS score-attack mode), P9
directed heat (the blowtorch paints a browning Field; creme brulee ends
in the judge's spoon-tap CRACK test), P10 cold & time (cures that span
real days, the churn, granita interrupts), P11 the banquet (fork-anchor
carving, three plates landing hot at once, credits).

Seeds 400-629 reserved per phase, same-seed families for prop sets.
Kill list live: microwave is a cameo, wellington waits, sous vide is a
joke prop. Everything rides the bones — new poetry on proven physics.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 16:35:06 +10:00
type-two
5d29bb2d96 Build Brief 3: FIRE — the pan, the rib eye, the salt doctrine
P2 cut from MASTERPLAN for an Opus 4.8 session to execute cold:
M16 salt (the dose-curve bone, two salts, the timing matrix and its
named sin), M17 the pan (butter state machine, sear Field, flip, baste
— mushrooms and bell pepper as first customers), M18 rib eye (season/
cook/rest/cut/pair — the early-cut juice flood is the lesson, no UI
timer, the steak's slump is the clock), M19 the steak day. Working
agreements + every trap this session paid for (assetUrl subpath, born-
hidden panels, stale-harness guard, m4pro retries, worktree hygiene).
Asset batch seeds 180-209. Ends with: deploy, then cut Brief 4 from P3.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 16:00:33 +10:00
type-two
db31273fc9 Deploy-base-aware asset URLs; live at partly.party/toastsim
Absolute '/assets/...' paths 404 under the /toastsim/ subpath (and the
root /assets/ URL is another game's — returns 200 with wrong content,
the classic partly.party trap). assetUrl() prefixes BASE_URL inside
loadProp + the four direct loads (bench/backdrop textures, title art,
judge portraits). Verified live: JS application/javascript, toaster.glb
4.5MB binary, title_art image/png, title screen renders.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 15:58:18 +10:00
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
type-two
46b1d8fd72 Merge branch 'worktree-agent-aa2e6cce1a128c28b' 2026-07-18 15:26:40 +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
type-two
6f74d31469 Kitchen panel no longer leaks over station scenes
It was born visible — a day starting at a station (prep/juice/grate)
never passes through kitchen.enter()/exit(), so the BROWNING panel sat
over every station HUD. Hidden at construction like every other scene;
verified hidden at bench, shown after handoff, hidden at juice.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 15:18:06 +10:00
type-two
8f5641a57f Pieces render true to pattern: dice = chunk grid, wedges = exploded sectors
Dice show the criss-cross (drawn 1.6x true size — honest proportions
vanish against the board at game camera distance); wedges explode 0.28
from center so gaps and uneven angles read. Slices keep the fan. Plus
scripts/deploy.sh (partly.party/toastsim, cratewars pattern) — pending
an ssh permission grant to run the final docker cp step.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 15:13:21 +10:00
type-two
03e3d046fc Pantry cast GLBs: three mushrooms + bell pepper
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 21:49:00 +10:00