Conversation
- vrControls kept ONE grab slot. A second hand's grab overwrote it, so the first hand's piece stayed in its kinematic physics hold with nothing updating or releasing it: it hung in mid-air until a hand took it again (Towers report). - grabs[] is indexed by controller slot; each hand updates and releases its own, a released body is dynamic at once. Two hands on the SAME object still scale it in Edit (and still do nothing extra in Interact). - vrGrabbedHand reads 'both' while both hands hold; the two stick gates honour it. - vrGrabbedUuids(): the knock probe skips everything the hands hold. - an input-source rebind now RELEASES what the hands held instead of dropping the grab with the body still kinematic. - new suite vr-two-hand-grab (19, fake XR + a running rapier sim). CF: clearing the other hand's slot on grab (the old single-slot shape) -> 6 red (1.1, 1.2, 2.2, 2.3, 2.5 hold user/bodyType 2 = the frozen crate, 2.12). - vr-grab 7, vr-grips-by-mode 18, vr-source-rebind 5, knock-physics 77, vr-sleeve 29 green. svelte-check 333/47 unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…g (33 L4) The user: "opened waves game, then towers - music from waves stays and the gun, but only objects from towers load". A game module is a SCENE SCRIPT; when its scene goes away, whatever it registered for its own game must not reach the next. - sceneScope.js (a LEAF): the modules the scene was USING when it was cleared (noteSceneLeaving, called first thing in clearSceneLocal) and the next scene does not use are LEFT BEHIND; ownerInScope(owner). A fresh install in a blank scene is in scope (nothing was left behind), so every existing flow is unchanged. - gameShell: levels / How to play are kept PER OWNER and the newest in-scope registration shows - a kept Waves' levels do not appear in Towers' menu, and come back with no re-registration when Waves is open again. Restart hooks carry an owner and a left-behind one does not run. - gameSettings: a left-behind module's settings rows leave the Settings page. - gameMusic: the track is OWNED (api.music passes the module id). A left-behind owner's track stops and it cannot start one; api.music.stop only stops the module's own track; the module teardown journal now stops its music (it never did - "music from waves stays" after an unload too). - playSettings: a left-behind module's runtime spawn is ignored. - towers: onSceneClear unregistered nothing (it reset wasActive, so the frame edge saw false -> false) - Towers' twelve levels stayed in the next game. - api.inScene(): false once a switch left the module behind, so a game module can stand down its own drawing (the gun) - feature-detected. vitest sceneScope 12/12; counterfactual (ownerInScope -> true): 8 red. vitest 390/390, svelte-check 333/47 (baseline). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…4 audit) The teardown audit of every SDK registration (music, game.*, effects, input, spawn, locomotion): - playPublishers: a KEPT module's scene-root group still carries its userData.play, so Untangle's free cursor + world grab became the next game's. A group whose owner module a scene switch left behind publishes nothing. - api.followCam journals its stop: a module unloaded mid-follow left the camera chasing its object. (Already journalled and unchanged: input claims + bindings, possess, setSpawn, hud rows, toolboxes, transport events, frame/click/drop handlers, scene-root groups; effects bursts and the announce banner clear with the scene.) vitest sceneScope 13/13; counterfactual (filter removed): the play-contract test red. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ile per long task) - scripts/scene-load-trace.cjs: a fresh phone context per scene (412x915 isMobile @2.6, real GPU, CPU throttle 6), the Templates open path and autosave -> reload -> Restore, each reporting wall time, PerformanceObserver long tasks, the longest rAF gap, the CPU profile's top self time, app call chains, and the three longest tasks broken down. - Baseline (c7018be): Castle Courtyard Restore = ONE 89 s task (91 s frozen), open 525 ms tasks / 2.1 s frozen; Tavern Restore 84 s; Forest gets NO Restore at all (its autosave is over the 50 MB cap). Table in the 33-scene-load handover. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ces warmed and placeheld - sceneLoader.js (leaf): slice(job) yields when the 10 ms budget is spent and throws LoadCancelled at a clean point; schedule(fn) runs a burst of continuations a slice at a time; ONE load job (sceneLoad store) - a new load supersedes the running one; cancel hooks; throttledPoke (250 ms) for the build loop. - sessions.applySession: the object loop is time-sliced with progress, kit stubs refill at once (counted when they land), every await re-checks the job, Cancel clears what the load added (replicated like the load), a load interrupting one mid-build writes no "Backup before" of the half scene. - packRefs: a refill's clone/attach runs through schedule() (every copy of a piece used to attach in the one task that resolved its template); warmTemplate uploads textures one per slice and compileAsync's the programs against the live scene before any copy draws; refills poke at most every 200 ms; fingerprint checksums cached per geometry by buffer versions; packRef.box (root-frame bounds, additive) drawn as ONE scene-root InstancedMesh of grey placeholder blocks while a piece is on its way. - App.svelte debug hook: + sceneLoader (220/220/220). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s", Cancel - SceneLoadBar.svelte in the toast stack's first slot (the spectator banner's spot, so arriving toasts never shove it): name, count, a progress bar (indeterminate while preparing/reading), Cancel while objects are being BUILT; once only kit models are still arriving it keeps counting without Cancel. Non-modal, shown after 250 ms so a small scene does not flash it. Ids #scene-load-bar (data-done/total/phase), #scene-load-cancel. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ubs, sliced restore
- autosave writes pristine kit pieces as STUBS (packRefs.parkPackPieces: hollowed for the
GLTF export and put back on the exporter's afterParse hook, before the async encode can
let a frame draw a hollow castle). Castle Courtyard's snapshot 51 MB -> 86 KB; Forest
Clearing gets crash recovery again (it was over the 50 MB cap).
- applyRestore: time-sliced with progress ("Restoring your last session"), the wire copy
built only when a peer is connected (stubs for pristine pieces) - every child's toJSON
re-encoded its textures as PNG data URLs for nobody, the 89 s task; Cancel clears and
re-offers the prompt; superseded by another scene cleanly.
- The debounced autosave waits out a load (saveNow does not).
- tests/e2e/scene-load.test.cjs (CPU x6 phone): open + restore with no long task > 200 ms,
the bar counts, UI answers mid-load, supersede, cancel, stub snapshot, edited piece kept.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…); every level + level tabs in the VR pause menu
Untangle's Quest report (roadmap 33): "I can make bigger/smaller the globe but cannot use up/down
on stick to move it further/closer ... just as in edit mode for objects", and "main menu do not
show in VR levels to choose and tab to switch between flat/globe".
G2 — the stick reels what you hold, the world included:
- vrControls: the one-hand world PAN (a right grip on scenery/air in Edit, or in Interact when
play.locomotion.worldGrab) now reels the world on that hand's stick Y with Edit's own object
reel (grabStickAdjust): forward pushes it away along the hand's ray, back pulls it in, from
the distance to the gripped spot (panReach). worldReelStep is pure + exported.
- api.claimInput('sticks'): BOTH VR sticks' navigation stands down (left-stick move, right-stick
snap/smooth turn + teleport) for a module that reads the sticks itself (Untangle's held globe:
Y reels, X scales). claimInput now answers true for a scope this core knows (feature detect).
G3 — the pause menu's Levels page in a headset:
- shellPanelDraw drew list.slice(0, 20): Untangle's levels 21-30 were unreachable in VR. Now a
6-column grid as tall as fits (pages with arrows beside Back beyond that); <= 20 levels and no
tabs keep the old 5 x 4.
- addSetting({..., type: 'choice', onLevels: true}): the choice is also drawn as TABS above the
Levels grid, desktop (GameShellMenu, role=tablist) and VR (shell:tab hits). Untangle registers
its Board (Globe / 2D board) that way.
Tests:
- vitest shellPanelDraw (new, 6) + gameShell (+3): CF — the old slice(0, 20) restored: 2 red.
- e2e vr-world-reel (new, 21/0 on 5278): pure step, Edit pan reel push/pull exactly by the reach
growth, no drift at rest, Interact+worldGrab reel, the 'sticks' claim silencing snap turn and
the left stick then releasing. fakeXR controllers do not follow the reference space, so the
suite poses a spec-accurate right hand (re-derived on every setReferenceSpace).
CF: on the feat/1.19 base the suite is red (no worldReelStep, claimInput answers undefined).
- held: game-shell 66/0, vr-game-panel 36/0, vr-nav-suppression 5/0, vr-grips-by-mode 19/0,
vr-laser-panels ALL PASS (SCENES_DIR = the preview-1-18 scenes).
- svelte-check 333/47 (= baseline), vitest 387/387, check:storage clean.
- modules flights on this branch: untangle-vr2 94/0 (incl. S: left X -> Levels -> Board tabs ->
a level, through the panel's press path), untangle-vr 98/0, untangle 71/0, untangle-drag 54/0.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ons; menu layout audit (33 G5) - The report: "Menu" and "Top strip" too close / outside the menu's border. Three 240-stage-px footer buttons need 760 px while a cropped game menu board is 720 wide, so the row hung past BOTH edges (Menu at x -30, Top strip ending at 1110 on a 1080 px canvas) in all seven games. - footerLayout(W, k, n): natural width when it fits, else the buttons share the board inside a side margin. fitLabel(): shrink a button label to fit, ellipsize only as a last resort and report it (collectOverflow -> vrGamePanelDebug().overflow). Used by the footer, the wrist card, the shell pages and a game screen's own buttons on the board. - new suite game-menu-layout: the seven games (real .tpscene + real zips) x desktop HUD in every game state (inside the view, not half off a panel, 4 px apart), the pause menu's pages (inside the card, no overlap, no sideways scroll), the VR board per state + the shell pages on the board + the wrist (inside with a 10 px margin, 8 px apart, labels fit). BEFORE (HEAD's panel code): 7 red, exactly the reported footer (log + shots lanes-30/after-33/33-games/layout-before). AFTER run: see the handover. - svelte-check 333/47 unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…orce (WIP: e2e pending) - P1 contract: a pack item row's `lods` place as the object's LOD group (userData.lod, normalizeLodGroup in lodGroupCore.js is the one boundary); levels are a RENDER-TIME per-mesh geometry swap matched by node name inside lod.js's bracket (registerLodPass), drawn with LOD0's own material; lazy (built when first needed, once per file); thresholds by screen size (25% / 10% defaults, 15% hysteresis); implicit group from the pack row for pieces placed before their pack had lods - write path lodGroupActions.js: one props undo entry + one objectParameters 'lod' message (applier in commandsHandler, wireValidate shape, props kind in objectActions) - P2 LodGroupPanel.svelte in Inspector: level bar with draggable edges (one entry per drag), Force LOD, Generate levels (meshopt in the worker), replace a level with an object / Explorer drop, select-a-level preview, per-level material override, gizmo on a level (lodLevelEdit.js + Scene isLodLevelProxy branches), Show LOD level overlay, cull; api.lod handle gains force()/levels() - P3 VR props panel LOD row (readout + cycle Force LOD) - docs: PACKS.md lods field, MODULES.md - vitest lodGroupCore (22) green; svelte-check 333/47 = baseline - e2e lod-group: 22/24 on the first run, both reds fixed (test premises + block writes now take effect the same frame); re-run, lod-panel and vr-props-panel NOT yet run (slots/memory) - see the handover Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…counter, VR select-then-grip (WIP, proofs running) - E1: default bar = move/rotate/scale, Interact, Play, object list, node editor, Explorer, Animation; a stored record that is still a shipped default migrates (keeps posX/collapsed), a customized bar wins as saved and does not gain Animation (PROMOTED_DEFAULTS) - E2: the Interact <button> cell drops classActive's click-focus ring (focus-visible only) - E3: the play well's hover halves answer `*:hover`, not `p:hover` (the Interact button left two pill-coloured corners beside Play) - Q1: Settings > Interface > Viewport "Show FPS + draw calls" (+ VR settings row): the one FpsCounter in any mode, calls amber > 120 / red > 150 (fpsMeter.callsTier); vrPerfStrip.js head-locked strip in a headset; sceneBudget.noteExternalFrame so XR frames drive the draw-call sampler (the window rAF stops in a session) - E4 (VR half): a SELECTED scenery object (>= 4 m / around the head) is grip-held in Edit; unselected scenery still moves the world; one-time headset hint - tests: vitest fpsMeter + vrGrip; e2e editor-ui-33 (new), controls-roster + vr-grips-by-mode updated — the e2e proofs were still running at this commit (see the lane handover) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…op), no autoplay
- behaviorCore.js (pure, vitest 18): normalizeBehavior, the pose as a pure function of
{on, at, from, n} + the session clock, nextState, frameSlabs/passageThrough (the doorway
cut out of a one-piece frame, through the wall's depth)
- packBehavior.js: per-frame pose driver (rest pose in Edit whatever the shared state),
a core click handler in moduleSDK's one dispatch (desktop Interact click, Play tap, VR
laser/poke), proximity (1.5 m) and knock (VR hand >= 0.6 m/s) triggers, the `behavior`
wire message (latest-wins on sessionNow), a kinematic box per moving node that follows
the swing while a sim runs + the frame as custom slab colliders, a local Animation-panel
preview (never sent/saved), the per-item sound
- animatedImports: NOTHING AUTOPLAYS on placement any more (the first clip used to start
and loop); behavior roots skip the transport; spec (+ live state on the wire, + the
lod-editor's userData.lod) ride objectfile, the save snapshot and the undo entry
- fileHandler: animated imports land at the drop point; opts.behavior from the pack row
- packs/Explorer/explorerDrop: the row's spec travels to placement; an "animated" badge on
cards and list rows
- gameSfx: + door gate slide lever lid; wireValidate `behavior` shape; ROOM_SCOPED;
vrGameInput treats behavior roots as clickable; api.behavior {list, state, trigger}
- suites: pack-behavior (new; test-door.glb built with gltf-transform), animated-models and
animation-persist press play first now, game-audio expects 25 sounds
WIP at the budget checkpoint: pack-behavior ran 41 pass / 8 fail before the priming fix in
this commit. 7.2 (the first walk resolved on the plane tier: charController's lazy physics
import; primed now, not re-run). 7.6 (the reopen click on the frame post did not toggle)
cascades into 8.2/9.1/11.2/13.1/13.2 - next window's first job. Counterfactuals already
green: 7.7 (uncut frame = a wall when open), 2.3/12.1 (no autoplay), 14.1 (slow hand).
svelte-check 333/47 (= base), vitest 396/396, check-storage clean.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…behavior - PACKS.md: the behavior contract (types, triggers, the no-autoplay rule, shared state, frame + moving parts + collider follow, authoring notes, the Explorer badge) - MODULES.md: api.behavior list/state/trigger and the five item sounds Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…(E4) + suite fixes - physics.js: in EDIT, in a GAME scene (state-bound HUD screen or play.simOnPlay), releaseBody PARKS the body (hold 'edit': kinematic at its object pose, skipped by the deviation detector) instead of handing it back to the sim with a throw; leaving Edit (Interact or Play) releases every parked body at rest. A plain physics playground keeps the P-A editor throw. Measured before: Towers 10/44 pieces flung ~47 m, Stars Room 26/49 drifting, the football rolling away once a game ran and the editor went back to Edit. - new suite edit-move-games: the seven games, every object (cap 60) dragged through the gizmo events, fresh AND game-running; parked bodies released on leaving Edit; Interact release not parked (counterfactual). Waves' walking enemies are module-positioned mid-wave (noted, F5). - editor-ui-33: E3 corner pixels chosen outside the FAB and sampled un-hovered; Q1 toggle pressed as an element click. controls-roster: the walk needs a third press past Interact. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…oser programs, sky first - Controls.svelte: under the virtualisation threshold the object tree MOUNTS IN CHUNKS (40, then 16 per frame) — a load handed it ~180 rows in one poke, the longest task left (434-565 ms on a CPU x6 phone). svelte-check 333 -> 332 (the inline filter callback was a counted error). - packRefs.warmTemplate compiles BOTH program variants: the canvas one and the render-target one the post-processing composer draws with (three keys a program by tone mapping + output colour space) — the composer's programs were linking inside the first frame (260 ms). - sessions/autosave: the scene's environment is restored BEFORE the objects, so pieces warming as their packs land compile for this scene's lights, and the right sky shows at once. - Cancel stays offered while kit models are still arriving; the bar reads "n / N objects" in both phases. - scene-load suite: a fresh page per measured load (parsed pack files are cached in memory, so a same-page re-open was instant), pack files routed with a staggered 0.6-2.7 s delay, store-sampled count-up, Cancel hit-tested, the menu timed against an idle reference. - scene-load-trace: PACK_DELAY=1 for the same delay. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…gh the loader - lod-real-packs (new, skips without LOD_PACKS_DIR): 33-pack-fix-lod's real level files (architecture-kit Gate + WallStone, default Duck with its camera node + parent scale, cube_diorama Cat with 3 meshes) each build as name-matched SWAPS covering every mesh and draw fewer triangles far away (Gate 5440 -> 1080, Duck 8424 -> 1684, Cat 20108 -> 4014) - lod-panel 7.3 was a check that could not fail (it compared a count with itself): it now counts lod messages before/after the overlay toggles - lodGroupStats levels carry `paired` + `error` - held battery on this head: lod-group 24/24, lod-panel 49/49, vr-props-panel, lod (31), inspector, undo, lod-real-packs all ALL PASS; vitest 400; svelte-check 333/47 = baseline; npm run build green (dev server stopped) - counterfactuals: hysteresis (vitest: a 100-frame bob on an edge switches once; band 0 pops back at once); a block write taking effect the same frame (lod-group 4.1 was red before 24fe36b's same-frame re-read); shared materials (1.8: the mesh wears its own material mid-draw; 5.2 shows the override path is a copy) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… paths indexed (33 G1)
- The report: "Stars room is too heavy for mobile users, it lags". Measured on a phone
profile (412x915 @2.625, isMobile + hasTouch, CPU x6, scripts/perf-games.cjs --phone):
the governor sat at level 0 — a 100-call game is LIGHT by the desktop budgets and a light
scene is never governed — so a phone drew 32 render passes a frame at 2.6 Mpx each
(shadows, the authored AO, bloom, SMAA).
- qualityGovernorCore: PHONE_START_LEVEL = 4 (shadows off, 72 % resolution, AO off; the
look stays) + pure isPhoneLike (coarse pointer, no hover, short side <= 820 CSS px).
qualityGovernor: decided once at load; a phone starts there in auto mode (no floor - a
phone that holds its frames walks back up) and context() judges it heavy, the headset's
rule, so slow frames step it further. Auto off and a game's Quality pin still win.
- flowRuntime handleStamp/triggerStampFor read the existing edgesInto index instead of
scanning every edge per call (24 latches x 3 handles x ~700 edges a tick); physics
collectParams indexes nodes once (it was O(edges x nodes) on every flowGraphs change
mid-sim). Both were top app frames in the Stars Room profile; gone after.
- Stars Room, phone profile before -> after: calls 101 -> 47, render passes 32.2 -> 15.3
a frame, drawing buffer 2.60 -> 0.65 Mpx, p50 50 -> 33 ms, p99 200 -> 83 ms
(lanes-30/after-33/33-games/perf-phone-{before,after}.md; Towers 83 -> 48 calls,
p50 66.7 -> 33.3). A desktop GPU stands in for a phone's: a real phone reading is OWED.
- scripts/perf-games.cjs --phone (+ renders/frame, Mpx (dpr), physical/transparent columns).
- NEW suite quality-phone 10/0 (phone vs desktop page, the real Stars Room); CF (phone
detection off) 5 red. vitest +3 (381). game-stars-room 72/0, logic-nodes 77/0.
flow-physics-actions 8.5 is red IDENTICALLY on HEAD (A/B, both 35/2) - pre-existing.
svelte-check 333/47.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
# Conflicts: # src/lib/qualityGovernor.js # src/lib/qualityGovernorCore.js
…d frame hold - packRefs.warmPrograms(root): one compile per mesh per slice, canvas AND render-target variants, then each program's FIRST USE (getUniforms) one per slice — measured: the first frames created no program the warm-up had not, the cost was three's first-use link WAIT (no parallel-compile extension) plus the environment change re-keying every existing material. warmTemplate uses it. - sceneLoader.holdFrames()/releaseFrames()/framesHeld() (bounded by HOLD_MAX_MS = 2 s): Outline.svelte skips its render while a scene load re-links programs and builds — the canvas keeps its last frame, the DOM (bar, menus) carries on. sessions + autosave hold from the environment restore until the build is in and warmed. - SceneLoadBar enters with a CSS animation, not svelte's fly (getComputedStyle on mount forced a whole-document layout inside the load's longest task). - Suite (CPU x6 phone, slow pack links): open max 220 ms (from 525), restore max 220 ms (from 89 468); menu opens in 102 ms mid-load vs 24 ms idle. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…empty frames compile it - sessions/autosave: environment AND post stack restored before the objects, then nextFrames(2): the composite shader and the helpers re-keyed by the new fog/lights compile on a frame with almost nothing to draw, not on the first frame of the full level. Then the hold + sliced warm. - sceneLoader.nextFrames(n) (rAF, 100 ms fallback where rAF does not run). - Suite: restore max 166 ms (PASS, from 89 468); open max 204 ms (from 525). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…honestly - FpsCounter: the detail parts are joined by " · " (svelte trimmed the whitespace: "251.2 ms·14 calls") - editor-ui-33: E2 measures the ring where it is DRAWN (an outer box-shadow over the neighbour; layers parsed by colour + size); E3 corner pixels >= 1 px outside the FAB; Q1 tier reads set+read in one page task on microtasks (the live meter republishes per rAF) - edit-move-games: an object a flow/module effect animates (Waves' Goal core) is judged by its BASE pose (suspendAnimation/read/resumeAnimation), and a fresh-load failure names dx/dy Counterfactuals (lanes-30/after-33/33-editor-ui/counterfactual*): with `p:hover` restored the well corners stay pill-grey beside a hovered Interact (E3.1/E3.2 RED); with classActive back on the toggle a rgb(255,213,204) 4px ring paints the neighbour (E2.1/E2.2 RED). The park: the game-running probe before c561f24 measured Towers 10/44, Stars Room 26/49, football 1/60 moved objects not staying (run1/run3.log). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… loaded toast waits - sessions/autosave: environment -> two frames -> post stack -> two frames: each re-keys programs on its own near-empty frame (measured ~60 + ~40 ms of links together). - qualityGovernor ignores frames while a scene load runs (desktop and XR): it answered a load's slow frames by dropping the pixel ratio, and that setSize was the longest task of a phone Restore (432 ms at CPU x6, tavern). - 'Session loaded' waits for the kit models (and says nothing for a cancelled/superseded load): its fly entrance forced a whole-document layout mid-load. - CLAUDE.md: the sceneLoader architecture entry. - Suite (CPU x6 phone, slow links): open max 197 ms, restore max 148 ms, menu 66 ms mid-load (26 idle) — 28/28. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…t play schedules the clip
- physics: with nothing dynamic, a sim start simulated the (sticky) selectedObject — the
door just placed. It fell over and every Interact click on it became a carry (the
pack-behavior 7.x flake). Functional items (userData.behavior) are skipped; a door-only
scene now gets the existing "Nothing to simulate" toast.
Counterfactual: pack-behavior 7.0a (the door dynamic) and 7.3/7.7 go red without it.
- animatedImports: with registration no longer autoplaying, nothing scheduled the action
when `playing` later turned true (a press, or a restored playing save) — the state said
playing and nothing moved. setAnimationState schedules it on the first play.
Counterfactual: animated-models "clip animates on A/B", animation-persist "animates
before saving / after the round trip" go red without it.
- packBehaviorDebug grows click diagnostics (clicks, lastClick {mesh, result}).
- suites: pack-behavior primes the walker, adds a real dynamic crate + 7.0a/7.0b, samples
the preview window (10.1); animation-persist's panel section starts from a playing clip.
Battery (vite inside e2e-slot, PEER_CONFIG): pack-behavior 51/0, animated-models 15/0,
animation-persist 23/0, game-audio 26/0, packs-explorer 10/0. svelte-check 333/47 (= base),
vitest 396/396, check-storage clean, npm run build green.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…all-panels sharpness probe (33 X1) - 1.18 (merged) keeps the headset eye buffer full (b8a785f, vr-panel-sharpness 6/0 here). Extending the probe to EVERY canvas panel: text in a canvas is only as sharp as the canvas, so a texture stretched over more headset pixels than it has texels reads soft on a sharp eye buffer. Measured as texels per headset pixel (texelRatio, a Quest 3's ~25 px/deg at the panel's real distance): board 0.78x and wrist card 0.60x were magnified; strip 1.07x and banner 1.13x already fine. - vrGamePanel: SCALE 1.5 -> 2 (board + pause menu 1.06x at 1.2 m) and WRIST_RES 2 (the 320x300 wrist layout drawn at 640x600 through drawCard's new `res`, hits returned in canvas px; 1.31x at 0.44 m). Layouts unchanged. - NEW suite vr-panel-sharpness-all 10/0: board, pause menu, wrist, strip, banner at their real poses + every visible vr-* canvas mesh is in the probe + a rendered check (the wrist's text edge energy from the eye, full vs half its texels: x1.40). CF (SCALE 1.5, WRIST_RES 1) -> 3 red (0.80x / 0.65x / 0.80x). troika-Text panels (radial, objects, props, keyboard) are SDF and resolution independent. - vr-game-panel 35, vr-laser-panels 25, vr-two-hand-grab 19, game-towers 21, game-shell-games 71, game-menu-layout 36 (re-run at the new scale) all green. svelte-check 333/47. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… checksum cache - sessions/autosave: environment -> hold + sliced warmPrograms of the (empty) scene -> post stack -> release + two frames for the composer's own shaders (compile() cannot reach them) -> hold for the build. The env re-key no longer links inside a frame. - packRefs: the checksum cache keyed by buffer versions is GONE — pack-refs caught an in-place vertex move without needsUpdate reading as pristine, which would save an edited piece as a stub (the edit lost on reload). Counterfactual = that pack-refs check, red with the cache, green without. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…oader.within) templates-modal (software GL) went red after the whole-scene warm became awaited before the build: a software link is hundreds of ms per program, so the scene stayed empty past the check's wait. The warm carries on in the background; the load never waits on the driver. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… ordinary-objects fixture - Outline.svelte registers warmComposerPasses through sceneLoader.registerComposerWarm: every pass (and each effect's internal passes) compiled against the target it draws into, one per slice, then first use per slice. The load paths call it (bounded by within) while the viewport is held, after restoring the post stack. - scene-load section 7 builds 1200 ordinary meshes (300 measured 166 ms even unsliced — too small to prove the slicing guard). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ompiles in batches of twins - sceneLoader.pump keeps the SHARED slice clock: a loop awaiting one scheduled item at a time started a fresh pump per item through microtasks, and the whole loop ran as ONE task — measured 881 ms of program warm-up on a 1200-mesh scene, and the source of several earlier residual tasks inside pump. - packRefs.warmPrograms: compile(object, camera, scene) walks the whole scene for lights per call, so per-mesh compiles were quadratic. Plain meshes compile in batches of 48 throwaway twins sharing their geometry + material (which prepares the real material); skinned / instanced / other drawables compile as themselves. - scene-load-trace: SLUGS=many-boxes (MANY=1200) — a synthetic scene of ordinary meshes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…pheres) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…iece in one draw call The 1.18 levels drew 320-394 calls per frame in the headset analogue (Tavern 394 at the spawn, Castle 371) against the Quest budget of 150, while their 224/210 meshes come from only 45/44 distinct pack meshes. A kit level IS a few pieces repeated. - kitInstancing.js: a render-time pass (lod.js registerBatchPass, AFTER the level swaps) groups eligible meshes by (the geometry they draw, the template material, a 16 m column centred on the origin, the shadow flags), hides the members for that render call and draws one scene-root InstancedMesh per group of two or more. The tree, picking, physics, the mesh tools and every serializer never see it (the lod.js rule). - Eligible = a refilled pack piece still pristine where it shows: its own geometry (unedited), its own material, not transparent, material signature equal to its template's. Selected pieces, pieces another pass hid or re-materialled, hidden roots are drawn as themselves. - packRefs.instanceOf records which template mesh each copy was cloned from (kitMeshSource, a WeakMap). - kitInstancingCore.js (pure): the column key, the material signature, the rules. - Settings > Performance "Draw repeated kit pieces together" (LOCAL, default on). - scripts/perf-levels.cjs + level-views.cjs: the draw-call probe for the General-tab levels from named viewpoints (desktop look, and the headset analogue: post off, shadows off as the XR governor's entry floor does). Measured (perf-levels, headset analogue, 1.18 levels authored on this union): Tavern spawn 394 -> 70, Castle spawn 371 -> 75, worst view 83. Tests: vitest kitInstancingCore 12; e2e kit-instancing ALL PASS 14 (fewer calls, same picture, picking, stubs saved, selected/recoloured/hidden/moved/cleared). Counterfactuals: signature check removed -> recoloured wall draws template colour (2 red); root-visibility check removed -> hidden pieces still drawn (premise + hidden red). svelte-check 332/47 (= union), vitest 430, check-storage clean. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…thor script places them
A door from the interactive kit is an animated import (its mixer binds the parsed tree, so
it cannot be packRefs' hollow stub), and a save carried its whole GLB: 0.5-1.4 MB of base64
per door in every .tpscene and every autosave. Measured: two DoorWoods = 1,320,948 B.
- animatedImports: an animated import that IS a pack file saves `animRef = {pack, item,
path}` (PACKS_BASE-relative, the kit-stub rule) instead of `bytes`; the restore fetches
the file once per url and parses it as before; the objectfile message, the undo entry and
applyObjectFile carry the reference so a peer's or an undone copy re-saves small too. An
entry with bytes restores and re-saves exactly as before; the wire still sends bytes.
- fileHandler.importFile stamps `userData.animRef` from the drop's packRef for an animated
file (it used to drop the reference). The Explorer drop already passes it.
- author-templates: a `kit` object whose pack row carries a `behavior` (contract P2) is
placed through importFile - the Explorer drop's own call - with the row's behavior and
LOD group, rotation and scale applied after.
Tests: e2e anim-pack-refs ALL PASS 11 (two doors = 1,204 B, PACKS_BASE-relative, behavior
+ LOD kept, restored from the pack, ONE fetch for two copies, re-save stays a reference,
an entry with bytes restores as before). Counterfactual: the fileHandler stamp removed ->
6 red (1,320,948 B saved). svelte-check 332/47.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A batch is culled as one object, so an off-screen member was drawn for every on-screen member of its batch. The pass now leaves every member outside the camera frustum out of its batch (three culls it as an ordinary mesh, no call), which keeps triangles where the frustum puts them - and makes wide columns free, so CELL_METRES goes 32 -> 128. Measured (perf-levels, headset analogue, the 33 levels' worst views): Tavern 460k -> 327k triangles, Wizard's Tower 375k -> 272k, Market 302k -> 237k; calls 120 -> 112 (Tavern back corner) with the wider columns. perf-levels gains --cell to try column sizes. Tests: kit-instancing +1 check (members 12 in the full view -> 5 looking at the west end). Counterfactual: the frustum test removed -> that check red (12 -> 12). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e top of its frame Two pre-existing collider faults the new scenes' doors walked into: - charController: rapier's KinematicCharacterController swept with NO query filter, so every sensor - the grass, flowers, rugs and water the 30c levels mark pass-through, a trim, a trigger volume - was a wall in Play. Found at the Tavern's open front door: a pass-through wainscot trim held the walker at the threshold (z 4.22). Both sweeps now pass QueryFilterFlags.EXCLUDE_SENSORS. - behaviorCore (33 P2 frame colliders): a LOW leaf in a TALL frame (the town kit's garden gate, ~1 m of picket under a 1.96 m frame) left the frame box above the leaf as one solid slab across the gateway, so the open gate still stopped the walker. A vertical passage now runs to the top of its frame box when more than 0.4 m of frame stands over the leaf (a door fills its frame and keeps its lintel); and a leftover thinner than 2 cm (the gate's static iron straps, 1 cm proud of the leaf) is dropped instead of becoming a 1.76 m wide invisible wall. Tests: e2e walker-sensors (walks through a sensor wall, stopped by a solid one); vitest behaviorCore +2 (the measured FenceGate boxes; the straps). Counterfactuals: the filter flag removed -> walker-sensors red (stopped at z -2.53); each behaviorCore rule removed -> its test red. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…treet; Wizard's Tower and Market Town Square scripts/level-templates.cjs (the General-tab levels, authored by author-templates): - TAVERN INTERIOR (the architecture shell, updated): the front door and the kitchen door OPEN (interactive-kit DoorWood / DoorStudded, frames included), the street window's shutters close, the loft chest and drawers open; furnished from the Interiors kit (bar counters + back-bars under the balcony, a stone fireplace, a tavern table set, a dining set, a chandelier on a beam, a kitchen run, a double bed and a wardrobe, teal wainscot); a slate gable roof (the hall is open to it); outside a Town & Market street (sidewalks, curbs, a crossing, lamp posts, a bench, barrels, a cart) and two cottages across it. You arrive on the road facing the front door. 32 KB. - WIZARD'S TOWER (new): a three-storey 8 x 8 m stone tower at dusk - an alchemy lab (trapdoor, chest, lever), a study (rune rug, lectern, desk, armillary, drawers, shutters), a bedchamber (wardrobe that opens) and a roof terrace with a telescope; a garden gate and the tower door open; an ambient banner stirs above the door. Hero props: arcane-kit (packs feat/33-arcane-kit). 24 KB. - MARKET TOWN SQUARE (new): a 16 x 16 m paved square - fountain, three stalls with goods, a well, benches, lamps, banner poles, a clock tower, a road with a cart, cottages round it - and a bakery you walk into (its door and shutters open). 28 KB. - a shared cottage() and street() helper; every building on the kit grid. Quest budget (draw calls per frame, headset analogue, every named view in scripts/level-views.cjs): Tavern worst 135, Tower 136, Market 98 (was 394 for the 1.18 Tavern). Tests: e2e level-templates-33 ALL PASS 67 - General-tab card load, every kit piece refilled and every functional piece registered, nothing moves in Edit, a REAL click opens and shuts a door in Interact (leaf 0 -> 95 deg), the ambient banner runs in Interact, Play spawn + sim + a readable frame, each door STOPS the walker shut and lets it THROUGH open, stairs to every storey, <= 150 calls from every view. Its earlier red runs found the sensor and gate faults fixed in the previous commit and the Market's missing dynamic bodies (no sim on Play). levelTemplates unit: five levels, all on the kit grid, every item in the union packs. level-templates (30c) keeps the Castle and the Forest. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…L2 + L4)
The user: "When opening another scene it should ask whether to keep all modules
... in settings it can be adjusted (automatically unload, automatically keep,
the default is to ask) ... the user should know about the behavior", and
"opened waves, then towers - music from waves stays and the gun".
- sceneSwitch.js: the setting (Settings > Scene > "When opening another scene":
Ask / Keep modules / Unload modules, safeStorage scenes:modulesOnOpen) and
prepareSceneSwitch(payload). The ask lists the loaded USER modules the scene
being LEFT uses and the incoming one does not need (a tool no scene uses and
every core module are never asked about); Unload primary, Keep, "Remember my
choice", Cancel aborts the open. The unload runs when the load really applies
(solo now, a peer proposal on acceptance). Unload = the live teardown journal +
switched off in Modules (persisted) + a toast with [Modules].
- The "This scene uses modules" prompt's Enable is LIVE now (it said "reload").
- Explorer Open: travelToLevel {askModules, freshGame} - the ask, and the file's
OWN game state instead of fork 3's carry (kept for the travel node / Go-to):
a Jam Room round and its variables no longer carry into Towers.
- applySession: `quiet` opt; sceneArrived settles the scope at the end.
- confirmDialog.showChoiceEx (items + one checkbox + checkedLabel).
- boot startSceneSwitch; debug hook +sceneSwitch +sceneScope (221/221/221).
e2e (real .tpscene + module zips, Games-tab path): scene-switch ALL PASS
(Waves -> Towers ask -> Unload: music, gun root, frame tasks 22 -> 8, objects
gone, Towers' 12 levels), scene-switch-keep ALL PASS (Keep + Remember, left
behind, no ask with the setting, the setting's Unload toast, live Enable),
scene-switch-games ALL PASS (Untangle -> Stars by the setting, Football ->
Jam Room lists Football only, Explorer Open starts fresh while a travel hop
still carries). Counterfactuals: ownerInScope -> true turns keep 1.13 red (the
Waves spawn places the Towers player); 3.6 (the travel node carries) is 3.4's
counterfactual in-test. vitest 391/391, svelte-check 333/47, build green.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The user: "after hitting clear scene in an opened game example I still see the Menu button and the ability to play ... clear scene does not really clear. The modal should say clear only objects, or modules and everything else ... pick the best decision here". - confirmClearScene is ONE modal (#confirm-clear-scene): "Clear objects" stays the primary (today's behaviour) and ONE box, "Also reset the game setup and unload its modules", whose hint names what (game menu & HUD, play & physics settings, music, sky & look, flow nodes; the modules) and which relabels the button "Clear everything". OFF by default, ON when there are no objects. A checkbox, not an Advanced chevron: it is the only extra choice there is. - Clear everything = sceneSwitch.clearSceneEverything: unload the scene's modules + apply an EMPTY scene payload (replicated, quiet) - no game chip, no pause menu, no flow, no HUD, the play block, game state and sky reset. - Objects only: a toast "Still here: ..." with [Clear those too]. - The Templates "Blank scene" card = a new scene: "Start a blank scene?" and always the full reset (#confirm-blank-scene). - The Sidebar row has an id (#clear-scene) and closes the menu behind the modal. - The dialog answers the click at once: sceneSwitch is a STATIC import here and the setup check reads primed refs. Through dynamic imports the modal took ~9 frames (measured 4.8 s in-page on Waves in the slow renderer, 9-15 s by click); now ~1 ms. e2e scene-switch-clear ALL PASS (modal < 8 s by click on a 2 fps page; objects-only leaves the setup + says so; everything resets it and unloads; Blank). templates- modal ALL PASS (its Blank section moved to the new modal). Counterfactual: the dynamic-import version reads 9303/8025 ms -> red on the prompt check. (Reverting the Towers clear fix alone stays green here: the scene USES towers, so the scope also hides its levels - guarded twice.) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The sceneScope / sceneSwitch architecture entry, the Clear scene modal, the suites and the 35-50 s per-open timing finding; api.inScene in the SDK section. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…-game moves, FPS + draw calls
…ngs, force/edit/select
…s that open, no autoplay) # Conflicts: # PACKS.md # src/App.svelte # src/components/editors/Explorer.svelte # src/lib/explorerDrop.js # src/lib/fileHandler.svelte.js # src/lib/wireValidate.js
…dow, with visible progress # Conflicts: # src/App.svelte # src/lib/packRefs.js
…scene, game B never inherits game A # Conflicts: # src/App.svelte # src/lib/sessions.js
…, phones start lighter, crisp VR panels # Conflicts: # src/lib/vrGamePanel.js
…aimInput('sticks'); every level + tabs in the VR pause menu
… Tavern rebuilt; kit instancing (the branch's accidental .lane-ready scratch file is dropped in this merge)
…lt from the kits The user asked to "update architecture shell scene"; 33-scenes rebuilt the Tavern (the planner's reading), and the card actually titled "Architecture shell" stayed a grey primitive room. It is now a kit room (level-templates SHELL_DEF): sandstone walls on flagstones, the Interactive Kit's studded door in its frame (opens in Interact/Play), a shuttered and a glazed window, columns, oak beams, a slate half roof, a spawn outside the door and a few dynamic crates so Play starts a world. - author-templates: a `remote: false` def field (seed-only); the greybox becomes `architecture-shell-greybox`, the offline seed's copy, never written to --out. - levelTemplates unit test: six General-tab levels; the shell is one room (> 60 pieces). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…clock; docs + baseline - scene-switch-clear 4.5 waits for "not a game any more": since 33-scene-load the apply is SLICED (objects empty before the HUD + play block are replaced), so the first read after the objects vanish saw the old scene (red on the union, green with the wait; 35/0). - animated-models "peers in phase" compares the peers' CLIP CLOCKS (tick + runtimeNow + wall time in one task per page) instead of two poses read in two evaluates — red 3/3 on a loaded box, the 33-anim-core branch included (A/B), while the pose is a pure function of the synced clock. - CLAUDE.md: round-33 entries (LOD groups, functional pack items + animRef, kit instancing, editor-ui/games/sticks) and five gotchas. - check-baseline.json ratcheted 333 -> 332 / 47 (the union's only svelte-check delta vs main is one REMOVED error). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- scene-load §6 clicks EXACTLY "Restore": on the phone profile 33-games' quality toast also offers "Restore full quality", and `hasText: 'Restore'` picked it first, so the session was never restored (6.1-6.5 red on the union, green with the exact match; the exclusive run is ALL PASS, longest task 151 ms — the 237/277 ms reads were the other slot's load). - animated-models launches on the GPU backend: three pages (the late joiner C) on software GL is the documented 1.17 three-page gotcha (C's Connect input never appeared, 2/2) — 14/0. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…estore full quality" 33-games starts every phone at a lighter quality level, so this toast (26-D) now shows on EVERY phone load — beside the session's own "Restore" toast after a reload, which is exactly the user's L1 path (phone, reload, Restore). Two buttons starting with "Restore" side by side: the e2e suite picked the wrong one, and so would a thumb, undoing the phone fix without restoring anything. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… Architecture shell (perf-levels) Both measured inside the Quest budget in the headset analogue (forest 77 / 31 calls, shell 32 / 31). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…view proven) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Round 33 ("packs that move, scenes that load, games that switch") for core 1.19.0.
Merged: #258 editor-ui · #257 lod-editor · #259 anim-core · #261 scene-load · #263 scene-switch · #260 games · #255 untangle-core · #262 scenes, plus integrate fixes:
remote: falsedef field keeps the greybox as the offline seed)Verification: vitest 464 · svelte-check 332/47 · build green · ~75 core suites + 31 modules flights green on the union · flicker probe 222/222 over ten mesh packs · preview https://preview-1-19.theprototype.pages.dev proven: seven games 211/0, round-33 items 58/0.
Per-item table and the owed-on-device list: the 33-integrate handover and the cloud roadmap-33 master.
🤖 Generated with Claude Code