# THREADS — append-only lane log One line per landed feature, contract change, or open question. Format: `[lane letter] YYYY-MM-DD — note` [.] 2026-07-16 — repo seeded: DESIGN.md (canon), PLAN3D.md (build plan), prototype/ (2D reference, do not modify) [E] 2026-07-16 — ⚠️ PLAN3D §2's asset paths do not exist on this box. We are on m3ultra.local (Apple M3 Ultra); the plan was written against the M1 Ultra (johnking@100.91.239.7), so its "verified" inventory is that box's, not ours. Corrections below — read before you go hunting for a library. [E] 2026-07-16 — ✅ present here: 90sDJsim (~/Documents/90sDJsim/ — vendored three r175, web/world/models/peds/ full fleet, DEVMANUAL.md). 3D-STORE IS here but at ~/Documents/Destroyulater/3D-STORE/ (NOT ~/Documents/3D-STORE) — has build_booth_room_v23.py, racks_to_glb.py, clean_glbs/. Blender 5.1.2. [E] 2026-07-16 — ❌ MISSING here entirely, under any name: ~/Documents/character_kit/, ~/Documents/FBX/, ~/Documents/3D=models/, ~/Documents/mixamo-fetch/ (there is an unrelated ~/Documents/mixamo_upload/). Impact: Lane D's whole anim pipeline (character_kit rigs + FBX clips + mixamo-fetch wishlist) has no source on this machine, and Lane A's `3D=models/street-furniture/building_shell_01.glb` does not exist — use Lane E's house_yardside_v1.glb instead. Either those libs live only on the M1 Ultra and need syncing over Tailscale, or the inventory was aspirational. Flagging, not fixing — not my files (§0). [E] 2026-07-16 — landed tools/blender/build_yard_assets.py: ONE deterministic script → 16 GLBs + grass atlas. Run `blender -b -P tools/blender/build_yard_assets.py` (flags: --only / --no-verify / --no-debris). Proven, not asserted: 17/17 output files byte-identical across two runs; every GLB re-imported from disk and checked for dims-in-range, tri budget, and node-name survival; contact_sheet.png renders each against the 1.7 m ref capsule. Heaviest asset is garden_bed at 2,580 tris — all far under the 15 k budget. Machine-readable manifest: tools/blender/asset_report.json. [E] 2026-07-16 — contract, trees: `trunk` (trunk+branches, rigid) and `canopy_01..03` are SEPARATE nodes — Lane A, sway the canopies only. `branch_anchor_01..03` are empties carrying anchor_type="tree" + rating_hint (thicker limb = higher) for world.anchors / Lane B. [E] 2026-07-16 — contract, house_yardside: `fascia_anchor_01..03` empties carry rating_hint=0.35 and collateral="gutter", and the gutter node carries collateral_of="fascia" — DESIGN.md's "the fascia board is a lie" wired as data, so ripping it can take the gutter with it. Facade only (9.20 × 1.05 × 2.90 m), no interior. [E] 2026-07-16 — contract, sail_post: exported VERTICAL with `rake_pivot` at the footing and `top_anchor` at the head. DESIGN.md says rake the post away from the load — that's a player decision, so rake is a runtime rotation about rake_pivot, NOT baked geometry. Post is 4.0 m. Also: shed_table has `pickup_anchor`, ladder_01 has `ladder_base`/`ladder_top`, gate has `hinge_axis`. [E] 2026-07-16 — contract, hardware: shackle/carabiner/turnbuckle each keep their failure part as its own node — `pin` (unscrews then shears), `gate` (flutters open), `body` (thread strips) — with failure_mode stamped as a custom prop, so a break anim moves just that piece. They read at 1 m; see contact sheet. [E] 2026-07-16 — contract, garden_bed: all 3 damage states ship in ONE glb as sibling nodes `plants_full` / `plants_tattered` / `plants_dead` (full visible, rest hide_render). Lane A: toggle .visible, don't reload — instant swap, no pop-in. Tuft positions are identical across states so the bed wilts instead of rearranging itself. [E] 2026-07-16 — debris in web/world/models/debris/, copied verbatim from the library and scale-checked: BlueCrate_v2 0.36×0.36×0.29, BlackTub_v2 + WhiteTub_v2 0.36×0.54×0.20, WoodenBin_v2 0.35×0.36×0.31 m — all plausible real-world sizes. Plus tramp_01_v1.glb (2.96×2.96×0.78, mass_hint 45) because every Australian storm produces one airborne trampoline. NOTE: §0's *_v1.glb rule beats §5-E's "tramp_01.glb" spelling — Lane C, glob the dir rather than hardcoding names. [E] 2026-07-16 — grass is a texture, not geometry (§5-E item 9): web/world/models/textures/grass_atlas.png, 512², 2×2 tufts, alpha. Lane A instances billboards from it. [E] 2026-07-16 — ⚠️ FOR LANE A + LANE C, bounding boxes: `THREE.Box3.setFromObject(obj)` expands each mesh's LOCAL box by the world matrix, so a node carrying a rotation reports an inflated box — and that's what three uses for frustum culling. Blender's obj.bound_box has the identical trap (it cost me an hour of chasing phantom "failures"). Fixed at source: join_group() now bakes rotation into the vertices, so every node's local box is axis-aligned and tight. Before the fix three reported tramp_01 as 3.29×1.27 m vs its true 2.96×0.78. You can use the default Box3 safely now — but if you ever measure geometry yourself, measure VERTICES, not bound_box corners. [E] 2026-07-16 — landed tools/assetcheck/ (README + assets_in_three.html): loads every GLB through three.js r175 GLTFLoader and asserts Y-up, node survival, and that branch_anchor_01 lands at a sane +Y world position. This exists because the Blender round-trip CANNOT catch an axis bug — it exports Z-up→Y-up and imports Y-up→Z-up, so a broken export_yup flips back and passes. Currently green: 16/16 + anchors, `branch_anchor_01` at (-0.96, 3.64, -1.46). Lane A: once server.py + vendor/ land, this wants to live next to selftest.html — happy for you to fold it in, it reads expectations from asset_report.json so it stays in sync by itself. [E] 2026-07-16 — added a root .gitignore (thumbs/, *.blend, *.fbx per §0). Unowned file — Lane A, fold it into yours if you'd rather own it. [E] 2026-07-16 — open q for Lane A: does house_yardside at 9.20 m wide sit right on the north edge of a 30×20 m yard, or do you want a wider/second variant? Cheap to re-cut — it's one function in the script.