engine v5.2.10
Engine v5.2.10
July 30, 2026
A patch in the Lume line.
Worn gear welds on, moving platforms carry you, cooking models show their art
what's new
- Gear attached to a character's bones (armor, hats, held items) now moves welded to the body — no more trailing a beat behind when you run or spin. No script changes needed;
interpolation: nullworkarounds can stay or go, they no longer matter. - Editing a live effect now just works: save the
.fx.jsand every running emitter picks up the new program — no more destroy-and-respawn, no filename rotation.visible: falseactually hides an object's particles now (and your own torch flame stops rendering inside your first-person camera), andfx.paused: truekeeps suspending the deck like before. - Fast vehicles and characters no longer hop on flat ground. The engine used to briefly lift the physics body ~25 cm at speed and float it back down — cars read as bouncing, and collision/traction wobbled with it. The body now stays glued at any speed; slopes, stairs, and kerbs still climb exactly as before.
- If your game's input map carries a binding that can't work — a misspelled key name, an axis declared with no source, an engine-owned axis name — Savi now sees exactly which binding is broken and why the moment it's written, instead of your controls silently half-working until someone debugs it. Working bindings are never affected by a broken one.
- Look scripts can now tile textures:
ctx.texture("/cdn/paper-fibre.png", { wrap: "repeat" })repeats past the texture's edge instead of smearing the last row of pixels into streaks across the screen."mirror"reflects instead; offscreen targets take the same option. - You can design your phone controls now: twin sticks, gas-and-brake pedals, drag-to-aim buttons, touch pads — and swap layouts per player mid-game (driving vs on foot). Savi can finally read exactly what's on a phone screen, and the engine tells her when a button didn't fit instead of quietly hiding it.
- Buttons in custom game UI can be held down now (sprint, charge shots), and hand-built on-screen sticks stop snapping back under your thumb.
- Fixed two ways a 3D model could silently render nothing: a model file the device can't decompress now reports a clear load failure (instead of an invisible "successful" load), and a model that's taking unusually long to generate now says so — to you and to Savi — instead of leaving empty floor with no explanation.
- NPCs told to walk somewhere while facing something —
moveTo(target, { face: player.id })— now actually keep facing it the whole way: backing away while aiming, strafing a range band, or sidestepping around obstacles no longer snaps their gaze to wherever their feet point. Drop thefaceoption and they look where they're going again. - When a game looks frozen because its hosting player's tab is hidden (solo rooms pause while you're away — that's by design), Savi can now see and explain exactly that instead of chasing a phantom engine bug.
- Spawned models show their concept art in seconds now: while the 3D model is still generating, the spot where it will land shows a billboard of its 2D image (the one Savi's generation mints almost immediately) instead of just the loading swirl — and the real model replaces it the moment it finishes cooking.
- Fixed a silent failure where, in worlds with very many uniquely-shaped generated objects, newly added ones could stop appearing entirely — with no error anywhere — once a rendering batch filled up. The engine now tells Savi exactly which batch filled and what frees space, the moment it happens.
- Worlds with very many uniquely-shaped generated objects no longer hit an invisible ceiling where newly built things stop appearing: the rendering batches now grow with the world (up to what the player's GPU can actually hold, which is several times the old limit). If a device's true limit is ever reached, it still announces itself instead of failing silently.
- Savi can now actually see your game's UI when she checks it — before, a technical wall meant her UI checks always came back empty, and she could mistakenly report your HUD as broken (and prescribe reloads) while you were playing through it just fine. Her reads now also come with the engine's own delivery receipts, so false alarms about invisible UI stop at the source.
- On a shaky connection, multiplayer games no longer stutter backwards: NPC timers, animations driven by state clocks, and moving objects run by your game's scripts keep advancing smoothly instead of repeatedly snapping back a half-second every time the connection strains.
- On a bad connection that goes one-way (you can still see the world but the game stops hearing you), the server no longer yanks you back to your landing spot every ~30 seconds forever — after a few minutes of provable silence it tells your game to reconnect once, you land back in your own body, and play continues.
- Fixed a bug where nudging just one rotation axis (like
rotation.roll) on an object could silently do nothing — the object stayed frozen at its old angle. Single-axis rotation writes now turn the object, keeping the axes you didn't touch. - Savi can finally see what she builds: her inspection camera now carries your world's look and bloom (godray scripts, grades, vignettes — the same pixels you see), and she can read exactly where your sun is instead of guessing from shadows. Look work stops being "a bet until you turn around and look."
- If a scripted object's geometry script produces an empty mesh (nothing to draw), Savi now finds out immediately — the error log names the object and the script, and asking her to look at the object tells her the geometry came up empty instead of guessing it's still loading.
- Stepping off a moving boat onto a dock (or any ground) now cleanly ends the ride — no more being dragged along the pier at boat speed. Riders also survive server host changes and tab-away/return without getting glued to the boat.
- Moving platforms just work now: stand on a boat, elevator, or any moving object and you ride it — walk around on deck at normal speed while it moves, jump and you keep its momentum, step off and you're free. No follow scripts needed, and the ride is glued at render cadence (no more shaking while the deck moves).
- Fixed a bug where long-running rooms could get stuck naming every new thing with the exact same id — weapons, projectiles, and effects would silently replace each other and cleanup would miss. Affected rooms heal themselves the moment they mint their next id; no reset needed.
›technical notes
- Bone-attached children join the render-composed rail (ledger 1860 — tucker's armor trailing its wearer). A
tome/parentop carryingattachment.boneused to parse toparentId: null, transformAlreadyPresented: true: excluded from the renderer's parent-relative composed-child rings and drawn from raw wire rows that the SIM composed through the cross-worker bone-pose feedback — the bone's world transform from the renderer's LAST displayed frame, plus a channel hop. Net: worn gear = the body's drawn pose 1–2 render frames ago, trailing by speed (measured 0.39 units at a 0.25 u/tick sprint in the new harness). Now bone children keep their parent edge and compose RENDERER-side at draw time: parent's displayed pose ⊗ live bone local ⊗ authored offset — the live rig providers already register renderer-side (lume/models/skinned-character.ts), so the composition is same-frame with zero feedback round-trip. This is #11535's stand-rider rail (drawn composition against the parent's displayed pose at render cadence) extended to bone anchors. - The drawn chain, not the physics chain: the new
writeDrawnBoneTransformprovider leg runs the base through the visual's own trailing chain (local offset ⊗ geomScale + ground-alignment lift —composeVisualWorld's math), so the bone lands where the body actually draws. The existingwriteBoneTransformbase leg stays deliberately lift-free (ragdoll seeds pin that contract). - #7350 stays dead, structurally: the pass-through rail (
transformAlreadyPresented+ its sync path) is deleted, and bone children never seed a snapshot ring — the wire rows CANNOT be re-interpolated (the double-delay #7350 cured has no code path left). Pinned from both directions: corrupt the feedback channel and the drawn pose stays welded to the live rig while the sim rows dutifully carry the corruption. - The feedback channel keeps serving the sim, unchanged:
hierarchy-render-solvestill composes the child's World* rows from the SAB feedback every renderPrep (gameplay reads, script queries, ecs-sync). Those rows draw only as the fallback while no live rig is registered (model loading / horde-batched parent — the ledger-#308 seed baseline), exactly the old first-frames behavior. - Named gaps: a pivoted DIRECT bone child draws unfolded (the pivot fold needs bounds the op stream doesn't carry, and anchor inference can't serve here — a direct bone child's authoritative rows are feedback-stale, so the inferred anchor would bake in the trail); children NESTED under a bone child infer normally. Bone children of an interpolation-disabled or record-less parent keep the raw-row fallback.
- Running fx decks adopt every recompile — live
.fx.jscontent edits included (ledger 1834 face 1). The renderer-side diffs (lume particles store, particle service, both backends) adopted a program only whenFxCompiledProgram.sourceVersion(=FxEmitter.version) moved — but a live script-content save recompiles under an UNCHANGED emitter version, so a running deck kept executing the original program until the ref itself was rotated to a new filename ("the effect system reloads by filename, not by file contents" — savi taught filename-rotation as the workaround, baking ref churn into specs).FxCompiledProgramnow carriescompileSeq, a per-entity monotonic counter minted by tome/fx-compile on every program write, and every downstream diff keys on it: any recompile — params write, script edit, lib edit, cache-epoch reset — reaches the running deck by construction. The recompile-invisible-downstream class is unrepresentable now, not patched per trigger. - fx decks honor visibility (ledger 1834 second face, incident b62bd296 THE VOW). The particle path never read
draw/visibility:properties.visible = falsehid the model while the deck kept emitting (mist visibly rendering whilevisible === false; same leak under the first-person body hide, the editor-only courtesy hide, and the foreign camera-attach gate — all DrawVisibilityOverride writers). tome/fx-compile now resolves effective visibility the way the mesh path does (client-local override wins over authored; layerMask 0 = hidden) and a hidden emitter drops its compiled program exactly likepaused: true— deck gone next frame, unhide recompiles from scratch (decision 11; non-zero layer masks stay visible).fx.pausedandapi.destroy()behavior unchanged and pinned. - Ground-follow lift gate (ledger 1853, tiger's car-bump): a kinematic character controller driven across a FLAT heightfield at race speed popped up to ~0.20 m in one tick and floated back down over the next several — track flat, zero script y-writes, autostep disabled. Mechanism (measured,
ground-follow-lift.integration.test.tsreproduces it from a plain world): rapier's controller sweep collides with the SHARED edges between coplanar heightfield triangles and parry reports phantom tilted contact normals (n = (-0.28, 0.96, 0)on an all-zero field); collide-and-slide climbs the phantom ramp, and the taller the per-tick sweep the taller the climb — race speed turns a sub-millimeter artifact into a visible hop, and the motor's ground press (-6 m/s) takes ~3 ticks to bring the root back down. The float-back was never a separate mechanism; the up-pop is the whole injection. - The fix verifies the output against the world instead of trusting the phantom: when the motor is pressing down (no commanded upward motion) and the computed movement still rises more than 5 mm, down-probes at the landed position — axis, thin lateral offsets, and leading samples out to the shape's full support reach along the motion direction (a rotated car box reaches 2.2 m lengthwise; slope entries and kerb step-ups ride the leading corner) — measure the ground that is really there. A rise the surface backs (slope, step, kerb) commits untouched; a rise into air gets clamped to follow the measured surface, and the grounded verdict rides the committed pose. Probe origins sit INSIDE the shape near the feet, so an overpass above the car can never masquerade as support, and embedded floors stay visible. Rays fire only on rising ticks (≥ 5 mm) — idle, walking, and flat cruising pay nothing.
- Not fixable at the source, and mantle needs none of this:
HeightFieldFlags.FIX_INTERNAL_EDGES(rapier 0.19.3) only corrects the contact-manifold path — measured: controller pops byte-identical with the flag on — so the gate lives at the controller output seam. Mantle's heightfield narrowphase already folds neighbor-triangle normals structurally (narrowphase/static-heightfield.tsedge activity) and never manufactures the ramp. - Pins:
ground-follow-lift.integration.test.ts— (1) race-speed box on a flat two-chunk heightfield stays glued (pre-fix: +0.2035 m single-tick rise, red; post-fix: ≤ 0.0134 m, within contact noise); (2) a 15 % grade still climbs at race speed (the gate's leading-corner samples keep real hills honest); (3) a 0.2 m kerb still autosteps at driving speed. - Per-binding input-map refusals (ledger 1851 residue (a), forensics 3b93f733 — tucker's pirate sloop): the input compile was already per-binding tolerant — a binding that can't resolve is dropped ALONE and its neighbors stay live — but nothing ever SAID so:
resolveKeyBindingreturned null silently, a case-broken code-shaped name ("Keyw") passed through verbatim as a binding that can never match an ev.code, an engine-internal axis declared ininputs.axes(aimYawSin: {}— the pirate map's exact content) compiled to nothing, and the only diagnostics wereconsole.warns in whatever realm compiled the map. A creator sat pinned at spawn for an afternoon while Savi probed for what one write-time line would have named. NewcollectInputBindingFaultsininput-config.tsshares the exact resolver tables (no drift possible) and emits one fault per broken binding — path, reason, fix, with the valid form suggested when the mistake is spelling case ("Keyw"→ did you mean"KeyW"?,"escape"→"Escape"). Two lanes consume its printable form (capped at 12 lines + remainder):patchInputswarns on the mutation-warn rail (Savi's getLogs, at write time, validating the MERGED map so still-broken bindings re-warn on the next write), and spec-sync's control install warns on the client console ring once per distinct authored map (the existing inputs-signature gate) — so a map written by ANY path names itself. - What faults, exactly: engine-internal axis declarations (
ENGINE_INTERNAL_AXES— never bindable, the declaration does nothing); dead key tokens (silent-drop null resolutions AND verbatim-passthrough tokens whose case-fold matches a known binding); Tab (named reservation); wrong containers (axiskeysmust be a slash-string, actionkeysan array — a bare string binds each CHARACTER); unknown mouse/gamepad names with their valid vocabulary; junkactiveOn/modifiers; near-miss field names on inert defs (key→keys). What deliberately does NOT fault: any case of a documented shortcut ("arrowup"/"Arrowup"resolve — the pirate map's lowercase arrows always worked), unenumerated code-shaped tokens with no case-fold match ("NumpadComma"— the raw-capture escape hatch), empty ACTION defs (the sendAction UI pattern), empty AXIS defs get a notice teaching their live uses (sendAxis / authored touch sticks) rather than a refusal — declared-but-unbound axes stay declared, and creator metadata beside a live source stays unpoliced. - The anti-cliff pin:
input-binding-validation.test.tspins survival (WASD moves and actions fire with three broken axes in the same map; a dead key name disables its one binding alone) and byte-identical compile of the pirate map, so per-binding tolerance is now a contract instead of an accident — input validation can never become all-or-nothing without a red test. - Look-script textures gain wrap control — UV>1 no longer forces silent edge-texel smear (THE VOW's screen streaks, ledger 1847: every look-pass
ctx.texture()bound a hard-coded clamp-to-edge sampler, so a paper-fibre texture sampled atscreenUV × aspectrendered ~2/3 of a 16:9 frame as horizontal/vertical streaks with zero signal).ctx.texture(assetId, { wrap: "clamp" | "repeat" | "mirror" })andctx.target(name, { …, wrap })(riding.previoustoo) lower to the GPUSampler address mode (clamp-to-edge/repeat/mirror-repeat) — the smallest primitive, no pipeline: the wrap rides theShadeTextureRef, and the look pass picks the sampler per ref at bind time. Defaults stay clamp everywhere (existing content pixel-identical); the engine-owned frame/depth inputs stay clamped by construction (edge-extension is what screen-space kernels want). Unknown option keys and unknown wrap values throw teaching errors insidelook(ctx)— one diagnostic naming the vocabulary, look parks on the copy fallback (the fx-sink invented-keys law). - Authored touch surfaces:
inputs.touch.surfaces— named, completeTouchSurfaceDefdocuments (multiple sticks with zone/anchor/quantize, buttons incl. axis pedals and drag-aim with release payload{x, y, magnitude}, absolute/relative pads, one-action world gestures, ordering slots, icons, sizes, roles) — plusinputs.touch.active, a per-player selector script (default export(ctx) => name, worker-evaluated on the god-posture ship pattern; junk/unknown picks park that player to the draft with a teaching fault; oscillation is rate-limited and faulted). Surfaces compile into the sameControlSurfaceSpecthe draft and god specs feedderiveLayout; the engine never merges two surfaces. - Silent drops died as a class: every clamp/drop/park is a named
TouchSurfaceFault(unknown-channel, zone-conflict, button-overflow, stick-suppressed, gesture-folded, …) surfaced through compile errors and getLogs teaching faults; main-side geometry drops post anengine.diagnostic(touch-layout-overflow). - The draft is a value:
api.resolveControls(surface?)returns the engine's auto-projected draft (no arg /"auto") or a named surface, each with diagnostics. The draft's projection semantics stay byte-stable (pin-tested at reference viewports) — floor, not ceiling. - Authored button capacity is geometry, never a fixed cap:
deriveLayoutfits what safe-area + chrome reservations + 44px thumb floors physically allow and drops loudly from the slot-order end. The draft keeps its cap of 4. Slot is an ordering number;axis-hold's game-path ban androle's god-only scoping died. - Runtime: slot-indexed analog SAB (16 slots — twin-stick works),
input.axes.touchActive(1 while touch is the live input; desktop trace-free), surface swaps are edges with cancel synthesis,?touch=1gives desktop creators a live touch-chrome eye. - Tome UI contract fixes:
sendAction(name, undefined, { down: boolean })plumbs the wire's held-button field (routes tosetAction— press/release edges, hold bindings included); the god-sliderdata-draggingmorph guard is generalized — any authored element holding an active pointer capture is never morphed mid-drag (hand-rolled UI sticks stop being rebuilt under the finger). - A model that cannot decode now says so out loud instead of rendering nothing (ledger 1862, dig e6888eaf).
parseGlb/parseGltfJsonrefuse a file that REQUIRESEXT_meshopt_compressionwhen no meshopt decoder is available (GltfParseError), instead of "succeeding" with every compressed accessor read as zeros — full node/mesh topology, invisible geometry, all load books green (repro: 0/165,246 nonzero positions). Since the v7 quantize+meshopt republish that was every Magic CDN visual model for any client whose wasm init failed. The refusal rides the EXISTING model-load failure machinery — renderer retry ladder,console.error, and the ledger-1025model-load-faileddiagnostic carrying the parse refusal verbatim — never a new lane. Files that merely USE the extension (fallback buffers present) keep the per-bufferView warn-and-zero degrade; healthy loads parse byte-identically (pinned). ParsedGltf.warningshas a consumer (ledger 1862's second face: written since the lume cutover, read by nothing). A model that loads successfully but parsed with warnings — zeroed accessor reads, skipped primitives, unsupported required extensions — now surfaces them once per id on the new allowlistedmodel-parse-warningsdiagnostic (log-only: the load succeeded, so it is why-does-it-look-wrong context in getLogs, never a DM) plus a worker-console warn.- A Magic CDN cook loop past five minutes is a named fact, not a silent treadmill (ledger 1025's never-terminal hole, dig e6888eaf: badgerblunts' ten-clip
?animations=bot variants looped 202→cooldown all session with zero Savi-visible signal). 202 answers never escalate attempts by design, so the attempt-5model-load-faileddiagnostic can never fire for a cook that simply never lands. New allowlistedmodel-generation-slowdiagnostic fires once per pending episode when a model is still answering 202 past 5 minutes — deliberately worded NON-terminal (the engine keeps re-checking; the model appears when the cook completes; regenerate/simplify only if it stays stuck). - Cooldown-parked models stay visible to
getUnreadySceneAssets(the dig's confirmed second gap). Render-on-change stops prepares in a quiet scene, so a model waiting out a retry cooldown aged past the 2s poll-liveness window and vanished from the unready report for most of every cooldown — Savi's captures read "nothing missing" while ten bot variants were mid-cook. A model whose retry is scheduled (finiteretryAfterin the future) now counts as scene-referenced until that deadline: the due-retry wake buys a prepare, so a scene that still wants the model re-polls (stamp refreshes) and one that dropped it ages out at the deadline (pinned both ways). moveTo(target, { face })now holds the face override for the whole move (MechaBlade "regression on untouched code" dig: the option carriedfaceTargetIdin the intent but stamped only the move beat, andresolveYawTargethonors the override only while the FACE beat is fresh — which only the explicitface()verb stamped. Net: the documented option decayed to travel-direction yaw within one beat grace, so "walks toward you facing you" worked only when travel happened to point at the target — empty-yard geometry — and died the moment pathing sidestepped around an obstacle). The fix follows the beat system's own grain: a face option IS a face assertion, so moveTo stamps both the move and face beats (assertNpcIntentnow stamps a beat per asserted channel); the override holds exactly as long as the moveTo keeps being re-asserted and releases with it — travel yaw resumes within the same decay grace as everything else. No resolver/system special case.- A plain
moveTono longer clobbers a liveface()override's fields — same root, second symptom: moveTo's patch used to carry explicit-undefinedface keys, soapi.face(x)called BEFOREapi.moveTo(y)in the same pass was silently wiped (only the face-after-moveTo order worked). moveTo now writes the face channel only when its option asserts it;face()beside a plainmoveToholds in either call order. When both writers assert in one tick, the last call wins (merge-and-stamp semantics, pinned). - Sweep result:
facewas the only intent option gated on a beat its writer never stamped —speed/range/exact/avoid/fleeUntil/wander opts are all honored while their own verb's move beat is fresh, andsteer()stamps its own beat. No other silently-decaying option found. - Held/unhosted places tell you they're frozen (ledger 1839, dig e6ed67b2 — the ledger-931 pause-legibility pattern extended to host state). In client-hosted multiplayer the server never simulates place content; while a place's seat is successorless-HELD (ledger 1788 — the sole host's tab hidden/silent/mid-reconnect with nobody else to elect) or the place is unhosted-but-occupied, update()/api.on events/interactions/timers are frozen there by design while onSpawn, crons, api.sql, and edits keep working — previously with zero Savi-visible surface (Savi filed the designed freeze as an engine bug four times in one night; ~447 hold episodes across 48 apps in a 4h window is standing weather for solo-occupied rooms). Now: (a) every run_script result during the freeze carries a prepended
place "X" is not being simulated right now …note naming the reason, the since-tick, the frozen-vs-alive class split, and the "designed netcode behavior, not an engine fault" verdict; (b) an edit landing in a frozen place writes one getLogs runtime-log line per hold episode (episode-deduped like 931's pause line); (c)api.reportEngineBugincident dumps carry aplaceHostsblock on the server context — every table row plus hold state (reason, sinceTick), tenure confirmation, and occupancy — so a "update() never fires" filing self-classifies. One truth for all three surfaces:tome/place-sim-status.tsreads the same hold-episode record the assignment system maintains (moved into the cross-systemPlaceHostTenureState; zero simulation-behavior change). - The placeholder ladder's middle rung is back: a cooking model shows its 2D concept image as a billboard within seconds, then swaps to the model when the cook completes (thread 1785358887). The three-era hologram (extensions/models/placeholder-visuals.ts) crossfaded to the Magic CDN preview image; the lume port (#7602) cut that lane with a
TODO(lume-placeholder-previews)and shipped pure-procedural rings — the "swirling circle until it loads completely" regression. The lume placeholder module now resurrects the old logic minus the bubble shell (removed on taste back in ledger 417):collectPlaceholdersalready derivespreviewTextureIdfrom the preview metadata poll (preview_image_urlis written ~2-3s into a Trellis/Meshy cook, minutes before the GLB), andpackPlaceholdersnow registers that URL with the lume texture service (registration + subscription IS the load demand — the override-map mount pattern), partitions instances procedural-first then one contiguous run per decoded preview texture, and draws each run with its own bind group and afirstInstanceoffset into the shared instance buffer. The preview's own alpha masks immediately (BiRefNet cuts the background CDN-side); the procedural hologram crossfades out underneath at the three-era 4/s rate. The rung is visual-only: pick volumes (placeholderDrawnSize) and physics are untouched, failed cooks stay procedural red (legibly dead beats pretty), a missing/undecoded/layered preview falls back to today's procedural hologram instance-for-instance, and the model-ready swap releases the texture retain and subscription the frame the placeholder drops. - The batch-lane arena-full refusal is loud on every rail (ledger 1833, War Cloud d757e8d9 root). When a primitive batch lane's shared geometry arena hits its cap (
LANE_MAX_VERTICES/LANE_MAX_INDICES), every NEW unique shape routed to that lane is skipped by design — the object exists, answers queries, keeps physics/bounds, its bespoke-geometry wait resolves (data was delivered), and it renders NOTHING, permanently. The engine already emittedprimitives-lane-arena-fullfor this, but the code was never allowlisted server-side (the #330 emitted-but-dropped class) and the emitter printed no console line — so the loss was invisible end to end: clean console, clean getLogs, zero stall reports (War Cloud saturated one vertex-PBR lane at exactly 1,048,576 reserved vertices across 2,664 split-slot registrations from ~440 scripted bodies; everything refused after the cap — the creator's viaduct among them — was silently invisible for two sessions and ~100 rebuild attempts). Now: the diagnostic is allowlisted (full-body DM — ACTION-classified with the #297 consent rule, the outline/shadow-slots shape), it prints a server-console breadcrumb (client-side emitter; the stdout line is what makes arena exhaustions DD-countable fleet-wide), and the emitter warns on the worker console (once per lane) so client dumps carry the refusal. - Batch-lane geometry arenas grow on demand — the fixed 1M-vertex wall is gone (ledger 1833, the capacity half; the silence half landed as
primitives-lane-arena-fullloud rails). A primitive batch lane's shared vertex/index arena was admission-capped at a fixedLANE_MAX_VERTICES = 1<<20/LANE_MAX_INDICES = 1<<21, even though the arena's whole growth stack already existed (pow2 CPU mirrors + GPU buffer recreation that preserves flushed content with a buffer-to-buffer copy — mobile memory diet phase 2a). War Cloud held one vertex-PBR lane at exactly the cap (2,664 registrations, 702,604 live vertices, ~33% pow2 reservation padding), so every later unique shape — the creator's viaduct among them — was refused at the batch seat and rendered nothing, forever. The fixed caps are deleted: admission now checks a device-derived ceiling (laneArenaCeilings) — min(maxStorageBufferBindingSize,maxBufferSize) over the lane's vertex stride (pulled lanes bind the whole arena as ONE storage buffer, so bindability is the bound, not creatability),maxBufferSize/4 for the u32 index arena, and an absolute 2^24 vertex cap (instance-param float 21 carries each slot's vertex-arena base as f32; integers stay exact through 2^24). Under the ceiling the arena simply grows on the existing ladder; before a lane has seen its device, the WebGPU spec-default limits are the floor (any device can bind what they admit), so cold-boot bursts grow too. On a desktop 512 MiB grant a vertex-PBR lane now tops out ~7.07M vertices (~6.7× the old wall); at the TRUE ceiling the loudprimitives-lane-arena-fullrefusal (DM + breadcrumb + console) fires exactly as before, now carrying used/max vertex+index counts. Growth cost is a one-time hitch per doubling (a lane doubles at most a handful of times in its life): one GPU-side copy of the old arena (~76 MB at the 1M-vertex boundary — sub-millisecond at VRAM bandwidths, and it replaces what would otherwise be a CPU re-upload) plus one prepare's worth of mirror re-uploads and bundle re-records. A permanent one-frame hitch per size class beats permanently invisible geometry. The pow2 reservation padding (~33% burn in the specimen) is deliberately untouched — growth, not packing, is the fix. - Authored UI is now READABLE from the tool side — the realm-aware read relay (ledger 1845 fix b, the structural cut behind the c13632c4 phantom-blackout family). Since the authored-realm isolation cutover, ui.js DOM renders inside a sandboxed opaque-origin iframe, so
run_ui_script's host-document reads structurally cannot see it: every probe of the taught addresses returned empty and healthy HUDs got filed as "never paints" while creators played through them. New client RPCread_authored_ui(contractread-authored-ui/1) crosses the frame broker into the realm and executes the query where the DOM is legible: a closed read-verb vocabulary (snapshot | html | text | rect) + selector via querySelectorAll — never evaluated, realm answers only (never initiates), bounded payloads with honest truncation markers, write/eval-shaped requests refused loudly at the RPC gate AND structurally unrepresentable on the wire (protocol family 23read, strict decode). The response carries the realm boot state and a FRESH HUD-pipeline receipts block (compile state, sent count+sig, applied outcome + sent→applied lag, dedupe count+last-tick — minted per read from the runtime worker) alongside the DOM answer, so a "never paints" claim has to beat the pipeline receipts in the same payload. - The reset-path client-sim rewind dies (ledger 1848, kin 1523). The reset-delta path was the only inbound lane that could rewind client-simulated state in normal play:
sweepProjectionForResetdespawned host-simulated/owned entities and the reset's create rows rebuilt them from the server's canonical blobs — but for an entity THIS client simulates, the canonical copy IS the client's own uploads, ~RTT stale by construction, so every reset rewound live hot state (TomeState clocks, owned appearance values) by RTT. Send-failure reset storms (Bunws.sendbackpressure → strict false → reset; ledger 945's prod specimens) made that a per-0.6s event on saturated links. Family contract, now enforced at the seam: an inbound apply must not write client-simulated (or script-diverged) hot state it cannot prove newer. - The guard shape: the
applyAllbypass stays (load-bearing — owned entities must re-materialize on place transitions). What changes: a newshouldAdoptEntityStateOnResetingest hook (control-utilsshouldAdoptReplicatedEntityStateOnReset) marks entities this client currently simulates; the reset sweep keeps them alive, and their reset rows are rewritten to server-stamped (clientAuthWrite !== "owned") components only — spawn-stamped fields (TomeLifetimeDeadline) and authority transitions keep landing, owned values keep the local copy: exactly the value the server itself defers to. Preserved entities the reset does NOT re-show are despawned after the row passes (server-side kills of simulated entities keep landing via resets). Unknown/foreign entities adopt wholesale, unchanged. - The wake handback is exempt by design: this session's OWN suspension-stamped envelope adopts the reset wholesale — while the tab was hidden the place HOST simulated it, so the resume reset's blobs are the newer truth (docs/client-auth-suspension-takeover-plan.md). A suspended FOREIGN envelope this client host-simulates under the takeover is protected like the rest of the remainder.
- The no-confirm reset-resend loop gets a terminal state (ledger 1850 — #11360's disease, server edition: unbounded retry, no terminal). The o-53 ingest-truth lane deliberately removed the give-up-flip-to-Streaming (the 2026-07-20 one-directional freeze) and assumed a pipe that never confirms is owned by other rails — but a HALF-ALIVE socket (downlink delivering every frame, uplink eating every echo) is visible to none of them: the client's silence watchdog keeps hearing heartbeats, its self-heal re-request rides the dead uplink, and the ledger-695 eviction guards on Streaming. The GOHN specimen (07-29): full 1.26–1.7MB world snapshots every ~32s (the hold ceiling) for 25+ minutes with zero
ingest_confirmed, each delivered resend re-seating the mid-flight creator back at the arena landing ("DAMNIT IT TOOK ME BACK TO AMONKET"), BAD@277 landing 0.3s after the final resend — version-independent (same shape on 5.2.6 and 5.2.9). - The verdict shape: a new deafness budget
RESET_ACK_TERMINAL_HELD_SENDS = 11(5 doubling holds + 6 at the 32s ceiling ≈ 3m43s of provable deafness — far past every genuine recovery the ledger-945 client budget observed). At the hold expiry that would cut snapshot N+1, the egress instead logsnetcode.reset_projection.terminal(warn, withheldSends), closes the socket retryable (4437/reset_ingest.deaf— deliverable, the downlink works), and detaches the connection into the ordinary disconnect grace. NOT a despawn and NOT a Streaming flip: the entity keeps simulating through the grace window, no snapshot rides the transition (the per-resend re-seat was the harm), and the client's existing reconnect lane lands a reattach that resumes into the same entity viareplaceReadyConnectionwith every reset counter re-zeroed — a fresh socket, the only thing that can heal a dead uplink. - Ground truth always wins: a late
ingest_confirmedanywhere inside the budget clears the counter through the existing echo path and the episode ends as a normal confirm — the terminal check only ever reads the counter, so a healed link can never be condemned after the fact (pinned by test). - The
road/splineECS component is deleted (residue cut named by #11164, probe 60f4933c, row r-dd0a41ea). The component had zero producers since roads migrated to the spline system (#6308, 2026-04): every remaining reference was ahas-guarded remove or ahas-guarded set — branches that could never fire. Dies with it: the component definition + its bespoke encode/decode (features/road/components.ts, the whole file), its registry row (defineRoadComponents, theroad/splinewire-dictionary entry and its share ofschemaHash), the 8 dead guard sites intome/api/properties.ts+tome/interpreter.ts, and the fossilRoadSplinesection ofdocs/API.md. Survivors, untouched:features/road/geometry.ts(getRoadGeometry/RoadSplineValue— rivers and spec-levelspline: { kind: "road" }visuals are a different, live system) and the road feature barrel, which now re-exports geometry only. - Dotted euler writes no longer vanish against quaternion rotations (ledger 1863, GUTTERBT snowboard dig).
setObjectProperty("rotation.roll", …)deep-merges onto the{x,y,z,w}read-back and the merged{x,y,z,w,roll}hitresolveRotationSpec's quat branch first — the fresh euler intent dropped with no rail (the lookAt+euler conflation had one; quat+euler had none). Both resolver twins (api/object-api.ts,spline.ts) now detect euler keys riding a full quaternion and apply them onto the quat's decomposed axes (quatToYawPitchRoll, new in the math library: intrinsic Y-X-Z, the exact inverse ofquatFromEulerDegrees, gimbal-lock safe); unnamed axes keep the quaternion's values, so per-framerotation.rollwrites are absolute, not accumulating.warnRotationQuatEulerConflationjoins the spec-shape rail — once per room, names what the engine did. Pure-quat and pure-euler resolutions are pinned byte-identical. - Savi's inspection camera sees the post chain — camera/frame captures composite the active look + bloom (the lume port of #8344's composite leg; r-df174d4e gap 1).
view_live_scenecamera/frame captures rendered the raw scene (ACES + sRGB only) and skipped the whole post chain, so Savi authored look scripts blind ("my inspection camera can't see the post chain… this fix is a bet until you turn around and look"). The capture now owns a post rig of the chain's own pieces (lume/capture/postfx.ts): the dual-filter bloom pyramid, the generated composite kernel (veil 0, no outline slots, no RCAS — scene + bloom → ACES → fused look → sRGB + dither), and a capture-ownedLookPassfor two-hop looks (offscreenctx.target/ctx.passgraphs — the godray shape), with a 1× depth resolve feeding the depth-reading kernels (haze, dof,ctx.depth). One-shot correctness:BloomPass.prepare()/LookPass.prepare()await pipelines where the live chain keep-presents, so the first capture after a look edit carries it. Nothing active keeps the plain path byte-for-byte; any post-leg failure falls back to the plain render (never a failed capture). Every capture note names exactly what the image carries — composited / nothing-active-matches / still-compiling-recapture / broken-look-matches-viewport / could-not-apply. Burst tiles ride the same path, so filmstrips are graded too. - The render-time sun is readable —
getAtmosphere().sunState(r-df174d4e gap 2). Savi inferred the sun's world-space direction from shadow angles in a render; the derivation (timeOfDay orbit, authored-rotation pin, cycle override, overcast dimming, night moon handover) lived only inside the client atmosphere-sync system. The sun block is extracted verbatim into one shared resolver (atmosphere-sun-color.ts resolveAtmosphereSun) feeding both the sync's light entity and the new derived read-onlysunStateongetAtmosphere():{ direction, rotation: { pitch, yaw }, color, intensity, body: "sun" | "moon" }, ornullwhen the atmosphere has no sun at all — the "new worlds ship with no sun" truth as a readable fact instead of a black render to reverse-engineer.patchAtmospherestripssunStateon the way in, so a full read patched back round-trips clean. - The empty-derive silence hole dies (ledger 1833, War Cloud/viaduct-river):
realizeScriptedPrimitiveGeometry'sok:false reason:"empty"outcome — geometry() returning without error and without a single emit — used to returngeometry: nullwith NO report (only thrown errors were loud), after which the interpreter removed the entity's DrawMesh pointer entirely: no log, no DM, no heal traffic, no vocabulary in getLogs or view_live_scene. An object that exists, answers queries, and renders nothing anywhere, forever, silently. Now the empty outcome reports through the same behavior-error rail every other geometry() failure rides (runtime log + deduped DM, naming entity + script + "emitted no geometry — check the params/seed"), from the ONE derive funnel (deriveScriptedPrimitiveGeometry), so every lane is loud: server mint, client applySpec, live property writes, the #929 ingest drain, ground redrape. - view_live_scene's frame-mode miss names the real cause: a failed/empty derive on a client world now leaves a
ClientBespokeGeometryDeriveFailedrow (client-plane, replicate:never, forwardToRenderer:always — the Unhealable pattern) carrying{ script, reason: "empty" | "error" | "no-geometry-export" }; cleared by the next successful derive, dies with the entity. The renderer's primitives store keeps it as a per-entity note (it is the ONLY renderer-side trace — a no-mesh entity has no pointer, no wait, no record), andsceneViewObjectBounds' miss carries it into the frame-miss sentence:Object "x" exists but renders nothing: its geometry script (gen/foo.js) ran and emitted no geometry on this client — the mesh is empty under the object's current params/seed.— replacing the "assets may still be loading" guess that cost a creator two sessions. - Stand-parent grounded egress (#11535 landed-look, finding P1). Walking from a moving platform onto foreign non-moving ground (static dock, terrain) while staying grounded now ends the ride: the attached tick detaches (no momentum — feet never left ground) when the CC's support entity is neither the platform, nor a moving attachable (the transfer branch), nor a member of the platform's own parent chain (a static-bodied part of the same assembly holds the ride until the pose watch qualifies it for transfer). Persistence-gated at 2 consecutive foreign-ground ticks so single-tick support misreads at the deck edge can't drop the ride and re-base the anchor. Before this, every detach path required airborne ticks, despawn, or a moving support — stepping onto a dock left the carry composing the rider in the departed platform's frame, dragging them at platform speed indefinitely (measured: 2.000 m of drag over 60 idle ticks at 2 m/s, exactly the deck's travel).
- Stand-parent authority handoff (#11535 landed-look, finding P2). The engine's edge now survives its simulator:
attach()writesTomeParent { parentId, stand: true }, and a simulator that finds a bare flagged edge with no local state (host migration, suspension wake, control transfer — the replicated product outlives the realm-localTomeStandRidermarker and the WeakMap) ADOPTS it instead of classifying it as a script mount and orphaning it. Before this, the orphaned edge re-entered the hierarchy solve and the −10 projection on the new simulator — the two pinned-red grinds (teleport-compose depenetration + mixed-frame erosion) with no detach path ever. Scripts-win is unchanged: script writes lack the flag (never adopted), and a script overwriting a flagged edge still makes the system forget it. - Destroy cascade reads the flag. A rider spared from a platform's destroy cascade is now recognized by the wire-visible flag on every realm, not only where the realm-local marker lives — an NPC rider no longer dies with its platform when the destroy is issued by a realm that doesn't simulate the NPC. (SessionOwner players were already spared everywhere; the marker check remains for pre-flag edges.)
- Named the rotation gap. Platform rotation (facing does not turn with the deck; detach momentum misses tangential ω×r) is now in the system header's out-of-v1 list rather than unstated.
- Stand-parent — the moving-platform primitive (thread 1785355069: jacob's "parenting to the boat that should just work" + "unparenting when leaving"; tucker's brain dump is the spec). Stand on a moving entity → the realm that simulates your character parents you to it (
TomeParent+Local*anchor): movement becomes deck-frame-relative (walk speed independent of platform velocity), the platform carries you through rapier's own controller pipeline, and the renderer composes your drawn pose from the platform's drawn pose via the existing parent-relative presentation rings — the deck-rider world-space follow workaround and its structural one-platform-step sawtooth die. Leave (jump, walk off, platform despawn) → unparent with world-pose continuity plus the platform's last measured velocity carried ballistically until the next grounding. Jump straight up on a constant-velocity deck and you land back on the deck (re-attach) — the deck only leaves you if it accelerates. - Mechanism: the rapier character controller now reports its ground-support ENTITY (
getCharacterControllerGroundEntity— axis support resolved whenever grounded). A carry system (order 9000, pre-physics) composes the rider's target from the platform's current pose ⊗ the stored local anchor and adds the HORIZONTAL delta to the controller's desired translation — deliberately NOT a hierarchy body-sync: teleport-composing a capsule into exact deck contact makes rapier burn its solve on depenetration and eat the desired movement (walking dies on deck — pinned). Vertical stays the controller's own (snap-to-ground follows a falling deck; depenetration push-out follows a rising one). The anchor system (order 10000, post-physics) owns attach/detach and re-derivesLocal*from same-tick frames. Attach: grounded on a non-terrain, non-character physics body that is moving (kinematic/dynamic by declaration; script-moved static bodies the first tick their pose changes), cycle-guarded. Detach: airborne past a 4-tick grace, despawn, or support transfer. - The 1848-family seam, handled: both hierarchy solves and the client
Local*←World*projection skipTomeStandRiderchildren — at the -10 projection point the rider's World* is last-physics-step truth while the platform's is this tick's fresh ingest, and dividing those mixed-authority frames bakes the platform's inter-tick delta intoLocal*(the ride erodes to a standstill — pinned red).TomeStandRideris realm-local (replicate: "never"); the managedTomeParent/Local\*rows are the replicated product, and each rider's edge is managed exactly once, by its own simulator (client-predicted set / not-client-auth-external — the hierarchy solve's own predicate family). Relay tier unmounts both halves (steps no character physics). - Destroy cascade: riders and players (
SessionOwner) parented to a destroyed platform detach instead of dying with it — a rider is not a part. - Scope (v1, named): any character-controller entity rides — players and kinematic NPCs alike. Out: characters as platforms (head-standing never parents), items without character controllers (dynamic props ride via physics friction, not parenting), mantle-runtime places (mantle's CC already velocity-follows platforms), 2d-top (no vertical support semantics).
api.uniqueId()can no longer collapse into one permanently-identical id (incident 0cb62ea7, THE VOW on 5.2.6). The poison chain: any exec's setup spawns the baretome/execentity with noTomeIdSeq; a later spawn-only exec shipped a PARTIAL counter delta ({ spawn: k }, the untoucheduniquelane dropped as a zero delta), merge built a partial live component, and the nextuniqueIdmint didundefined + 1 = NaN— from then on every call returned the same_NaN-suffixed string forever (spawns silently adopted each other, destroys missed), and the NaN delta re-shipped on every merge. Killed at all three layers: the mint is total over malformed stored values (a non-finite lane re-derives from the persisted-spec scan — long-lived rooms already carrying the poison self-heal at their next mint, no reset needed), the exec overlay never ships non-finite counter deltas and ships snapshot-missing keys at delta 0 (a partialTomeIdSeqis unrepresentable at merge), and merge-side counter arithmetic skips non-finite input on both arms. Applies to both lanes of the machinery —api.spawn's minted ids ride the same component.