diff --git a/LANEHANDOVER.md b/LANEHANDOVER.md index 7191c46..cc1f2d7 100644 --- a/LANEHANDOVER.md +++ b/LANEHANDOVER.md @@ -2121,3 +2121,125 @@ flicker). Recorded in SPACES.md so nobody burns farm time chasing a preview. ### Verified in pane Dance floor at game scale: disco ball has real mirror-tile detail, decks show their platters, speakers read as cabinets, truss reads overhead. Screenshot. + +## SESSION — FABLE-SOLO-26 · 2026-07-28 + +**Branch:** main (solo, Fable) · **Gate:** lint ✓ build ✓ test ✓ (821 tests, 56 files) +Three jobs: four rooms instead of one, 48 new sprites, and a review pass that +found two bugs in my own refactor. + +### 1. The venue ladder gets four actual rooms (docs/VENUES.md) +`data/venues.ts` has always listed four venues; they shared ONE floor map and +differed only by difficulty knobs, so a promotion you survived a week for looked +exactly like the room you had just left. + +- `venueMap.ts` grows a **`FloorLayout`** contract (map + props + lights + posts + + probes + palette) and the `newGrid()` primitives every room is painted with. + **Voltage stays IN venueMap** rather than moving to `layouts/` — the new rooms + import primitives from it, and a `layouts/voltage.ts` would close an import + cycle for no benefit. +- Three new rooms in `src/scenes/floor/layouts/`, authored in parallel against a + written brief: **The Royal** (horseshoe public bar you work from inside, pool + room, pokies corner, trough + two cubicles, a beer garden that is plainly the + nicest room in the pub, and a DJ *corner* that is a folding table because this + pub never built a booth), **Elevate** (open-air rooftop — most of the grid is + `void` sky, the DJ is on an unwalled plinth, you arrive by lift so the entry is + a lobby), **ROOM** (concrete warehouse, pillars you path around, central booth, + loading-dock smoking area, twelve lights in the whole venue). +- Per-venue **`palette`** gives each room its own materials without inventing + TileKinds — The Royal's floor is pub carpet, ROOM's is bare concrete, but both + are still `'floor'` to the sim, so pathing and every existing test are untouched. + +### 2. The station coordinates were Voltage's, and nothing said so +`FloorDemoScene` had six module constants — `TAPS_SPOT`, `DECKS_SPOT`, +`RACK_SPOT`, `DISH_SPOT`, `WATER_SPOT`, `TAPS_FRONT` — holding Voltage's tile +coordinates. Fine with one room; at four, a hardcoded `(47,3)` puts the bar shift +inside The Royal's pool room. `POSTS`/`PROBES` existed for exactly this and were +never wired to anything. Now each layout names its own stations and the scene +reads them from the layout. + +### 3. `tests/floor/layoutInvariants.ts` — the rulebook is executable +Perimeter holds · every walkable tile reachable from the entry (cubicle +interiors excepted, they are sealed on purpose) · anchors on walkable ground · +**posts not sealed** · probes have somewhere to stand · props on-grid · no staff +prop on a post tile. **Proved against Voltage BEFORE the three new rooms were +authored against it** — if the rulebook is wrong it is wrong on the known-good +room first. Add a room, add it to `LAYOUTS`, it gets checked. + +### 4. What the review pass found — two of them mine +Three independent reviewers checked each room against its brief. All three rooms +genuinely distinct, no brief items missing, no blockers. But `FloorView` was +still drawing two things at Voltage's coordinates, in the file the layout authors +were told not to touch: +- **Scan beams pivoted on a hardcoded `(38,23)`** — Voltage's mirror ball. In The + Royal that swept two beams across the empty middle of the pub, ~25 tiles from + that venue's dance floor. Now derived from the layout's own `discoball`; a + venue without one gets no beams, because the beams ARE the ball's light. +- **The resident DJ stood at a hardcoded `(55.5, 22.5)`** — Voltage's booth, which + in The Royal is the middle of the bistro. Now placed from the venue's own gear + and stepped clear of `posts.decks`. The beat-bob had the same constant in it. +- Elevate's **bartender stood on the exact tile the bar shift teleports you to** — + you would have played the shift from inside her. That is now an invariant. +- Elevate's two **mirrors were both `poster3`** (the author raised it as a contract + request and stubbed it honestly). `mirror` and `graffiti` kinds added. + +### 5. Assets: 37 generated props → 86 +`tools/gen/`: `assets.py` is the manifest (one entry per sprite, prompt + ship +size + rake + route), `run_batch.py` walks it, resumable, `--workers N`. +**Cloudflare Workers AI first** (10k free neurons/day, resets 00:00 UTC) falling +back to MODELBEAST `flux_local` automatically — the free tier was already spent +when this session started, confirmed from Cloudflare's own error body rather than +guessed, so the whole batch ran on the farm. + +**The mesh tier is the thing worth knowing.** TRELLIS's defaults +(`1024_cascade` + 2048px metal PBR bake) cost 5–12 min each on a queue that runs +ONE gpu job at a time — four hours for a 37-prop tileset. `mb.FAST_MESH` +(`512` + `vertex` baker + 200k faces) lands in ~90s, and A/B'd at ship size the +difference does not survive the downscale to 32px. Judge at ship size, as +SPACES.md already said. + +### 6. Three concurrency bugs, all introduced by parallelising the batch +Recorded because the serial version could not have had any of them: +1. **`props_import.py` manifest was a read-modify-write.** Two imports finishing + together each read the same list and the second write dropped the first's + entry — which ships a prop that is on disk but absent from the manifest, so + the game renders a placeholder over perfectly good art. Now `flock`ed. + `tools/gen/rebuild_manifest.py` repairs a manifest from the directory. +2. **`glb_to_sprite.py` wrote its Blender script to one shared path** and unlinked + it after — a finishing render deletes the script a starting one is about to + read. Now per-kind. +3. **`mb._req` raised `SystemExit`**, which is a `BaseException`, so `except + Exception` in the worker threads did not catch it: one stray 401 during a poll + killed a 43-asset run after a single asset. Now a normal `MBError`, with + backoff on transient 401/429/5xx. + +### How to see it +`npm run dev` → **`#floor:theRoyal`**, **`#floor:elevate`**, **`#floor:room`** +(plain `#floor`/F is still Voltage). Or play: week 1 IS The Royal now. +In Claude's preview pane the tab reports `document.hidden`, so drive frames by +hand — `const b=performance.now(); for(let i=1;i<=900;i++) window.game.loop.step(b+i*16.67)` +— and note the world only builds after the loader finishes 86 textures, which +needs real wall-clock time, not more `loop.step` calls. + +### Broke / known-wonky +- **ROOM's barkeep stands in a one-tile-wide service lane**, so the player walks + through him during a bar shift. Inherent to props-as-set-dressing in a lane + that narrow; not on a post tile, so the invariant passes. A bar with no + bartender looked worse. +- Reviewers noted **The Royal's central floor is sparse** (~500 walkable tiles, + four props) and **ROOM's east hall has no lights or anchors** (~23% of its + interior). Both are consistent with their briefs — the pub's dance floor is + meant to be an afterthought, ROOM is meant to be hard to navigate — but a + future pass could furnish them. +- ROOM's sodium signature (`0xe08420`) differs from bar amber (`0xd8a020`) only + in the green channel; under additive glow they are close. Deliberate but worth + an eye. +- `picnicTable` failed once mid-batch on a farm-side `404 no such job`; retried + clean. 86/86 kinds placed across the four venues have art — no placeholders. + +### Next session should +- Furnish The Royal's middle and light ROOM's east hall. +- Consider body-frames-under-outfits for patrons (ASSETS.md §2 / FABLE-SOLO-23) — + still the only patron-art path that does not blind the dress-code game. +- Cloudflare's neurons reset daily; a batch started after 00:00 UTC gets ~150 + free images before it ever touches the farm. diff --git a/public/props/ashUrn.png b/public/props/ashUrn.png new file mode 100644 index 0000000..23344d5 Binary files /dev/null and b/public/props/ashUrn.png differ diff --git a/public/props/ashtray.png b/public/props/ashtray.png new file mode 100644 index 0000000..6207161 Binary files /dev/null and b/public/props/ashtray.png differ diff --git a/public/props/bainMarie.png b/public/props/bainMarie.png new file mode 100644 index 0000000..047bb85 Binary files /dev/null and b/public/props/bainMarie.png differ diff --git a/public/props/balustrade.png b/public/props/balustrade.png new file mode 100644 index 0000000..967ea26 Binary files /dev/null and b/public/props/balustrade.png differ diff --git a/public/props/banquette.png b/public/props/banquette.png new file mode 100644 index 0000000..418fb0e Binary files /dev/null and b/public/props/banquette.png differ diff --git a/public/props/beerUmbrella.png b/public/props/beerUmbrella.png new file mode 100644 index 0000000..ee7f179 Binary files /dev/null and b/public/props/beerUmbrella.png differ diff --git a/public/props/brokenBasin.png b/public/props/brokenBasin.png new file mode 100644 index 0000000..7d283d5 Binary files /dev/null and b/public/props/brokenBasin.png differ diff --git a/public/props/cableSnake.png b/public/props/cableSnake.png new file mode 100644 index 0000000..fc5dbfe Binary files /dev/null and b/public/props/cableSnake.png differ diff --git a/public/props/canStack.png b/public/props/canStack.png new file mode 100644 index 0000000..b1e20ac Binary files /dev/null and b/public/props/canStack.png differ diff --git a/public/props/cdj.png b/public/props/cdj.png new file mode 100644 index 0000000..1b3c3e6 Binary files /dev/null and b/public/props/cdj.png differ diff --git a/public/props/champBucket.png b/public/props/champBucket.png new file mode 100644 index 0000000..a2b0ee3 Binary files /dev/null and b/public/props/champBucket.png differ diff --git a/public/props/cocktailBar.png b/public/props/cocktailBar.png new file mode 100644 index 0000000..8606e83 Binary files /dev/null and b/public/props/cocktailBar.png differ diff --git a/public/props/djPlinth.png b/public/props/djPlinth.png new file mode 100644 index 0000000..565ad6c Binary files /dev/null and b/public/props/djPlinth.png differ diff --git a/public/props/graffiti.png b/public/props/graffiti.png new file mode 100644 index 0000000..7e97e86 Binary files /dev/null and b/public/props/graffiti.png differ diff --git a/public/props/liftDoor.png b/public/props/liftDoor.png new file mode 100644 index 0000000..4e4af3f Binary files /dev/null and b/public/props/liftDoor.png differ diff --git a/public/props/loungeChair.png b/public/props/loungeChair.png new file mode 100644 index 0000000..3ffa713 Binary files /dev/null and b/public/props/loungeChair.png differ diff --git a/public/props/manifest.json b/public/props/manifest.json index 0c1f689..9a34a12 100644 --- a/public/props/manifest.json +++ b/public/props/manifest.json @@ -1,14 +1,26 @@ { "props": [ "arcade", + "ashUrn", + "ashtray", + "bainMarie", + "balustrade", + "banquette", "barFridge", "barShelf", + "beerUmbrella", "bistroTable", "boothLamp", "boothTable", + "brokenBasin", "buttBin", + "cableSnake", + "canStack", "carpet", + "cdj", + "champBucket", "cloak", + "cocktailBar", "crate", "cubicleDoor", "cueRack", @@ -16,32 +28,63 @@ "deck", "discoball", "dishwasher", + "djPlinth", "glassRack", "glassStack", + "graffiti", "handDryer", "highTable", "iceWell", "jukebox", "keg", + "liftDoor", + "loungeChair", + "marbleSink", + "mirror", "mixer", "monitor", "mopBucket", + "oldCouch", "pallet", "patioHeater", + "picnicTable", + "pillar", "plant", + "planter", + "plywoodBar", + "pokie", "poolTable", "poster1", "poster2", "poster3", + "raffleDrum", + "rollerDoor", + "ropePost", + "ruleBoard", "sinkRow", + "skylineCard", + "smokeMachine", "speaker", + "stackF1", + "staffBartender", + "staffDj", + "staffGlassie", + "staffSecurity", "stampPodium", "street_backdrop", + "street_elevate", + "street_room", + "street_theRoyal", + "strobe", + "subBass", + "tabBoard", "taps", "till", "trough", "truss", + "tvSport", "urinal", - "wetFloor" + "wetFloor", + "wheelieBin" ] } diff --git a/public/props/marbleSink.png b/public/props/marbleSink.png new file mode 100644 index 0000000..20897f0 Binary files /dev/null and b/public/props/marbleSink.png differ diff --git a/public/props/mirror.png b/public/props/mirror.png new file mode 100644 index 0000000..d0b6eb2 Binary files /dev/null and b/public/props/mirror.png differ diff --git a/public/props/oldCouch.png b/public/props/oldCouch.png new file mode 100644 index 0000000..2a91b8c Binary files /dev/null and b/public/props/oldCouch.png differ diff --git a/public/props/picnicTable.png b/public/props/picnicTable.png new file mode 100644 index 0000000..92e1e0a Binary files /dev/null and b/public/props/picnicTable.png differ diff --git a/public/props/pillar.png b/public/props/pillar.png new file mode 100644 index 0000000..c380f08 Binary files /dev/null and b/public/props/pillar.png differ diff --git a/public/props/planter.png b/public/props/planter.png new file mode 100644 index 0000000..f1350ce Binary files /dev/null and b/public/props/planter.png differ diff --git a/public/props/plywoodBar.png b/public/props/plywoodBar.png new file mode 100644 index 0000000..a49edb5 Binary files /dev/null and b/public/props/plywoodBar.png differ diff --git a/public/props/pokie.png b/public/props/pokie.png new file mode 100644 index 0000000..d2daf55 Binary files /dev/null and b/public/props/pokie.png differ diff --git a/public/props/raffleDrum.png b/public/props/raffleDrum.png new file mode 100644 index 0000000..1cfdd1e Binary files /dev/null and b/public/props/raffleDrum.png differ diff --git a/public/props/rollerDoor.png b/public/props/rollerDoor.png new file mode 100644 index 0000000..018b289 Binary files /dev/null and b/public/props/rollerDoor.png differ diff --git a/public/props/ropePost.png b/public/props/ropePost.png new file mode 100644 index 0000000..ad8540c Binary files /dev/null and b/public/props/ropePost.png differ diff --git a/public/props/ruleBoard.png b/public/props/ruleBoard.png new file mode 100644 index 0000000..bd9484e Binary files /dev/null and b/public/props/ruleBoard.png differ diff --git a/public/props/skylineCard.png b/public/props/skylineCard.png new file mode 100644 index 0000000..b92637b Binary files /dev/null and b/public/props/skylineCard.png differ diff --git a/public/props/smokeMachine.png b/public/props/smokeMachine.png new file mode 100644 index 0000000..c2f4c03 Binary files /dev/null and b/public/props/smokeMachine.png differ diff --git a/public/props/stackF1.png b/public/props/stackF1.png new file mode 100644 index 0000000..d59b00b Binary files /dev/null and b/public/props/stackF1.png differ diff --git a/public/props/staffBartender.png b/public/props/staffBartender.png new file mode 100644 index 0000000..06ad28e Binary files /dev/null and b/public/props/staffBartender.png differ diff --git a/public/props/staffDj.png b/public/props/staffDj.png new file mode 100644 index 0000000..223522f Binary files /dev/null and b/public/props/staffDj.png differ diff --git a/public/props/staffGlassie.png b/public/props/staffGlassie.png new file mode 100644 index 0000000..78b0411 Binary files /dev/null and b/public/props/staffGlassie.png differ diff --git a/public/props/staffSecurity.png b/public/props/staffSecurity.png new file mode 100644 index 0000000..22cd880 Binary files /dev/null and b/public/props/staffSecurity.png differ diff --git a/public/props/street_elevate.png b/public/props/street_elevate.png new file mode 100644 index 0000000..952e6fd Binary files /dev/null and b/public/props/street_elevate.png differ diff --git a/public/props/street_room.png b/public/props/street_room.png new file mode 100644 index 0000000..eb94e8e Binary files /dev/null and b/public/props/street_room.png differ diff --git a/public/props/street_theRoyal.png b/public/props/street_theRoyal.png new file mode 100644 index 0000000..8020972 Binary files /dev/null and b/public/props/street_theRoyal.png differ diff --git a/public/props/strobe.png b/public/props/strobe.png new file mode 100644 index 0000000..f917443 Binary files /dev/null and b/public/props/strobe.png differ diff --git a/public/props/subBass.png b/public/props/subBass.png new file mode 100644 index 0000000..6c2c2c2 Binary files /dev/null and b/public/props/subBass.png differ diff --git a/public/props/tabBoard.png b/public/props/tabBoard.png new file mode 100644 index 0000000..917786c Binary files /dev/null and b/public/props/tabBoard.png differ diff --git a/public/props/tvSport.png b/public/props/tvSport.png new file mode 100644 index 0000000..0f52165 Binary files /dev/null and b/public/props/tvSport.png differ diff --git a/public/props/wheelieBin.png b/public/props/wheelieBin.png new file mode 100644 index 0000000..ea87b27 Binary files /dev/null and b/public/props/wheelieBin.png differ