Repository navigation
Conversation
…tic generator
- terrainScatterCore: a set may carry seed + gen {count|density, scale range, rot, spacing}
(GENERATED slots, each drawn from its own (seed, k) stream so a slot is a stable id),
ov {[slot]: {x,z,yaw,scale,del}} (hand edits a re-roll keeps), sources [{source, w}]
(a mixed set; painted records keep their pick in `pick`), tint, sink; every field optional,
so a 1.38 set normalizes to the identical object and the block stays v1 until one is used
- instancesOf (the ONE list: generated + overrides, then painted, ids g<k>/p<j>),
withOverrides, withSeed, generateSlots (place/accept hooks for 51-surface), pack sources
- terrainSurface draws per source with tint colours and sink, colliders from the same list
(one entry per source of a mixed set), pack items load through packRefs
- terrainPaint: writeScatter / editScatterSet / editScatterInstances / rerollScatterSet /
addScatterSet / scatterInstancesOf; the brush keeps v2 fields, counts generated instances
toward its density and erases them as deleted edits
- vitest scatterSetFormat (25): determinism twice + across two wire hops, stable slots,
masks, spacing on raw slots, re-roll keeps edits, migration byte-identical, weights
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…wn file
- ScatterSection.svelte = the Scatter <Section> block of TerrainPaintSection, moved
verbatim (set list, align, collider, remove, Scatter... button)
- TerrainPaintSection renders <ScatterSection {object} /> where the block was, so the
terrain Inspector is unchanged; any mesh can carry the section from here on (E2)
- svelte-check 299/39 = baseline
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e/scale/relax brushes, per-source erase, per-terrain splat detail - scatterEditCore.js (pure, vitest): box pick by projected point, rotate/scale/delete/re-roll a selection, group transform about a pivot, rotate/scale/relax/erase brushes (rate-based, smoothstep falloff, relax deterministic and clamped to the brush), yaw from a quaternion - scatterEditAdapter.js: the ONE file that knows the set format (today the 1.38 instance bytes; re-pointed at 51-sources' overrides when it lands, QUESTIONS-51-edit Q1) - scatterEdit.js: Scatter brush TOOLS Paint/Erase/Rotate/Scale/Relax/Select + Affects (one model or all = per-source erase); Select = click (Shift/Ctrl adds), box drag, Ctrl+A, Delete, Esc; the shared gizmo on a scene-root proxy (move on the ground, turn about up, uniform size) previews live and commits ONE terrainScatter entry per drag; toolbox Turn / Bigger / Smaller / Re-roll (keeps every other instance's edits) / Delete - every op = applyTerrainScatter + the existing objectParameters terrainScatter message + one terrainScatter undo entry: peers, undo and save need nothing new - per-terrain splat resolution: Inspector > Terrain paint > Detail 64..512, the map resampled (weights re-normalised to 255, coverage kept), one terrainSplat entry + message - terrainSurface: InstancedMeshes carry scatterIds (picking), scatterHolderOf(); terrainSculpt routes the edit brushes; Scene: proxy drag/change + the Select tool's press/move/release - four duotone tool glyphs; debug hooks scatterEdit / scatterEditCore Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…and a brush on any mesh
- buildSurface(positions, index?, normals?): the host's LOCAL triangles + an area CDF;
placeOnSurface(u, v) is uniform by area (u picks the triangle, its remainder + v the
barycentric), smooth normals interpolated when the mesh has them
- generateSurfaceSlots(gen, S, {draws}): FILL by count (keeps drawing until count are
PLACED, cap 30 x count attempts) or density (density x area attempts), masks slope /
height (local frame, so a moved host never reshuffles) / painted terrain layer, minimum
spacing as 3D Poisson-disk dart throwing in slot order; slot k = the caller's per-slot
stream, so raising the count adds without moving and every peer gets the same layout
- surfaceDab: the brush on a curved surface (sphere around the hit, the terrain brush's
density + spacing rules), eraseInSphere, a triangle grid for the query
- 16-byte records for a mesh host's painted instances (xyz over a stored box, octahedral
normal, yaw, scale) + boxOf / boxFrom
- vitest scatterSurfaceCore: 25 checks incl. two runs + two peers on one seed, spacing with
its counterfactual, saturation, records round trip; PERF on 99,904 triangles: build
~11-25 ms, fill 5,000 ~5-10 ms, fill 5,000 + spacing 0.3 + slope mask ~26-54 ms (node,
warm vs cold) - no worker needed
- svelte-check = baseline, vitest 2515 green
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…look, mixed weights, tint, sink - scatterSources.js: ONE catalog (primitives, Library, pack items, prefabs) shared by the brush toolbar, Properties > Scatter and the object menu; a prefab or a scene object scatters as its LOOK only (userData dropped, base at y 0, GLB filed in the Library by hash + pushed to peers) with a note the source row shows; a pristine kit piece scatters as its pack item (no bytes) - object menu: "Use as scatter source" (sets the brush's source) - Properties > Scatter: per-set model list (weights for a mix, remove), "Add a model to the mix", Tint and Sink scrubs (one undo entry + one message per drag), Re-roll for seeded sets, counts read what a set DRAWS - terrainScatterCore: withSourceAdded / withSourceRemoved (a removed model's painted records go with it; a mix of one becomes a plain set); terrainPaint: addScatterSource, removeScatterSource, setScatterSourceWeight, scatterBlockNow; debug hook scatterSources Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… (checkJs covers tests/unit) - svelte-check back to 299/39 = baseline (phase 1 had added 50, all in the test fixtures) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…acing in the set format
E2 - scatter on ANY mesh
- Inspector: the Scatter section (ScatterSection.svelte) renders on every plain mesh right after
the Water section (Add bubble / pour emitter); its "Add scatter" (#scatter-add) adds a seeded
FILL set with the brush's current source; a Terrain keeps its section beside Terrain paint
- the set format (terrainScatterCore, 51-sources' file, minimal): a set on a non-terrain mesh
carries host: 'mesh' + box; its painted records are 16 bytes (scatterSurfaceCore); absent on
every terrain set, so 1.38 terrain blocks stay byte-identical; overrides may carry y
- terrainSurface: ONE instance list per set (setInstances / scatterSetInstances) for the
renderer, colliders, brush and edit tools; a mesh host fills its own triangles (surface
cached per geometry identity); instances live in the host's LOCAL frame, so the existing
holder (matrixWorld copy) makes them follow a move / turn / scale on every peer
- the brush on a mesh: enterSculpt(uuid, {scatter: true}) = a scatter-only session that never
touches the geometry (no soup conversion, no size cap, nothing sent); the sphere brush
(surfaceDab) paints ON the surface, erase removes inside the sphere; one undo per stroke
- SculptToolbar offers Scatter on meshes (Paint stays terrain-only)
E3 - fill mode and masks (ScatterFillRows.svelte, per set)
- Count | Density (Segmented), minimum spacing, slope range, height range (local frame),
painted terrain layer + coverage, Re-roll, Remove fill; drags preview throttled and commit
ONE undo entry on release
- COUNT means count PLACED (generateSlots keeps drawing until placed, cap 30 x count) on
terrains too; terrain masks read the height grid + the splat (a layer-masked fill re-lays
when the paint changes); the Inspector shows the DRAWN count per set (scatterCounts)
- vitest: scatterSurfaceCore 28 (+ mesh-set format, ids on two peers, edits through the
16-byte codec); scatterSetFormat's mask test updated to count-placed; 2541 green
- svelte-check 299/39 = baseline; e2e suite scatter-surface written, queued (snapshot run)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…atter host too - terrainSurface.groupSurfaceOf: every child mesh merged into ONE surface in the group's own frame (relative transforms + normal matrices baked), keyed by each child's geometry and pose so a refilled kit piece or an edited child re-lays; terrainBounds falls back to it for a group - isMeshHost: a mesh, or a non-light group with meshes under it; the Inspector's Scatter section and the scatter-only brush session take it - Scene: the sculpt/scatter raycast is recursive for a group host, the brush ring takes the hit child's normal - e2e scatter-surface gains a group-host section (two boxes grouped, a slope-masked fill on both top faces, identical on B) - svelte-check 299/39 = baseline Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…tools - setScatterSelection records the scatter block it was taken against, so the onRevision check no longer reads our own (or an undone) block as a foreign change and drops a real click's selection or the re-selection after an undo - e2e scatter-edit: rotate / scale / relax brushes, the Affects-filtered erase, one undo entry per stroke, the second peer equal, per-terrain splat detail - e2e scatter-select: a REAL click and a REAL box drag, the gizmo move / turn / size (one entry per drag), Turn / Bigger / Re-roll (keeps every other instance) / Delete, Delete and Escape owned by the selection, the toolbox holding still; toolbox shots in dark / light / contrast. Split in two for the runner's 480 s cap Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…atter set - scatterConvert.instancesToObjects: the selection or a whole set becomes real objects at each instance's exact pose (ground, turn, slope tilt, size); one template per source (primitive / Library model / prefab - a logic prefab comes with its logic), duplicated per instance; asks first above 500; ONE history batch, so one undo turns them back - objectsToScatter: several copies of ONE object (a primitive or one Library model) on a terrain become instances of that source (position, turn, size kept) and the objects go, in one batch - entry points: Select tool "To objects", Properties > Scatter row "To objects", object menu "Make scatter" for a multi-selection - terrainSurface.scatterPrimitiveSpec (the create command, offset, colour, roughness a primitive instance is drawn with); debug hook scatterConvert - e2e scatter-convert: both directions, the 600-instance confirm, peers, undo, save/reload Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…evice line - scatterThinCore.js (pure, vitest): thinPlan walks instances nearest first and stops where the triangle budget runs out; NEAR_KEEP 30 m is never thinned; the last 20 % to the cutoff shrinks smoothly (smoothstep), so the edge reads as a fade; scatterBudget = half the target's line; thinProfileOf picks quest / phone / desktop from the XR session and a coarse pointer. - scatterThin.js (runtime): every scatter InstancedMesh registers its full matrices, colours and ids; one plan per frame at most every 150 ms or after a 0.75 m move; kept instances are compacted to the front and `count` lowered; userData.instanceIds is rewritten in DRAW order so a pick maps instanceId right. LOCAL only: nothing is saved or sent, and colliders never read it (scatterPhysicsSets builds every instance from the set). - Settings > Scene > Performance "Distant scatter" (Auto / Headset / Phone / Desktop / All), safeStorage key scatterDetail. - budgetMeterCore: a thinned InstancedMesh counts userData.instanceTotal, so the meter shows the content, not what this device happens to draw. - e2e scatter-sources: desktop + N6 folded peers; params + seed in one message (317 bytes for 6000 instances); the N6 draws 1488 of 6000 inside its 250k line with every instance within 30 m drawn; a ball dropped on a rock the N6 does not draw rests on it there and at the same height on the desktop; hand edits + re-roll; a weighted mix through the real select; tint + sink; a prefab as a look-only Library source; a 1.38 block stored byte-identical. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…-independent order - groupSurfaceOf sorts the child meshes by their pose in the group, vertex count and name (never a uuid, never tree order: a peer that built the group from messages adds the children as they arrive), so a seeded fill lands on the same triangles everywhere - e2e scatter-surface: density checked at the format's 4/m2 cap, spacing at a packable 0.2 m plus a past-saturation check (COUNT places what fits and stops), the grouped boxes are placed with replicated moves - 39/39 green two peers; terrain-paint 32, terrain-sculpt (2x lane, 2x base A/B: an earlier one-off red was a flake), mesh-sculpt 13, scatter-colliders 23 green Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…moved Scatter section - 51-sources added its per-set rows (model mix + weights, add a model, tint, sink) to the Scatter section in TerrainPaintSection.svelte; 51-surface had moved that section VERBATIM into ScatterSection.svelte (512623e, QUESTIONS Q2). TerrainPaintSection keeps our side; their rows, scrub helpers and imports now live in ScatterSection.svelte. - Drawn counts stay on scatterCounts (masks + mesh surfaces aware); their "Generated from seed N" Re-roll row is folded into the Fill block's Re-roll (the seed is in its title), so a set shows one Re-roll. - withSourceRemoved honours a host:'mesh' set's 16-byte records through io.records (as withOverrides does); rewriteSet passes surfaceRecords. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…o the 51-sources format Conflicts (QUESTIONS-51-surface A2), both sides kept: - SculptToolbar: Scatter panel on any host; source note row kept; 51-edit's tool row (Paint/Erase/Rotate/Scale/Relax/Select) on a terrain, Paint + Erase (the 47 erase flag) on a mesh host; Affects lists the drawn count + sourceLabel. - TerrainPaintSection: <ScatterSection> kept, 51-edit's Detail (splat resolution) row kept; the per-set "To objects" button moved into ScatterSection (terrain sets). - terrainSculpt: exitSculpt clears sculptScatterOnly AND ends the edit session; paintOp keeps scatter on meshes, 'scatter-edit' on a terrain only; strokeMove passes y; startScatterEditSession moved back into enterSculpt (auto-merge had put it in enterMeshScatter, where isTerrain does not exist). - terrainSurface layoutScatter: HEAD (sources/tint/sink/thinning); picking reads mesh.userData.instanceIds. Format seam (QUESTIONS-51-edit Q1): scatterEditAdapter re-pointed at 51-sources' API - lists = scatterSetInstances (string ids g<k>/p<j>), blockOf diffs each edited list into withOverrides (moves -> overrides, drops -> del, painted records rewritten; seed/gen/ sources/tint/sink ride through), id-less entries append painted records. Picking maps instanceIds -> list index; scatterEditCore setPoses/transformGroup keep id/src/tint; scatterConvert makes each instance of a mixed set from its own source and honours sink. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…verrides, painted rewrites, appends) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
three itself stays 0.186.1 (already npm latest; Dependabot moved it in 19ddcb6, shipped since 1.18.0). r186 migration notes audited against src/ and scripts/: no use of toTrianglesDrawMode, LightProbeGrid*, Source, SimplifyModifier, PCFSoftShadowMap on WebGPU, GTAONode, the Sky `up` uniform, and no Object3D subclass with its own dispose(). - three-mesh-bvh 0.9.15 -> 0.9.16: BVHHelper fix for r186 (peer three >=0.159). - @threlte/extras 9.22.0 -> 9.22.3: WebGPU variants + <Gizmo> fixes, drops the three-viewport-gizmo dependency (unused here; the app renders no threlte <Gizmo>). Peer ranges: postprocessing 6.39.5 three >=0.168 <0.187, three-bvh-csg >=0.179, threlte core/xr >=0.172/>=0.160 — all satisfied by 0.186.1. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- svelte 5.57.1 -> 5.57.2: patch fixes (async deriveds on destroyed components, untracked
derived reads retaining dependencies, {:else} after {:else} now a compile error, flushSync
flushes pending work first).
- svelte-check 4.7.6 -> 4.7.7: modern CSS properties, tsconfig "include" aligned with tsc.
check-ratchet unchanged: 299 errors / 39 warnings = baseline.
- @lucide/svelte 1.48.0 -> 1.61.0: icon additions/revisions (icon art can shift; covered by
the A/B screenshots).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- vitest 5.0.2 -> 5.0.5 (+ @vitest/mocker/spy 5.0.5): fixes GHSA-qxg6-89mg-rqv8 (code injection via unauthenticated manual mock registration) and GHSA-xp26-3454-mjgp (Windows backslash traversal past the mocker redirect allowlist) — the two open Dependabot alerts. `npm audit` = 0 vulnerabilities (was 2 moderate). - vite 8.3.3 -> 8.3.5 (rolldown 1.2.15): build preload-helper fixes (waits for in-flight stylesheets, chunk import map hashing), css `%` in url(); the rest is bundled-dev only (not used here). vitest: 206 files / 2572 tests, same as the base. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…t 4.14.0 The exact pins stay exact (48 G2 pinned iwer/devui; axe pinned since 53). - iwer / @iwer/devui 2.6.0 -> 2.7.0: hand poses/microgestures, run_sequence, and "spec-correct select and squeeze events". vr-iwer 36/0, vr-comfort-iwer 25/0, race-vr-shadows-iwer 13/0. - @axe-core/playwright + axe-core 4.13.0 -> 4.14.0: label-content-name-mismatch is now ON by default, plus false-positive fixes. a11y-axe 12/0 (0 serious/critical nodes on every surface), union-53 18/0. - prettier 3.9.9 -> 3.9.10 (patch). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
0.x minor (outside ^0.8.0), taken on proof (QUESTIONS-deps-1 Q1): libwebrtc M98 -> M106, fixes a segfault/abort at process exit (destructors ran after V8 shut down) and too-early GCs; RTCRtpTransceiverDirection "stopped"; spec addIceCandidate end-of-candidates. Proof: tools/agent npm test 16/16; an in-process two-peer data-channel loopback (offer/answer, ping -> pong) on 0.8.0 and 0.10.0; e2e agent-bridge 5/0 (real browser session <-> the Node agent over WebRTC: approve, create/move/color/delete land, Users list). npm audit (tools/agent) = 0. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…248) 1.64.0: WebMCP, video fps/style, locator.within(), page.getByRef(). Breaking changes checked against this repo: device descriptors now forward `screen` (no `devices[...]` used here), JSX/snapshot runner changes (no @playwright/test runner here), elements in hidden iframes count as hidden (the battery covers it). 1.64.1: Chromium 155+ preflighted cross-origin redirect abort with routes registered (fixed). Hosts: chromium-1248 installed on the Deck beside 1234/1243; the PC run-server (:39001) and tp-lanes serve 1.64.1 beside 1.62.1/1.63.0 (mux-pc-host, 2026-10-23). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…nel for pose streams
- fastChannel.js (leaf): a negotiated {ordered:false, maxRetransmits:0} data channel on each
PeerJS connection's own RTCPeerConnection (id 7, no extra signaling), opened only when the
other side advertises `fc: 1` in the modules handshake + a `fastchan` hello down the same conn
- every pose message (camera, vrhands, move) carries `ps`, one monotonic counter per sender;
receivers drop a pose older than the newest applied for that (sender, key) — on both channels
and inside batch envelopes, so a late streamed move never overwrites the committed one
- trailing keyframe: a stream's last pose is re-sent reliably (same ps) once the key goes
quiet for 250 ms, to the peers that got it unreliably
- streams marked fast: avatar camera (not when its extras change), VR hands (not on a rep
switch), gizmo drag, physics bodies, VR held objects (not the release), play-mode grabs,
possess rides; everything else stays on the reliable conn
- graceful fallback: older peers / closed channel / >12 KB messages go reliable; kill switch
safeStorage fastChannel=off; `fastchan` on the cloudHooks ALWAYS_ALLOWED floor
- debug hook __stores.fastChannel (links, stale filter, keyframes, pose tap for the loss e2e)
- unit: tests/unit/fastChannel.test.js (18)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- manifest: share_target (POST multipart `files`: images, glb/gltf, tpscene), file_handlers (.tpscene, .glb, .gltf → /), launch_handler focus-existing - static/sw.js: receives the share POST (a static site has no server), parks the files in the `tp-share-inbox` cache and 303s to /?shared=<n>; everything else is still a no-cache passthrough - src/lib/launchInbox.js: reads the inbox ONCE (cache deleted, ?shared= stripped) and takes launchQueue file launches; routes .tpscene → open (Sidebar Load path), models → import into the current scene (companions ride along), lone images → Explorer Library + "Show in Library"; waits for the scene group AND the restore-session answer first - autosave: restoreSettled() — resolves once the boot's restore question is answered (no snapshot / non-empty / auto-restored / Restore finished / Dismiss) - tests/unit/launchInbox.test.js (14): manifest keys, the real sw.js in a fake worker scope (park + 303, text-only, replace, passthrough), the worker → app round trip, classifier Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…r a cap - assetCacheCore.js (zero-import leaf): url keys (+ a version tag for build-bundled files), derived keys (source hash + kind + settings, field order irrelevant), the index shape and its normalizer, usage (a shared blob counted once), the LRU eviction plan, orphans - assetCache.js: one blob per SHA-256 in OPFS (tp-asset-cache/blobs) + index.json; the same model on CacheStorage where OPFS cannot write; a plain fetch where neither exists. Reconciles orphan blobs / missing blobs on open; index flushed on a 400 ms debounce and on pagehide - kinds wired: pack files (packCache.js is now a url-keyed view over the cache, same API; 39 P5's CacheStorage copies are migrated once and that cache deleted), bundled skies (hdriRuntime, keyed by the build version), CC0 downloads (cc0Fetch.fetchBytes), converted textures (the import optimizer's output keyed by source hash + options + version), pack anim refs - navigator.storage.persist() asked once, from a toast with the reason, the first time something is cached (assetCache:persistAsked); never on boot, never again - never the app shell: only content urls reach it - unit: tests/unit/assetCacheCore.test.js (13); place-drag-cache reads the store through packCache.cachedPackBuffer instead of CacheStorage Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…uites - Settings ▸ Storage (StorageSettings.svelte, Workspace group): what the cache holds by kind, Clear, the size limit (256 MB / 512 MB / 1 GB / 2 GB, LRU), the persistence state with "Ask the browser", and the Storage breakdown button - Storage breakdown: a "Cached downloads" category (skies, converted textures, downloaded files), one row per kind, reclaimed through the cache's own delete - e2e: asset-cache-opfs (second load served from OPFS with no request, persist asked once, no shell in the index, Settings Clear, the 39 P5 migration), fast-channel-loss (p95 pose gap at 0/5/10 % loss before/after through a WebRTC loss shim), fast-channel-fallback Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ngs the view back The player starts where the editor camera is: X/Z under it, the feet on the first solid surface straight below (scene meshes on the shared BVH trees, then the physics ground plane; hidden objects, sensors, water and reference images do not count), facing the view's heading. Nothing below → the scene's spawn; no spawn either → the camera's eye. A session-only override that currentSpawn() returns first, so desktop Play, the VR walker, api.respawnPlayer and the game shell all honour it for that round; leaving Play clears it and restores the editor camera, its orbit target and the selection. Entry points: Ctrl+Shift+Enter, a row in Play's right-click / phone long-press menu, an optional toolbar roster button (Customize), phone More ▸ Tools & View, the viewport menu's Tools, and the VR radial Tools ▸ Play here / Back to edit (Interact from where you stand; Edit restores the exact reference space and world grab). Height follows the scene's locomotion: desktop Play is grounded by default (37 R24 pins the eye), and the VR walker stands on real surfaces only while a simulation runs. Tests: unit playHere (the spawn raycast through a real three scene), e2e play-here (desktop + phone N6 folded) and play-here-iwer (IWER, applied-offset spy). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rop menu (untested) underlays.js (LOCAL per scene in IndexedDB; Share with session = underlay / underlaydelete / getunderlays, owner-only rows, ROOM_SCOPED, request on the floor, validated), UnderlayLayer (under the world rig, outside sceneObjects, on the editor helper layer, hidden in Play/Interact, no-op raycast), UnderlayWindow (opacity, size, height, ground / facing the view, share, remove) + the image-drop 'Use as reference' menu, viewport Tools rows. Viewport/room thumbnails now render without the helper layer. NOT yet run: svelte-check ratchet after the type fix, the underlay e2e suite. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…llback counts only its own poses; fix-live survives a cloud build - launch-inbox: the 2x2 PNG had a broken IDAT (the Library import never decodes, Use as reference does) — a valid one; the toast button is a DOM click; the check prints why. - launchInbox: Use as reference says so when there is no scene view yet, and records the outcome (lastLaunch.reference) for the debug hook. - fast-channel-fallback: the tap counts the suite's own poses only (the app's camera loop sent a 61st). - fix-live: the viewer role is re-asserted inside each AI call (a cloud build's roles plugin re-publishes rolesInfo); the re-dial after a graceful leave waits for the idle pill and taps a squeezed dot. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…runner speed) scatter-sync phase 1: the four two-peer scatter suites only fail on GitHub's runners; this lets them be reproduced on a fast box (throttle 2 + taskset 4 cores did it). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… reach them broadcast skipped a conn that was not open, so everything the host did between approving a joiner and the dial-back opening (15 s on a throttled run) was dropped; the joiner only converged if its full-state request was answered later, through the slow GLTF reply (+24.6 s measured: the scatter suites' 'B has the terrain' on GitHub). The host now holds room-scoped broadcasts for a peer in approvedDialBacks (bounded: 300 messages / 120 s, a leaf with unit tests) and flushes them, re-gated, after that conn's handshake. Counterfactual: e2e dialback-sync, red on v1.43.2, green here. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…imum); E2E_LONGTASKS logger On a GitHub-speed runner the approval dial-back opened 15 s after approval, past the fixed 9 s settle, so a suite's first edit raced a negotiating conn. connect() now also waits (bounded 45 s, never failing) until both ends hold an open conn. E2E_LONGTASKS=<ms> prints main-thread tasks over that length, to tell a hung click from a slow one. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… (h.eventuallyAfter)
On a GitHub-speed runner a two-peer page renders a frame every 1.5-2.5 s (E2E_LONGTASKS,
CPU x2), so B dispatches A's messages long after A sent them and fixed 10-15 s windows
went red with nothing lost ('B paints at 256 too'). h.caughtUp sends A's ordinary clock
ping to B and waits for B's pong: on the ordered conn that is the moment B has dispatched
everything before it (capped 120 s, says so if not). Each check keeps its own window,
measured from there, so a lost message is still a red.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ys cancelled (#468) * [docs] deps-auto 3g: pc-battery — PC_BATTERY_WRAP has to be quoted in a systemd unit Unquoted, Environment=PC_BATTERY_WRAP=e2e-slot --exclusive ... is split at the spaces (found installing the timer on the desktop). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * [fix] deps-auto 2j: shards balance on the median runner time of runs 5-10 (16 shards; replayed slowest shard 49 -> 35-38 min) - refresh --results is repeatable and takes the median per suite: a suite's time moves by half between runs on a shared runner, and the 15-shard split fitted to run 9 alone put run 10's shards anywhere from 21 to 50 min - battery.json seconds from runs 5-10 (required + optional passes); replaying runs 8 / 9 / 10 on the new split: max shard 38 / 37 / 35 min (was 49 / 38 / 49) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * [fix] deps-auto 2k: battery only where it tests something; optional set by hand - deps-policy.cjs: new `battery` output = the PR moves the root app's npm packages and is not a major - deps.yml: perf budget + plan/e2e only when battery=true (Actions, tools/agent and majors get ci.yml alone); a major's deps-gate fails on purpose ("not tested here"); the optional set runs on workflow_dispatch only - why: Dependabot's first round after 1.43.0 opened 9 PRs at once = ~380 jobs on the account's 20-job cap, queueing the v1.43.0 release job and every lane's CI for hours (runs cancelled, re-run after this) - RELEASING.md + depsCi test (9/9); check-ratchet 299/39 = baseline; actionlint clean Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * [fix] deps-auto 2l: a cancelled deps run stays cancelled - confirm / e2e-optional / optional-report / deps-gate used always(), which also runs after a cancel: cancelling Dependabot's 9 first-round runs still started their optional sets (18 shards each); now !cancelled() Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * [fix] deps-auto 2m: three of 51's scatter suites optional on the GitHub runner; medians from runs 5-12 - run 12 (#468, released 1.43.2 app code, CI-only diff) was the scatter suites' first run on the runner: scatter-select + scatter-convert red twice (shard + fresh confirm runner: "B has the terrain" never true), scatter-sources at the 600 s suite timeout twice (all its checks green until then) -> optional with reasons - scatter-edit was flaky (green on the confirm runner) and stays required - refresh --results over runs 5-12 (one durations file per run): 16 required shards ~35 min each Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * [fix] deps-auto 2n: scatter-edit optional on the GitHub runner too - run 13 (#468): scatter-edit red on its shard and both confirm tries, the same 'B has the terrain' as scatter-select/convert in run 12 (where it was green on confirm); scatter-surface flaky, stays required Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…back queue Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…k to required GitHub probe (scatter-sync-probe run 40191960730): 'B has the terrain' green on all three runners, runners 1+2 all green; the slowest runner's reds were all a real click timing out at 30 s with no main-thread task over 8 s on the clicking page — each input event waits for a frame ack, and a frame took seconds there. h.PATIENT_CLICK_MS (90 s, E2E_CLICK_MS) keeps every actionability check and only waits longer. scatter-colliders (optional since deps-auto run 5) passed on runners 1+2 and goes back to required. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…tach, not only the click)
deps-auto run 16 timed out scrollIntoViewIfNeeded ('waiting for element to be stable', i.e. two
frames with one box) before the click at :136. Measured (probe-surface-jank, CPU x2, two pages):
A's frame is ~2.2 s with one sphere idle, the same after the selection opens the Inspector
(no extra canvas, no long task beyond the frame), 2.45 s after Add scatter; the click needs
~13 frames = 28.7 s. A slow runner, not a jank at that click — the overload guard's own rule
(a light scene on software GL is slow, never paused).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…l look gets 90 s; runner estimates Confirm probe (run 40196033046, three slower runners): select/convert/edit green x3; the rest were load, not sync. scatter-colliders' drop() sampled stillness on wall time while the step accumulator catches up <= 8 substeps a frame (30 s wall ~ 1 s simulated on multi-second frames): each sample now spans 500 ms AND 8 rendered frames, capped at 240 s, and the watcher compares after a wire barrier. 'A draws the crates' (a local look load) 20 s -> 90 s. battery.json runner estimates for the six suites set from the probes (they also set battery.cjs' per-suite cap, 3x the estimate, min 600 s, which cut scatter-sources/-colliders off mid-run). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… runners; cap = 3x) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ettings ▸ Storage, poses on the fast channel); battery-snapshot copies quarantine
- vr-radial-menu, phone-bar-42, toolbar-edit-positions ("never Play" no longer matches "Play here"),
settings-shell (Storage after Explorer): the 60 Q1/Q6 additions.
- avatars-rigged, net-stress: camera/vrhands ride the fast channel between two 1.44 peers — count the
fast arrivals through fastChannel's pose tap as well as the reliable conn.
- ai-flow-physics prints the tool errors it finds.
- battery-snapshot.sh: battery.cjs requires scripts/ci/quarantine.cjs + tests/e2e/quarantine.json
(test-health-1) — a base snapshot crashed at load without them.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
peerHandler: #474's broadcast shape (an approved joiner's opening dial-back holds room content, withholdFrom/sendTo shared with the flush) carries 60 Q2's fast-channel attempt and keyframe bookkeeping inside it; sendReliableTo (the trailing keyframe) re-gates through withholdFrom. battery.json: the union's optional notes and estimates + #474's dialback-sync and its six scatter estimates; the scatter-* optional keys removed (scatter-colliders was the one left). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…asks for a dynamic Pebble (fix-live F-4) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…4.0 changelog line for #474 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* [docs] deps-auto 3g: pc-battery — PC_BATTERY_WRAP has to be quoted in a systemd unit
Unquoted, Environment=PC_BATTERY_WRAP=e2e-slot --exclusive ... is split at the spaces (found
installing the timer on the desktop).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* [fix] deps-auto 2j: shards balance on the median runner time of runs 5-10 (16 shards; replayed slowest shard 49 -> 35-38 min)
- refresh --results is repeatable and takes the median per suite: a suite's time moves by half
between runs on a shared runner, and the 15-shard split fitted to run 9 alone put run 10's
shards anywhere from 21 to 50 min
- battery.json seconds from runs 5-10 (required + optional passes); replaying runs 8 / 9 / 10 on
the new split: max shard 38 / 37 / 35 min (was 49 / 38 / 49)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* [fix] deps-auto 2k: battery only where it tests something; optional set by hand
- deps-policy.cjs: new `battery` output = the PR moves the root app's npm packages and is not a major
- deps.yml: perf budget + plan/e2e only when battery=true (Actions, tools/agent and majors get ci.yml alone);
a major's deps-gate fails on purpose ("not tested here"); the optional set runs on workflow_dispatch only
- why: Dependabot's first round after 1.43.0 opened 9 PRs at once = ~380 jobs on the account's 20-job cap,
queueing the v1.43.0 release job and every lane's CI for hours (runs cancelled, re-run after this)
- RELEASING.md + depsCi test (9/9); check-ratchet 299/39 = baseline; actionlint clean
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* [fix] deps-auto 2l: a cancelled deps run stays cancelled
- confirm / e2e-optional / optional-report / deps-gate used always(), which also runs after a cancel: cancelling
Dependabot's 9 first-round runs still started their optional sets (18 shards each); now !cancelled()
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* [fix] deps-auto 2m: three of 51's scatter suites optional on the GitHub runner; medians from runs 5-12
- run 12 (#468, released 1.43.2 app code, CI-only diff) was the scatter suites' first run on the runner:
scatter-select + scatter-convert red twice (shard + fresh confirm runner: "B has the terrain" never true),
scatter-sources at the 600 s suite timeout twice (all its checks green until then) -> optional with reasons
- scatter-edit was flaky (green on the confirm runner) and stays required
- refresh --results over runs 5-12 (one durations file per run): 16 required shards ~35 min each
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* [fix] deps-auto 2n: scatter-edit optional on the GitHub runner too
- run 13 (#468): scatter-edit red on its shard and both confirm tries, the same 'B has the terrain' as
scatter-select/convert in run 12 (where it was green on confirm); scatter-surface flaky, stays required
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* [fix] deps-auto 2o: auto-merge in its own job with contents: write
- #460 (actions, class auto) went green through the gate on the 2k deps.yml, then the gate's auto-merge step
failed: 'Resource not accessible by integration (enablePullRequestAutoMerge)' (it needs contents: write)
- a separate automerge job after a green deps-gate holds contents + pull-requests write and checks nothing
out, so the gate (which runs the PR's code) stays contents: read
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* [fix] deps-auto 2p: battery.cjs run with no --deadline runs every suite
- the deadline was Date.now() + 0, which is truthy, so '|| Infinity' never applied: by hand (no flag) every
suite after the first came out notrun. CI always passes --deadline, so the workflows were unaffected.
Found by lane scatter-sync.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* [fix] deps-auto 2q: scatter-surface optional on the GitHub runner
- red on its shard in runs 13, 14 and 16 (#473 run 16: shard + both confirm tries): the Add scatter click
times out (locator.click 30 s) - a different failure from the late-joiner terrain race
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* [fix] deps-auto 2r: knock-physics + env-preset-broadcast optional on the GitHub runner
- the two required suites that fail on their shard about half the time (runs 10-17): knock-physics (shard red
in 8, 9, 12, 14, 17; run 17 also both confirm tries, '2.3 (premise) B sees the ball parked') and
env-preset-broadcast (shard red in 10, 12, 13); at ~50% a run loses all three tries ~1 in 8, so each
alone turned deps-gate red every few runs. The rest of the flaky set is ~25% or less
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…terial-presets-big keeps peerjs's chunked flag Remove only cut the host's own link: the roster applier unions every relayed userdata and a 'hosts' mesh-fill dials whoever a third peer still holds, so B put C straight back (fix-live F-5 'Remove drops C' / 'cannot dial back in' red 2 of 3 on the preview). Both now skip removedPeers. The suite's send counter dropped send()'s second argument, so peerjs re-chunked its own chunks until the stack overflowed (a test-made page error). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s skipped (#475) * [docs] deps-auto 3g: pc-battery — PC_BATTERY_WRAP has to be quoted in a systemd unit Unquoted, Environment=PC_BATTERY_WRAP=e2e-slot --exclusive ... is split at the spaces (found installing the timer on the desktop). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * [fix] deps-auto 2j: shards balance on the median runner time of runs 5-10 (16 shards; replayed slowest shard 49 -> 35-38 min) - refresh --results is repeatable and takes the median per suite: a suite's time moves by half between runs on a shared runner, and the 15-shard split fitted to run 9 alone put run 10's shards anywhere from 21 to 50 min - battery.json seconds from runs 5-10 (required + optional passes); replaying runs 8 / 9 / 10 on the new split: max shard 38 / 37 / 35 min (was 49 / 38 / 49) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * [fix] deps-auto 2k: battery only where it tests something; optional set by hand - deps-policy.cjs: new `battery` output = the PR moves the root app's npm packages and is not a major - deps.yml: perf budget + plan/e2e only when battery=true (Actions, tools/agent and majors get ci.yml alone); a major's deps-gate fails on purpose ("not tested here"); the optional set runs on workflow_dispatch only - why: Dependabot's first round after 1.43.0 opened 9 PRs at once = ~380 jobs on the account's 20-job cap, queueing the v1.43.0 release job and every lane's CI for hours (runs cancelled, re-run after this) - RELEASING.md + depsCi test (9/9); check-ratchet 299/39 = baseline; actionlint clean Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * [fix] deps-auto 2l: a cancelled deps run stays cancelled - confirm / e2e-optional / optional-report / deps-gate used always(), which also runs after a cancel: cancelling Dependabot's 9 first-round runs still started their optional sets (18 shards each); now !cancelled() Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * [fix] deps-auto 2m: three of 51's scatter suites optional on the GitHub runner; medians from runs 5-12 - run 12 (#468, released 1.43.2 app code, CI-only diff) was the scatter suites' first run on the runner: scatter-select + scatter-convert red twice (shard + fresh confirm runner: "B has the terrain" never true), scatter-sources at the 600 s suite timeout twice (all its checks green until then) -> optional with reasons - scatter-edit was flaky (green on the confirm runner) and stays required - refresh --results over runs 5-12 (one durations file per run): 16 required shards ~35 min each Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * [fix] deps-auto 2n: scatter-edit optional on the GitHub runner too - run 13 (#468): scatter-edit red on its shard and both confirm tries, the same 'B has the terrain' as scatter-select/convert in run 12 (where it was green on confirm); scatter-surface flaky, stays required Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * [fix] deps-auto 2o: auto-merge in its own job with contents: write - #460 (actions, class auto) went green through the gate on the 2k deps.yml, then the gate's auto-merge step failed: 'Resource not accessible by integration (enablePullRequestAutoMerge)' (it needs contents: write) - a separate automerge job after a green deps-gate holds contents + pull-requests write and checks nothing out, so the gate (which runs the PR's code) stays contents: read Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * [fix] deps-auto 2p: battery.cjs run with no --deadline runs every suite - the deadline was Date.now() + 0, which is truthy, so '|| Infinity' never applied: by hand (no flag) every suite after the first came out notrun. CI always passes --deadline, so the workflows were unaffected. Found by lane scatter-sync. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * [fix] deps-auto 2q: scatter-surface optional on the GitHub runner - red on its shard in runs 13, 14 and 16 (#473 run 16: shard + both confirm tries): the Add scatter click times out (locator.click 30 s) - a different failure from the late-joiner terrain race Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * [fix] deps-auto 2r: knock-physics + env-preset-broadcast optional on the GitHub runner - the two required suites that fail on their shard about half the time (runs 10-17): knock-physics (shard red in 8, 9, 12, 14, 17; run 17 also both confirm tries, '2.3 (premise) B sees the ball parked') and env-preset-broadcast (shard red in 10, 12, 13); at ~50% a run loses all three tries ~1 in 8, so each alone turned deps-gate red every few runs. The rest of the flaky set is ~25% or less Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * [fix] deps-auto 2s: automerge runs after a green gate even when the battery was skipped - #460 on c6dda92: run green (scope auto, ci green, deps-gate + pc-battery SUCCESS) but automerge SKIPPED: its implicit success() also checks the gate's own needs, and perf/plan/e2e are skipped for every PR without the battery. Now: !cancelled() && needs.gate.result == 'success' && class auto && base deps Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Bumps the actions group with 6 updates in the / directory: | Package | From | To | | --- | --- | --- | | [actions/checkout](https://github.com/actions/checkout) | `4` | `7` | | [actions/setup-node](https://github.com/actions/setup-node) | `4` | `7` | | [actions/download-artifact](https://github.com/actions/download-artifact) | `4` | `8` | | [actions/upload-artifact](https://github.com/actions/upload-artifact) | `4` | `7` | | [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) | `2` | `3` | | [softprops/action-gh-release](https://github.com/softprops/action-gh-release) | `2` | `3` | Updates `actions/checkout` from 4 to 7 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v4...v7) Updates `actions/setup-node` from 4 to 7 - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](actions/setup-node@v4...v7) Updates `actions/download-artifact` from 4 to 8 - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](actions/download-artifact@v4...v8) Updates `actions/upload-artifact` from 4 to 7 - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](actions/upload-artifact@v4...v7) Updates `dependabot/fetch-metadata` from 2 to 3 - [Release notes](https://github.com/dependabot/fetch-metadata/releases) - [Commits](dependabot/fetch-metadata@v2...v3) Updates `softprops/action-gh-release` from 2 to 3 - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](softprops/action-gh-release@v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: actions/download-artifact dependency-version: '8' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: actions/setup-node dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: actions/upload-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: dependabot/fetch-metadata dependency-version: '3' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: softprops/action-gh-release dependency-version: '3' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
battery.json: deps' runner medians and optional reasons; scatter-surface takes the runner's 238 s; the scatter-* optional keys go after #474 (orchestrator) — knock-physics and env-preset-broadcast stay optional until green twice on the runner. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
net-drop on the deployed preview lost ONE direction's edits after a heal: one end closed its link
(F-3's ICE grace, or the drop) and redialled, while the other end's conn still read open - a close
never crosses a dead link - so it refused the inbound heal ("the stable outgoing conn stays
preferred") and kept sending, and answering the heal's full-state requests, into a conn nobody
reads. A healed handshake now says `relink` before anything that is answered; the receiver whose
conn still reads open adopts the conn it came on, closes the stale one and re-syncs both ways.
`relink` is on the capability floor (transport, no content).
net-relink manufactures the zombie (higher id: reads open, swallows sends, hears no close; the
lower id closes its end): 9/0 twice; counterfactual without the relink send 6/3 (the zombie is
kept, Z's edits and full-state reply never reach L). fix-live 46/0, dialback-sync 7/0,
fast-channel-loss 32/0 with it; net-drop/net-reconnect red alike on base and relink snapshots
(box load ~19). Battery: net-relink 97 s, optional until a runner has seen it green.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Release 1.44.0 — Quick wins (roadmap 60)
…P again) three's prebuilt basis_transcoder.js (basis_universal v1_50_0, Emscripten 3.1.64) builds its embind invokers with newFunc(Function, args). The build's CSP has no 'unsafe-eval' and KTX2Loader's blob: worker inherits it, so every KTX2 texture died with an EvalError (since 1.43.1). - static/basis: same source + flags, plus -s DYNAMIC_EXECUTION=0 -s EMBIND_AOT=1 (basis_universal's own opt-in NO_DYNAMIC_EXECUTION build, b15211474d). wasm byte-identical, JS 57,529 -> 66,534 B. - scripts/build-basis-transcoder.sh reproduces it; --reference reproduces three's two files byte for byte (sha256 8478b5b6… / 6cf17dc8…), which pins the toolchain. - scripts/no-eval-scan.cjs (acorn): finds eval / the Function constructor used as a value, which a grep for "new Function" misses. tests/unit/basisNoEval.test.js runs it over static/**/*.js (red on v1.44.0's transcoder, green now). - static/basis/README.md: provenance, sizes, sha256s, how to rebuild after a three update. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…LOG line - csp-build (built app, serve-build): transcode an ETC1S and a UASTC KTX2 through the app's KTX2Loader under the build's CSP. Built-app A/B (pc slot): fix 8/0; v1.44.0's transcoder 7/1 (the new check, "no answer in 30 s"). ktx2-twins 20/0, export-presets 19/0, code-popout 40/0 on the fixed build; production 1.44.0: ktx2-twins 10/4 then timeout (EvalError), export-presets hangs. - no-eval-scan: comparisons (`x.constructor === Function`) and `Function.prototype` are not eval. - CHANGELOG: "Compressed (KTX2) textures load again" under Unreleased (the integrator versions it). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
Shipped in 1.45.1 via cherry-pick (release/1.45.1, tag v1.45.1); feat/70 gets it by merging the tag. |
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.
What
Since 1.43.1 a build's CSP has
script-src 'self' 'wasm-unsafe-eval' blob: …with no'unsafe-eval', and that stays. three.js's prebuiltbasis_transcoder.js(embind glue) builds its invokers withnewFunc(Function, args), and KTX2Loader'sblob:worker inherits the page policy. So every KTX2 texture failed with anEvalError: import twins never formed (full-size textures stayed in GPU memory) and "For Quest / web" exports could not be opened back. ktx2-twins and export-presets were red/hung on production.Fix
static/basis/basis_transcoder.{js,wasm}: the same source (basis_universalv1_50_0,051ad6d8) with the same flags and toolchain (Emscripten 3.1.64), plus-s DYNAMIC_EXECUTION=0 -s EMBIND_AOT=1. That is basis_universal's own opt-in no-eval build (BinomialLLC/basis_universal@b15211474d, issue Bump the dev group with 5 updates #464). Upstream still ships the eval build, and three.js closed KTX2Loader: bundled basis_transcoder.js requires CSP 'unsafe-eval', but rebuilding with -sDYNAMIC_EXECUTION=0 fixes it mrdoob/three.js#34389 as upstream. The wasm is byte-identical. The JS goes from 57,529 to 66,534 B (gzip 15,060 → 15,501).scripts/build-basis-transcoder.shreproduces the build exactly. No cmake is needed: it runs the commands CMake generates. With--referenceit rebuilds the stock flags and reproduces three's two files byte for byte (sha2568478b5b6…/6cf17dc8…), which is how the toolchain was pinned. Rebuild time: ~10 s compile + link; the Emscripten install takes ~200 s once.scripts/no-eval-scan.cjs(acorn) findsevaland theFunctionconstructor used as values. A grep fornew FunctionmissesnewFunc(Function, …).tests/unit/basisNoEval.test.jsruns it overstatic/**/*.js, so a vendored three update can't bring the eval copy back. It fails on v1.44.0's transcoder and passes now.tests/e2e/csp-build.test.cjsnow also transcodes an ETC1S and a UASTC KTX2 under the build's policy.static/basis/README.mdrecords provenance, sizes and sha256s, and how to rebuild after a three update.Proof
All runs are on a built app (
scripts/serve-build.cjs, production's CSP meta) on a pc slot.Transcode speed: 12 KTX2 textures → BC7 through the app's KTX2Loader on an RTX 4090.
'unsafe-eval'added).Unit tests 23/0, svelte-check ratchet 287/39 (= baseline).
Not a security advisory: nothing is exploitable and the CSP is unchanged.
🤖 Generated with Claude Code