- Workshop (WORKSHOP_CARTRIDGE): five-stage cartridge assembly at Deck A — seat/square, crimp four tag-wires, torque screws, ride-the-arm counterweight balance, needle-drop diagnostic with per-fault audio + scope. Relay-synced (per-field co-op merge: held screw + carried wire survive remote state). - Glow-Up (G1-G5): 32px atlas with per-voxel variants, selective LED bloom (quality-gated), screen-print decal system + party flyers, mixer/PCB worldgen density pass, record groove-sheen side texture. - 10 confirmed multi-agent review fixes, incl. co-op screw-stomp soft-lock, ride-snap collider-identity (magnet feet / eaten record-fling), workshop SFX exact-match map (rca_seated hijack), beam ride colliders to the head, double-crimp guard, WIRING INCOMPLETE diagnosis mode, skate-abort timer, completedState wiring, per-tick material churn, trackingHeavy platter drag. - Crossfader playtest fix: slew-limited sled (3.4 v/s) + ribbed grip caps with amber index — the sled reads as a heavy handle, not a teleporting wall. - Demo harnesses: machinesDemo hold-key wiring, playerDemo seesaw phase continuity, audioDemo incomplete fault button. - Workshop sync: exact 1.0 screw endpoint gets its own emit signature. Verified: typecheck + vite build clean, live solo quest smoke, two-client co-op relay smoke (simultaneous torque, no rewind, exact convergence). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
8.9 KiB
Lane C — World Builder · HANDOFF
Status: complete. npm run typecheck clean, demo runs with no console errors,
build is deterministic and fully validated (see below).
Glow-Up G4 — worldgen density pass (2026-07-14)
Implemented the src/worldgen/** slice of docs/briefs/GLOWUP_ART.md (task G4).
The atlas (G1), bloom (G2), decals (G3) and record texture (G5) are engine/fx work
and are NOT in this lane — left for their sessions.
Files touched (this pass): src/worldgen/buildBooth.ts,
src/demo/worldgenDemo.ts. Nothing else.
Merge points (shared with the Workshop session): only src/worldgen/buildBooth.ts
(main.ts is untouched here). My edits are confined to buildMixer, buildUnderTable
(+ new pagoda/buildPcbDetail), and the deck-vent loop — all disjoint from the
Workshop brief's Deck A pedestal "workbench dressing", so a merge is clean.
What it adds (additive; every pre-existing zone stays byte-identical):
- Mixer face — dense mushroom knobs (1×1 stem + 2×2 cap, mixed heights, 6 rows × 3 cols × 4 channels, club-mixer palette) replacing the sparse specks; flush rubber panel-seam grooves sectioning the four channels; amber/green LED depth pips at each fader-alley floor and flanking; an amber glow behind each speaker-mesh vent.
- PCB Depths — walk-under IC-chip pagodas (matte_black slabs on solder pin-legs, stacked tiers, copper pin-1 dot, green status LED), ribbon-cable walls (deck rooms), extra copper trace inlays. Kept ≥15 voxels clear of every quest pickup.
- Deck vents — an amber pip at each shaft bottom so the drop reads as deep.
Determinism: the mixer detail is index-based (no rng()), and buildPcbDetail uses
its OWN seed (WORLD_SEED ^ 0x9e3779b1), so the main PRNG stream is untouched and all
other zones are identical. Two full builds are byte-identical (verified headless).
Anchors: no LAYOUT/QUEST_POS value moved. All 8 quest anchors still land on the
correct block (re-verified). Build 21–30 ms (« 800 ms budget). Chunk count unchanged.
One demo fix: the concept-art pass added an intentional shallow well step-ring
(outside PLATTER.radius); my old "well empty" assertion checked the full well and went
red. Retitled to "platter footprint clear" and scoped to PLATTER.radius — the region
Lane D's platter actually needs. Green again.
Note (not mine to fix): at this checkpoint the shared tree briefly failed global
tsc on the Workshop session's in-progress src/audio/testSignal.ts (a
Float32Array<ArrayBuffer> generic mismatch); it cleared on their next save. My files
typecheck clean in isolation.
Public API surface (import from src/worldgen)
buildBooth(world: IVoxelWorld): void // writes the ENTIRE static diorama
buildShell, buildDeckA, buildDeckB, buildMixer, buildCableCanyon,
buildPatchBay, buildUnderTable // individually testable sub-builders
SPAWN: Vec3 // deck A plinth top, faces the mixer
SPAWN_LOOK: Vec3 // suggested look-at (mixer face centre)
WORLD_SEED = 1210 // fixed seed → identical booth every run
QUEST_POS: QuestPositions // {stylus,rca,crossfader,fuse,power} for Lane D
QUEST_REACH_POINTS, hasAdjacentAir(w, p) // reachability helpers (used by the demo)
DECK_A, DECK_B, MIXER, UNDER, PATCH, RCA_SOCKET, RCA_PLUG // LAYOUT-derived anchors
What's done
Every zone in DESIGN §4 is built and visually confirmed in demo-worldgen.html:
shell + tabletop (front lip, vent slots, crate hatch + stair), both steel-grey
plinths (feet, alu border, empty platter well + chrome spindle stub, start/stop
- 33/45 recesses, pitch canyon w/ green detent, tonearm base pedestal, strobe lamp), Deck B dust drifts + half-open glass dust cover, the mixer massif (body, speaker-mesh climb vents, 4 channel strips = knob-stem grids + empty fader alleys, crossfader tram with the dust-jam plug, two VU towers, power-switch recess, fuse chimney), the cable canyon (5 splined RCA/power cables to the patch bay + the quest cable ending short with a loose gold plug + power strip), the patch-bay wall (alu panel, 2×6 gold socket grid, the empty quest socket with red blink, two doorway rooms w/ copper busbars + blue glow), and the PCB Depths (green board floors, copper traces, capacitor pillars, solder stalagmites, sparse LEDs, the fuse box with a blown fuse, the parts bin holding the fresh-fuse + stylus pickups, and the record crate of blue/black vinyl slabs on edge with a leaning-slab ramp).
What's intentionally NOT built (yours, Lane D)
The moving parts sit in the sockets I left for them: the rotating platter/record
(the empty circular well, radius PLATTER.radius+2, 6 deep, spindle stub at bottom),
the tonearm arm + headshell (base pedestal + empty socket anchor), the fader
caps/sleds (empty alley slots + crossfader tram), and the buttons/switches
(start/stop, 33/45, power — all left as recesses). Read QUEST_POS for exact voxels.
Quest anchor convention (important for Lane D)
Each QUEST_POS voxel is a reachable interaction anchor (has an adjacent air
voxel — validated). Semantics per node:
stylus.socket— empty voxel on the Deck A tonearm pedestal; drop the stylus here.stylus.pickup— the chrome stylus block in the parts bin (blue-spotlit).rca.socket— empty gold ring on the patch-bay wall face (air; push the plug in).rca.plug— the loose gold plug lying on the canyon floor (top is open air).crossfader.jam— top-centre of the dust plug in the tram (mine downward).fuse.box— the blown fuse (rubber block, chrome caps) in the wall fuse box.fuse.pickup— the fresh fuse (glass + chrome caps) in the parts bin.power.switch— recess on the mixer rear top (dark until the other four are done).
Validation (headless + in-demo + cross-lane, all green)
build 14 ms (< 500) · deterministic (two runs byte-identical) · 0 OOB writes · both platter wells empty · spawn on solid steel with headroom · 8/8 quest points reachable · fuse/stylus anchors land on the intended block · record crate populated · 29/30 non-air block types · ~4.5 M non-air voxels (24.6% fill).
Cross-lane smoke test (buildBooth against Lane A's real VoxelWorld, not a mock):
fillBox fast path ENABLED · 30.7 ms build · all acceptance checks still pass.
Post-review fixes (adversarial multi-agent pass, all verified)
- [HIGH] record crate placed zero vinyl slabs (footprint collapsed to ~2 voxels
because it was bounded by
deckA.minX); re-anchored to the open under-ledge volume in front of Deck A — now 8–9 records on edge. Added a regression guard. - [MED]
QUEST_POS.fuse.boxpointed at the alu frame, one voxel off the fuse; the blown fuse now sits exactly at that anchor (frame moved behind). Guarded. - [MED] VU-tower LED ladders were sealed inside a closed casing; the front (DJ-facing) face is now open so the meters read.
- [LOW] added the "resistor beams" the brief lists for the PCB rooms.
- [LOW] the demo's "Record Crate" teleport aimed at
x=12(inside the left plywood wall) and at the crate's old location; re-aimed at the crate's real centre. - (A
fillBoxdoc-comment nit was a false positive; tightened it regardless.)
Contract friction / notes for the integrator
- Mixer height. The brief says "tabletop →
topY+40", butconstants.ts(mixer.topY = 67, "27 voxels tall") and DESIGN §3 both put the mixer face plate TOP surface attopY = 67. I built to face-plate-top = 67 (body 40–66) with a raised rear ridge + 12-tall VU towers for the "tallest mesa" silhouette. If the design truly wanted a 107-tall mixer, that's a one-line change inbuildMixer. fillBoxfast path — RESOLVED.tools.box()usesworld.fillBox(x0,y0,z0,x1,y1,z1,id)(INCLUSIVE bounds) if present, else asetBlockloop. Confirmed against Lane A's shippedsrc/engine/VoxelWorld.ts:117: the signature and inclusive-clamp semantics match exactly, so the fast path engages at integration (verified — see cross-lane smoke test above). No action needed.- Elevation model. A
Y_*constant is the TOP-SURFACE PLANE: the topmost solid block of that stratum is at indexY_*-1; blocks placed on the surface sit atY_*. Documented inanchors.ts. Platter well is carved 6 down fromY_PLINTH_TOPso Lane D's platter (top atY_RECORD_TOP=85) rises cleanly out of it. - No duplicated coords. Every spindle/mixer/patch-bay position derives from
LAYOUTviaanchors.ts(single source). Lane D should read positions fromQUEST_POS/ the exported anchors, never re-hardcode. - Structural blocks are
breakable:falsealready inblocks.ts(plywood, cables) — the booth keeps its shape; nothing here relies on that beyond honoring it.