{"slug":"quadrille-showroom","total":175,"limit":100,"offset":0,"since":null,"commits":[{"hash":"17535f5","date":"2026-07-03 08:08:13 -0700","author":"Steve","subject":"chore: macstudio3 migration — reconcile from mac2 + repoint paths (stevestudio2→macstudio3, node/npm/npx→/opt/homebrew)","body":""},{"hash":"e7f3b0f","date":"2026-07-02 11:47:44 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-02T11:47:40 (2 files) — public/js/rack.js public/js/showroom.js","body":""},{"hash":"a156052","date":"2026-07-02 11:22:53 -0700","author":"Steve","subject":"5x sweep 1: keep bottom-corner panels clear of the centre pager (fix #g-next occlusion)","body":""},{"hash":"b657af0","date":"2026-07-02 10:47:24 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-02T10:47:21 (3 files) — package-lock.json public/js/dock.js public/showroom.html","body":""},{"hash":"bf8db8a","date":"2026-07-02 10:31:53 -0700","author":"Steve","subject":"chore: v1.3.2 (session close — furniture style selector + art direction)","body":""},{"hash":"02b4e0a","date":"2026-07-02 10:16:27 -0700","author":"Steve","subject":"furniture FIX 2: Transitional now unmistakably distinct from Contemporary","body":"Chair upholstery moved OFF cream/greige to a deep sage-olive (mattMat env-0 so the\n~1.6x showroom light-lift can't wash it back to cream) so the CHAIR — the dominant\nvisual mass — reads clearly green vs Contemporary's white seat. Table wood darkened\nto a matte mid-oak (env-0) so it reads as solid wood, not pale blonde. Also gates the\nresizeRoom re-dress: a resize only re-dresses if the user had actively chosen a room\ntype, preserving the FIX-1 clean-empty-room boot invariant.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"6bdf2b5","date":"2026-07-02 10:16:18 -0700","author":"Steve","subject":"furniture FIX 1: onload = clean empty room + table+chair only (no room-type dressing on boot)","body":"Boot no longer builds a fully-dressed 'living' room type (sofa/lamps/wingchair/\nrug/sculpture/plant). Room-type dressing now appears ONLY when the user clicks a\nroom-type chip; a returning user also boots to the clean empty room. Verified via\nprobe-boot-scene: currentRoomType null, no roomType group, 1 furnitureRoot (table+chair).\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"0a7d7ab","date":"2026-07-02 09:53:18 -0700","author":"Steve","subject":"furniture: art-direction fixes — concrete-grey Brutalist, warm-wood Transitional, visible walnut Traditional","body":"- Brutalist recolored to raw béton-brut concrete grey (dark bases that read ~#8a8a86 on\n  camera under the bright showroom light) + board-form groove lines; no longer white foam\n- Contemporary table now crisp white lacquer + steel blade legs (was warm oak)\n- Transitional table warm honey-oak + soft rounded barrel-back upholstered chair, so it\n  reads clearly distinct from Contemporary (material + silhouette)\n- Traditional chair reworked: matte no-env walnut frame + tall curved upholstered back +\n  crown rail so the walnut tone and curved back read from the default camera\n- Added mattMat() + buildBoardFormLines() helpers\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"646f4b9","date":"2026-07-02 09:47:02 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-02T09:46:55 (1 files) — public/js/showroom.js","body":""},{"hash":"20c04ec","date":"2026-07-02 09:37:01 -0700","author":"Steve","subject":"chore: v1.3.1 (session close — thumb/proxy upstream hardening + ContrarianGo)","body":"server.js hardening (retry + concurrency cap + placeholder fail-safe + shared\nfetchUpstreamBuffered for both /api/thumb and /api/proxy/image) already passed\n/5x (5 sweeps, clean twice cold) + /contrarian (cleared the bar) this session.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"97b809a","date":"2026-07-02 09:28:37 -0700","author":"Steve","subject":"ContrarianGo iter1: shared fetchUpstreamBuffered for BOTH thumb + proxy; split transient/permanent; warn-log","body":"Closes the 3 contrarian holes in the 5x sliver-thumb fix:\n- /api/proxy/image (open hero) now shares the SAME cap+retry hardening as /api/thumb\n  via one fetchUpstreamBuffered() helper — no more fixing only the tested endpoint.\n- fallback distinguishes transient (blip) from PERMANENT (dead 4xx) and console.warns\n  every fallback so a dead upstream leaves a trail a canary can grep — not a silent gray lie.\n- both routes de-duplicated onto the shared helper (the twin functions collapsed).\nVerified: healthy hero serves real 124KB image (no fallback header); permanent 404 →\nplaceholder 200 + logged PERMANENT; clickthrough CLEAN twice cold; verify-all 0 failures.\nAdds /ContrarianGo skill (this loop).\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"b6f8daf","date":"2026-07-02 08:51:30 -0700","author":"Steve","subject":"5x sweeps 2-5: upstream concurrency cap + placeholder fail-safe for sliver thumbs","body":"Cold-cache concurrent sliver-thumb bursts tripped transient cdn.shopify.com 502s\nsurfacing as browser console errors. Fix in /api/thumb url= path:\n- upstream-concurrency cap (6) so a ~24-thumb cold burst doesn't hammer the CDN\n- retries 1->3 w/ exponential backoff (transient only, never a real 4xx)\n- neutral-placeholder fail-safe: serve quiet 200 (X-Thumb-Fallback) on exhaustion\n  instead of a console-erroring 502 — decorative slivers must fail-safe.\nVerified: 0 dead URLs across all 883 catalog images; healthy images still serve the\nreal thumb; clickthrough CLEAN twice on cold cache; verify-all all green.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"aec52fe","date":"2026-07-02 08:46:38 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-02T08:46:35 (30 files) — data/thumb-cache/099160b17f710e570c5aa9ac.jpg data/thumb-cache/0b9c68a67796dd1ccce90a5a.jpg data/thumb-cache/2ba7e03368ff27763d2ef6a1.jpg data/thumb-cache/3f441bb0f30a6e1d2e049c99.jpg data/thumb-cache/44fbcf466a13d44c920bf876.jpg","body":""},{"hash":"d9cf3b2","date":"2026-07-02 08:34:34 -0700","author":"Steve","subject":"5x sweep 1: retry transient upstream in /api/thumb (cold-cache burst 502s)","body":"Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"3ac09a0","date":"2026-07-02 08:16:17 -0700","author":"Steve","subject":"chore: lint, refactor, v1.3.0 (session close)","body":"- hoist crypto require to module top (drop 2 inline requires)\n- age handler == null -> === null (strict-equality consistency)\n- version 1.2.2 -> 1.3.0 (sample-request submit feature landed this session)\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"fb869a9","date":"2026-07-02 08:03:37 -0700","author":"Steve","subject":"Wire the George email picker for sample requests (sending gated)","body":"- scripts/send-sample-request-emails.js: reads data/sample-request-emails.jsonl,\n  sends each pending row via George (POST /api/send, Basic auth from GEORGE_BASIC_AUTH\n  or dw-agents Keychain, optional X-Send-Approval), from steve-office. Marks rows\n  sent/sent_at/message_id; idempotent. --dry-run previews; --health probes George.\n- npm run send-sample-emails.\n- Safe by default: refuses to send unless SAMPLE_REQUEST_EMAIL_TO is set; NOT scheduled.\nVerified: George reachable from Mac2 (status ready), health + dry-run + gated-refusal\nall pass without sending; queued row stays sent:false.\nStill gated: recipient inbox (SAMPLE_REQUEST_EMAIL_TO) + SAMPLE_REQUEST_EMAIL=on + consent copy.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"38c4172","date":"2026-07-02 07:54:28 -0700","author":"Steve","subject":"Build sample-request submit: endpoint + tray form + admin cards (steps 1-3)","body":"- POST /api/sample-request: validated (samples 1..50, age 0..120, email, 32kb cap),\n  server-stamped created_at, append-only data/sample-requests.jsonl persist.\n- Email via George gated behind SAMPLE_REQUEST_EMAIL env (default off) -> queue file only.\n- Sample Tray: name/email/phone/notes fields + Request Samples button (disabled when\n  empty) + consent line + confirmation UX that keeps input on failure. Age rides along.\n- Admin /admin/sample-requests: card list with created date+time chip (standing rule).\nVerified: curl (ok+400s+GET), Playwright load (7 elements, button gated, 0 console errors).\nGated/pending: consent wording + email recipient (spec steps 4-5).\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"cc9d428","date":"2026-07-02 07:44:28 -0700","author":"Steve","subject":"Scope the sample-request submit endpoint (age rides along)","body":"Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"d6a840c","date":"2026-07-01 11:12:29 -0700","author":"Steve","subject":"chore: refactor + v1.2.2 (session close)","body":"javascript-pro refactor pass on the session's age-field code found + fixed a real\nedge case in initHUD(): restoring a stored age of 0 from localStorage coerced to null\n(parseInt('0')||null → null) while the input still showed '0'. Now uses\nparseInt(saved,10) + Number.isFinite guard, mirroring the live input handler. Re-verified\n(Playwright): 67 persists across reload, and the 0-case now restores as 0, 0 console errors.\nNo linter configured in repo — lint gate had no toolchain to run."},{"hash":"df1dc51","date":"2026-07-01 11:12:04 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-01T11:11:52 (2 files) — public/js/showroom.js scripts/_reverify.mjs","body":""},{"hash":"59eede3","date":"2026-07-01 10:53:43 -0700","author":"Steve","subject":"Capture visitor age as a plain data point on the Sample Tray (replaces Age View)","body":"Per Steve's call: age is no longer an in-showroom control — it's a plain field on the\nsample-request surface. Added an optional 'Your age' number input to the Sample Tray\n(#tray-age), persisted to localStorage('qh_visitor_age'), clamped 0–120, exposed via\nwindow._qh.visitorAge so a future request submit can include it. Styled to match the tray.\n\nAlso fixed a PRE-EXISTING bug this surfaced: the dock stows #sample-tray on boot with\n.qh-stowed (display:none!important), which the tray's own .visible (display:block) could\nnever override — so the Sample Tray never actually appeared even when samples were added.\nupdateSampleTray() now clears qh-stowed when populated (re-adds it when empty).\n\nVerified (Playwright): field renders + is visible when the tray populates; value persists\nacross reload; _qh.visitorAge reflects it; clamps 999→120; blank clears the key to null;\n0 console errors.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"88e4cd8","date":"2026-07-01 10:47:09 -0700","author":"Steve","subject":"chore: v1.2.0 → v1.2.1 (session close — /5x fixes + verification suite)","body":"Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"e4d7eca","date":"2026-07-01 10:40:11 -0700","author":"Steve","subject":"Remove Age View / age-adaptive theming entirely (per Steve's call)","body":"Age becomes a plain signup data point (signup flow to be scoped separately) — not an\nin-showroom control. Ripped out the whole apparatus:\n • deleted public/js/ageview.js + its <script> tag\n • deleted all 6 scripts/verify-ageview-*.mjs + AGE-VIEWS-SPEC.md\n • removed the _qh Age View control surface (setAvatarAge/coalesceAvatarAge/avatarAge/\n   ageRigParams export/upperRigState/setEnvForAge/resetEnv) from showroom.js\n • dropped the '#btn-ageview' dock entry + clickthrough selector; removed the\n   Age-View-ON pass from verify-walls-on-next; dropped the two ageview npm scripts\n\nKEPT byte-for-byte: the baseline age-28 avatar. buildAvatar()/ageRigParams() are the\nnormal showroom body's own material/dimension helpers (isBaseline fast-paths) and predate\nAge View — nothing can now drive a non-28 age or age-tint the env.\n\nVerified: fresh Playwright smoke — _ageview undefined, no levers, no #btn-ageview,\navatar present (6 children), 50 wings, 0 console errors; verify-walls-on-next still PASS.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"e0bed2f","date":"2026-07-01 10:36:58 -0700","author":"Steve","subject":"verify: At-the-Table behavioral test (nook hide/restore + seated set + sample-add) + verify-all runner","body":"scripts/verify-table-view.mjs (npm run verify-table-view) drives window._viewmode.set('table'),\nasserts the consultation nook hides (only centre table shows), the table+chair set and seated avatar\nappear, addSample() grows the basket, then exits and asserts the nook is restored — 9/9 pass, 0 console\nerrors. Covers the newest committed feature (842a1dc). Added npm run verify-all to chain the 4 headless verifiers.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"4840661","date":"2026-07-01 10:34:59 -0700","author":"Steve","subject":"5x follow-up: WebGL scene-correctness + FPS verifier (closes the biggest coverage gap)","body":"scripts/verify-scene-render.mjs (npm run verify-scene-render) asserts the Three.js scene actually\nrenders the wallcoverings — not just 'canvas drew something': 50 wing boards in the arc, 639 meshes\n/227 textured (maps loaded), centre book present + book-matched (L mirrored/R normal), renderer\ndraw calls>0, frame loop alive. FPS measured avg 72 headless Chrome (>=55 spec target; real hw higher),\n0 console errors. Closes the contrarian's #3 (24KB screenshot proves nothing) + the Age View Phase-5.1 FPS gate.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"504a0ed","date":"2026-07-01 10:11:46 -0700","author":"Steve","subject":"5x sweep 3 (contrarian-driven): persist sort+density to localStorage (standing rule) + repeatable reload test","body":"/contrarian gate caught a real bug the six-way suite can't: sort (currentSort) and density (windowSize)\nnever persisted to localStorage though the standing sort+density rule requires it — every other slider did.\nFixed: init from qh_sort/qh_density, write on change, reflect into the select/slider on boot. Proven with\nscripts/verify-sort-density-persist.mjs (npm run verify-sort-density-persist) — sets sort=newest+density=30,\nRELOADS, asserts both localStorage + restored control values survive. ✅ passes.\nReport updated with an honest coverage boundary (WebGL scene correctness / FPS / mobile NOT covered here).\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"f043317","date":"2026-07-01 10:11:25 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-01T10:11:20 (2 files) — public/js/showroom.js scripts/verify-sort-density-persist.mjs","body":""},{"hash":"da29667","date":"2026-07-01 10:08:07 -0700","author":"Steve","subject":"5x: prove sort/density reachable+functional via ☰→Controls (selectOption OK, density OK, 0 errors) — closes the false-positive gap","body":"Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"682e04f","date":"2026-07-01 10:07:13 -0700","author":"Steve","subject":"5x: REPORT.md — six-way GREEN after sweep 2 (6/6, 0 console errors, cross-browser); dup-id fixed; sort-in-dock false-positive documented + design question surfaced","body":"Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"604475e","date":"2026-07-01 10:02:34 -0700","author":"Steve","subject":"5x sweep 1: fix duplicate id=density-wrap (7x → class); document sort/density-in-dock false-positives","body":"Six-way verify: 5/6 + 0 console errors. The 2 clickthrough/E2E failures are false positives —\n#bottom-bar and #window-bar(sort/density) are intentionally stowed into the hamburger dock at boot\n(dock.js progressive-disclosure UX), verified reachable via ☰→Controls with zero console errors.\nReal defect fixed: 7 duplicate density-wrap ids → .density-wrap class + CSS selector update.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"bb091f8","date":"2026-07-01 10:01:10 -0700","author":"Steve","subject":"Age View Phase-5.5 (contrarian pass): coalesce avatar rebuild, tighten in-page gate, capture screenshots","body":"Contrarian final gate found three real holes; fixed the two code ones:\n 1. setAvatarAge() rebuilt the whole primitive avatar on EVERY slider input tick →\n    dozens of full teardown/rebuilds per second mid-drag (the real FPS-gate failure\n    mechanism). Now coalesced to ≤1 rebuild/animation-frame via coalesceAvatarAge();\n    buildAvatar() stays synchronous + byte-for-byte (boot + OFF-reset call it directly).\n 2. In-page seniorGate() used coarse type-size steps and never checked HEAD size — a\n    softer bar than the spec, so its 'all-pass' would've been green even before the\n    band type-size fix. Retightened to the interpolated 65→85 bar incl. heads, matching\n    verify-ageview-gate.mjs. Still all-pass (the bands genuinely meet the stricter bar).\n 3. Cross-engine harness now captures a screenshot per engine/age (was counters-only).\n\nRe-ran both verifiers post-edit (validation-loop): static gate ALL 10 PASS; cross-engine\nPASS on Chromium/Firefox/WebKit, 0 console errors, tightened seniorGate all-pass.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"d4295bd","date":"2026-07-01 09:56:47 -0700","author":"Steve","subject":"Age View Phase-5.3: cross-engine render gate (Chromium/Firefox/WebKit)","body":"Playwright harness drives the live showroom, turns Age View on, sets the REAL\n#av-slider to ages 12/45/85, and asserts per engine: 0 console errors, in-page\nseniorGate() all-pass (APCA + type/target on 65/75/85 + slider85), and China Seas\nwing wall intact. RESULT: PASS on all 3 engines, 0 console errors anywhere, readouts\ncorrect (12→Tween, 45→Connoisseur, 85→Legacy).\n\nFPS is reported but advisory only — headless uses software GL (swiftshader/llvmpipe)\nso it under-represents real-hardware FPS; the 5.1 FPS gate still needs a real-browser\nrun on-device. npm run verify-ageview-crossengine (exit 1 on hard fail).\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"1ea5d2b","date":"2026-07-01 09:40:04 -0700","author":"Steve","subject":"Age View Phase-5 gate: fix under-spec type sizes on Senior/Elder bands + add re-runnable verifier","body":"Ran the spec's HARD senior-usability gate (APCA + target-size + type-size) measured\nagainst the real BANDS tokens. APCA contrast passed everywhere with wide margins, but\nthe two mid-senior bands were short on type size once the 65→85 requirement is\ninterpolated to the oldest age each band serves:\n  • Senior (≤70): body 20→22, head 30→34\n  • Elder  (≤80): body 22→26, head 32→42\nRe-ran the gate post-edit (validation-loop): all 10 bands PASS. Added\nscripts/verify-ageview-gate.mjs (npm run verify-ageview-gate, exit 1 on fail).\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"e034910","date":"2026-07-01 09:11:01 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-01T09:10:58 (1 files) — public/js/dock.js","body":""},{"hash":"842a1dc","date":"2026-07-01 07:24:52 -0700","author":"Steve Abrams","subject":"showroom polish (070126A): wrap consultation nook in a group + hide it in the At-the-Table view (only the centre table shows), restore on exit","body":""},{"hash":"90fe8f0","date":"2026-07-01 07:14:23 -0700","author":"Steve Abrams","subject":"showroom: boot = first-person user standing in the doorway at the entrance end, stopped (no auto-walk); uniform same-size control pills + small pills on the bottom bar","body":""},{"hash":"8dfe072","date":"2026-07-01 07:02:47 -0700","author":"Steve Abrams","subject":"showroom P3: At-the-Table view — seated avatar + click-pattern-to-center + add/drag/remove samples on the table (basket persisted); fix addSample null-deref + table recorder","body":""},{"hash":"0fb3c6b","date":"2026-06-30 21:43:34 -0700","author":"Steve Abrams","subject":"auto-save: 2026-06-30T21:43:31 (2 files) — public/js/showroom.js public/js/viewmodes.js","body":""},{"hash":"5388cf1","date":"2026-06-30 21:29:25 -0700","author":"Steve Abrams","subject":"showroom P2: 5 room types (bedroom/office/living/lobby/gallery) + hybrid furniture (2 real GLB heroes + primitives) + flooring/room pickers + adjustable room; null empty model slots (no 404s)","body":""},{"hash":"02a9c21","date":"2026-06-30 20:43:16 -0700","author":"Steve Abrams","subject":"auto-save: 2026-06-30T20:43:13 (9 files) — public/js/showroom.js public/js/viewmodes.js public/showroom.html public/js/GLTFLoader.js public/models/","body":""},{"hash":"bc32b4a","date":"2026-06-30 19:38:07 -0700","author":"Steve Abrams","subject":"showroom P1: remove angled/orbit/topdown, boot=walk-through-center, macro=2.5ft, carousel=continuous parade, compare=user-choosable two designs","body":""},{"hash":"2b90e02","date":"2026-06-30 19:33:41 -0700","author":"Steve","subject":"spec: 5 room types for showroom room-preview","body":""},{"hash":"6c84fb0","date":"2026-06-30 19:32:41 -0700","author":"Steve Abrams","subject":"add boot-walk + carousel screen-recording harnesses","body":""},{"hash":"c66a587","date":"2026-06-30 18:47:13 -0700","author":"Steve Abrams","subject":"fix Wing° control: VIEW_OPEN was set by the slider but never read — now rakes the focused open board live (+re-ease nudge)","body":""},{"hash":"4bb3eba","date":"2026-06-30 13:46:53 -0700","author":"Steve","subject":"chore: lint, refactor, v1.2.0 (session close)","body":"- refactor: reset _dragTurned in onCanvasPointerDown discard path (prevents a stale flag swallowing the next click after toggling explore off)\n- lint: log unfocusWing failures instead of silently swallowing\n- node --check clean; nav re-verified green in headed Chrome (wheel/C-key/Center button, 0 errors)"},{"hash":"de7d9e4","date":"2026-06-30 13:18:57 -0700","author":"Steve","subject":"Avatar nav: comfortable centred boot framing + grounded scroll/arrow walk, drag-turn, and always-available Return to Center","body":"- Boot: pull eye back to z=0.7 + BOOT_PITCH -0.12 so the open book frames with headroom (was 'way too close')\n- Single-ownership nav: disable OrbitControls rotate/pan/zoom in both modes (killed the dolly-too-close / aerial-tilt / behind-furniture vectors)\n- Scroll wheel + arrows walk; drag or A/D turns (vertical drag ignored = always grounded); eye height never changes = never aerial\n- Grounded walkable box clamp excludes the closet/rack/nook/olive keep-outs (no walking behind the closet)\n- Add ⊕ Center button + C/Home key + recenterView() glide back to middle, facing forward\n- All-ages hint copy refreshed across HUD + guided instruction"},{"hash":"b81c393","date":"2026-06-30 04:35:43 -0700","author":"Steve","subject":"chore: vN minor bump (session close — Age View bands, walls-on-Next, dock fixes)","body":""},{"hash":"66c765f","date":"2026-06-29 16:41:46 -0700","author":"Steve","subject":"Loop ledger iter5: two clean clickthrough sweeps + visual confirmation of bar-legibility fix","body":""},{"hash":"67caf4d","date":"2026-06-29 16:39:30 -0700","author":"Steve","subject":"Age View bar legibility: fix invisible readout text on light-panel bands + add per-band bar-legibility gate","body":""},{"hash":"796c25f","date":"2026-06-29 16:31:00 -0700","author":"Steve","subject":"walls-on-next: add Age-View-ON regression pass (paging under senior band still clads all 4 walls)","body":""},{"hash":"71784da","date":"2026-06-29 16:26:47 -0700","author":"Steve","subject":"Add measured env-morph verifier: asserts real rendered wall-luminance delta (35 vs 85), not just intensity scalars","body":""},{"hash":"08a8479","date":"2026-06-29 16:23:15 -0700","author":"Steve","subject":"Age View FPS gate: measure steady-state by discarding morph-rebuild transient window (floor unchanged)","body":""},{"hash":"e40c08a","date":"2026-06-29 16:07:38 -0700","author":"Steve","subject":"Feature: Next/Prev book-page renders the new design on all 4 walls","body":"In BOOK_MODE (the default landing) gotoBoardOffset→pageCenterBook flipped the open book\nbut never clad the room. Added cladWalls(imageUrl) in setCenterBook — the single funnel for\nbook paging + load — so every Next/Prev renders the NEW design onto all 4 sides\n(back/left/right/front), gated on wallsAutoClad so 'Walls: neutral' still wins. Initial\nlanding stays neutral (clean cream walls) by design; the design appears on first Next.\n\nVerified: scripts/verify-walls-on-next.mjs — next×3 + prev all clad all 4 sides\n(SKU advances 703346→703446→703546), 0 console errors, screenshot confirms.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"c378473","date":"2026-06-29 16:03:05 -0700","author":"Steve","subject":"Age View band-differentiation: split young/adult/established into 3 hue families","body":"Contrarian residual #4: adult(28)/established(40) were ~90% identical (both blue-grey,\nviolet CTA, gap 12). Now three distinct identities on palette + CTA hue + density:\n - young(21) Emerging Adult: electric VIOLET #7c3aed, purple-dark, gap 10 (energetic)\n - adult(28) Young Pro: dashboard BLUE #1d4ed8 (Lc 87), cool blue-grey, gap 12 (modern)\n - established(40) Established: deep TEAL #155e75 (Lc 90), teal-slate, gap 9 (dense/assured)\nAll CTAs white-text + contrast-verified. Also drop #guided-bar from clickthrough PANELS\n(Steve made it a permanent fixture, not a dock panel).\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"c4fd7f2","date":"2026-06-29 15:53:45 -0700","author":"Steve Abrams","subject":"dock polish: stow pager on boot (P1), cascade first-open (P2), auto-close menu on launch (P3), Controls first-open height (P5)","body":""},{"hash":"95724fe","date":"2026-06-29 15:44:59 -0700","author":"Steve","subject":"Arrow keys (left/right) page prev/next pattern on the wings; body-turn moves to A/D","body":""},{"hash":"2bef6b4","date":"2026-06-29 15:44:09 -0700","author":"Steve","subject":"/5x REPORT: 2 dock.js bugs fixed, 2 clean sweeps","body":""},{"hash":"b3ca21a","date":"2026-06-29 15:42:47 -0700","author":"Steve","subject":"/5x: showroom-aware clickthrough + fix 2 real dock.js bugs it caught","body":"The generic /3x clickthrough can't open the dock hamburger, so it false-failed on\nintentionally-stowed controls. Built scripts/clickthrough-showroom.mjs that drives the\napp like a user (open hamburger → launch each panel → exercise sort/sliders/nav → proxy\nevery tool). It caught 2 GENUINE defects the blind clickthrough never could:\n\n1. dock.js quote-in-selector: TOOLS entries '#top-bar [data-section=\"wings\"]' / '...overview'\n   were interpolated raw into data-tool=\"...\" (inner quotes close the attr) and into\n   reflectMenu's querySelector('[data-tool=\"...\"]') (throws 'not a valid selector'). Wings +\n   Overview were unreachable from the menu and reflectMenu threw 14× PAGEERROR. Fix: key tools\n   by index (data-tool-idx), resolve TOOLS[idx].sel in JS.\n\n2. dock.js launched panels showed EMPTY: launch() removed qh-stowed from the wrapper but not\n   from the panel element, so every launched panel's CONTENT stayed display:none!important —\n   only the header bar showed (sort select measured 0×0, which is why selectOption timed out).\n   Fix: el.classList.remove('qh-stowed') on launch. Panels now render their real content\n   (verified by screenshot: Versions list + Controls sort/sliders visible).\n\nSweep 4 clean: sort all 5 options, all ranges, all nav, all 10 tools, 0 console errors.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"52d16fc","date":"2026-06-29 15:37:22 -0700","author":"steve@designerwallcoverings.com","subject":"snapshot: clickthrough script + 5x report from review pass","body":""},{"hash":"d598030","date":"2026-06-29 15:36:40 -0700","author":"Steve","subject":"Next/Previous step one pattern across all 883 + keep pager visible while walking","body":""},{"hash":"3b94ca1","date":"2026-06-29 15:30:40 -0700","author":"steve@designerwallcoverings.com","subject":"Hamburger dock: stow all panels on load + launch each as movable modal; hide first-person avatar in framed views","body":""},{"hash":"a785d8a","date":"2026-06-29 15:28:45 -0700","author":"Steve","subject":"Center book starts open at 20 degrees (was 65)","body":""},{"hash":"80267e9","date":"2026-06-29 15:26:37 -0700","author":"Steve","subject":"Center book: open leaves FORWARD at 65° + wire Open° slider to live splay","body":""},{"hash":"6145090","date":"2026-06-29 15:24:18 -0700","author":"Steve Abrams","subject":"auto-save: 2026-06-29T15:24:16 (3 files) — public/js/showroom.js public/js/dock.js public/js/modal-rig.js","body":""},{"hash":"f1439ea","date":"2026-06-29 15:16:21 -0700","author":"Steve","subject":"Fix center-book leaf orientation: leaves face the viewer (drop the PI group flip)","body":"The PI group rotation pointed each leaf's textured +Z front AWAY from the spawn and put\nthe right leaf on the -x side. Removed it -- the plane's +Z front already faces the +Z\nviewer -- and splayed each leaf's OUTER edge back so the book opens as a gentle V toward\nthe avatar. Verified: both leaves normalZ ~+0.95, R worldX +0.36 / repeat.x +1, L worldX\n-0.36 / repeat.x -1 (true horizontal mirror)."},{"hash":"fa8ee90","date":"2026-06-29 15:14:24 -0700","author":"Steve","subject":"Default view = walk the avatar + single centered mirrored book + minimal caption bar","body":"- Walking is now the default: exploreMode ON + persisted; arrows = up/down walk along\n  facing dir, left/right turn the body. Avatar rig + camera move together, clamped to room.\n- Spawn dead-centre, stationary, facing the wing -- no boot auto-fly (enterFixedCentre /\n  enterGuidedDefault are no-ops in book mode).\n- SKU display is now a quiet fixed #wing-caption strip (Pattern . Colorway . SKU . Vendor),\n  never pops/animates; the old wing-detail popup card is suppressed in book mode.\n- The view is ONE open sample book centred on -Z with two leaves hinged at the spine;\n  left leaf is a horizontal MIRROR of the right (repeat.x flipped). The 50-board arc is\n  hidden. [ ] / N-P page the catalog.\n- Consultation table+chairs nudged so the furnished 20x20 room reads in the scene."},{"hash":"04948ab","date":"2026-06-29 14:50:46 -0700","author":"Steve","subject":"Phase 5.5 /contrarian final pass: prove the AVATAR half + fix 2 weak non-senior CTAs","body":"Contrarian (correctly) flagged the Phase-5 gate verified only the HUD half and proved\nregression on a phantom hook. Closes both:\n\n- scripts/verify-ageview-avatar.mjs — proves the avatar morph on the REAL _qh hooks\n  (avatarAge getter + upperRigState): eye-height arc 1.40→1.62→1.61→1.58→1.48m,\n  stoop monotonic rotX 0@28 → -0.070@65 → -0.209@85, regression OFF==age28 rotX=0,\n  + look-down body screenshots at 12/28/45/65/85 (setSpin to PITCH_MIN). 0 errors.\n- ageview.js BANDS: teen CTA #ff2fd6/near-black (APCA 47) -> #c01a9e/white (Lc 81);\n  young CTA #a855f7/near-black (APCA 38, a real miss for the 21-yo band) -> #7c3aed/white\n  (Lc 83). Same neon/violet identity, legible filled buttons.\n\nValidation-loop re-run after edits: FPS floor 67 all 11 bands, senior 65/75/85 PASS,\nregression PASS, cross-engine PASS, avatar PASS, 0 console errors.\n\nResidual (honest, non-blocking): env-morph per-band is implemented + FPS-safe but\nasserted by intensity values, not a measured rendered-luminance delta; adult(28)/\nestablished(40) bands still ~90% identical (plan-gate fallback: merge or differentiate).\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"71a067a","date":"2026-06-29 14:44:00 -0700","author":"Steve","subject":"Phase 5.3 cross-engine verifier: Age View slider PASS on WebKit + Firefox","body":"scripts/verify-ageview-3x.mjs drives the 12/45/85 anchors in Safari(WebKit) and\nGecko(Firefox): scene built (50 boards), bands snap (tween/connoisseur/legacy),\npostMessage bridge intact, 0 console errors in both. Chrome/Blink covered by the\nreal-GPU phase5 verifier. All 3 engines green.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"d84af5b","date":"2026-06-29 14:42:03 -0700","author":"Steve","subject":"Phase 5 senior-usability HARD gate: measured verifier + 3 cascade/wiring fixes","body":"Add scripts/verify-ageview-phase5.mjs — measures RENDERED composited hex (not\ndeclared tokens) over the live WebGL scene on real-GPU Chrome, the contrarian\nfix #1 requirement. It caught 3 real gaps the in-code seniorGate() self-test\npasses:\n 1. CTA dead: #g-tour is a .g-btn, so '#guided-bar .g-btn' (color:--av-fg)\n    out-specified the '#g-tour' cta rule -> CTA text rendered in --av-fg over\n    --av-cta (APCA 9/9/0). Fix: name both ids to win the cascade (Lc now 99/99/101).\n 2. Body type-size vars set but never applied to #guided-instruction (stayed 18px).\n    Fix: wire --av-body/--av-head onto the real text elements (now 20/22/28 + 30/32/44).\n 3. #btn-ageview (36px) + #av-slider track (18px) under the senior target floor.\n    Fix: grow to >=48px (65/75) / >=56px (85).\n\nMeasured verdict: FPS>=67 all 11 bands, senior 65/75/85 PASS, regression OFF==age28,\n0 console errors.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"fc2441f","date":"2026-06-29 07:19:28 -0700","author":"Steve Abrams","subject":"Age View: continuous avatar age-morph (ageRigParams + upperRig stoop sub-group) + default-off age slider with banded UI/env snap + senior-opaque APCA gate","body":""},{"hash":"3048f97","date":"2026-06-29 07:10:30 -0700","author":"Steve","subject":"AGE-VIEWS-SPEC: apply /contrarian plan-gate fixes (opaque HUD backing for senior APCA-over-WebGL, gate the slider at 85, upperRig sub-group for stoop, differentiate bands 21/28, per-band env FPS profiling)","body":""},{"hash":"bdcbe78","date":"2026-06-29 07:01:17 -0700","author":"Steve","subject":"Add AGE-VIEWS-SPEC.md — overseer plan for 10 age-adaptive avatar showroom views (12-85)","body":""},{"hash":"f94d966","date":"2026-06-29 06:37:05 -0700","author":"Steve","subject":"High-end versions batch 3: V18 Infinity Mirror + V19 Conservatory Garden + V20 Penthouse Dusk — ALL 10 high-end versions (V12-V21) now built + registered in the picker","body":""},{"hash":"c4480af","date":"2026-06-29 06:23:09 -0700","author":"Steve","subject":"High-end versions batch 2: V13 Atrium Daylight + V14 Velvet Lounge + V15 Brutalist Concrete + V17 Japanese Tatami (protos + registered in picker; 7/10 high-end versions live)","body":""},{"hash":"607b67f","date":"2026-06-29 06:19:13 -0700","author":"Steve Abrams","subject":"auto-save: 2026-06-29T06:19:10 (4 files) — public/proto/v13-atrium-daylight.html public/proto/v14-velvet-lounge.html public/proto/v15-brutalist-concrete.html public/proto/v17-tatami.html","body":""},{"hash":"fad82ea","date":"2026-06-29 06:08:32 -0700","author":"Steve","subject":"Register V12 Gallery Noir + V16 Art-Deco Salon + V21 Garcia Room in the version picker (overlay entries + VERSION_ORDER + element lists for the code system)","body":""},{"hash":"fbe4548","date":"2026-06-29 06:07:12 -0700","author":"Steve","subject":"High-end versions: add V12 Gallery Noir + V16 Art-Deco Brass/Marble + V21 Garcia Room proto scenes (PBR/PMREM/ACES, real China Seas textures, 0 errors)","body":""},{"hash":"1c4a04e","date":"2026-06-28 23:43:35 -0700","author":"Steve","subject":"Avatar polish: lighten 1st-person sweater 0x2e3138->0x3d424d (look-down readability, /contrarian note)","body":""},{"hash":"09cf8ad","date":"2026-06-28 23:26:30 -0700","author":"Steve Abrams","subject":"Chunk K (first-person Steve avatar, yaw-only rig) + Chunk A′ (20'×20' room)","body":"K: first-person body built from THREE primitives (capsule limbs + boxes, $0, no\nGLTF) — torso/shoulders/two arms+hands/legs/feet, charcoal sweater + indigo jeans +\ndark shoes, NO head mesh (camera is the eyes). Parented to a YAW-ONLY rig\n(avatarRig.rotation.y = spinYaw, position pinned under lockedSpinEye) so the body\nturns with yaw but NEVER tilts with pitch — look down (PITCH_MIN deepened -0.62→-1.05,\n~60°) and you see a stationary torso/lap/hands/legs/feet on the floor (BE Steve);\nstraight-ahead the body sits below the sight line so the wing bank reads clean.\nupdateAvatarRig() runs every frame after the spin pose; camera.position untouched.\n\nA′: CONFIG.room 4.27→6.10 (20'×20'); arc re-fit arcRadius 2.2→3.2, arcCenter.z\n0.4→0.5 so the 50-board bank still fans with ~2\" slivers at a comfortable ~8.9'\ndistance; consultation nook relocated proportionally (1.25,-0.55)→(1.78,-0.78), same\n+66° yaw inside the ±72° clamp. buildRoom/walls/baseboards/crown scale off CONFIG.\n\nQH API: avatarPresent / avatarState() / setSpin() verification surface.\nProof bar (real-GPU Chrome, scripts/shot-chunkK-avatar.mjs): posDelta yaw 0, rigPitch\n0 across all poses, 50 boards, clad 4 walls on click, errorCount 0.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"33391a6","date":"2026-06-28 22:50:01 -0700","author":"Steve","subject":"Boot raycaster race fix: queue pre-ready canvas clicks + replay on ready (durable)","body":"The canvas click listener is wired at init() but wingBoards stays empty until the\nasync loadProducts()->buildWingWall() chain finishes (~2-3s). A click in that window\nused to silently no-op (empty raycaster targets) — to a 65-year-old that reads as\n'it's broken.' Now:\n\n- wingWallReady flips true the instant the full 50-board bank is wired into the\n  raycaster (end of buildWingWall).\n- onCanvasClick, when not ready, QUEUES the click coords (latest wins) and shows a\n  gentle 'Loading the collection — your selection is queued…' cue via the existing\n  loader status — never a dead click.\n- drainBootClick() replays the queued click as the FINAL boot action (+2600ms, after\n  the version engine's boot-time V1 apply), through the exact onCanvasClick path, so\n  the board the user pointed at is selected + conveyored-to-front + 4-wall clad +\n  detail shown. Their click WINS, durably.\n- enterGuidedDefault / enterFixedCentre yield to a consumed/pending boot click during\n  the boot window (_bootClickConsumed / _bootFixedCentreYield) so the resting-middle\n  re-asserts and the versions.js V1 build cannot stomp the user's selection.\n\nNo Slice-1/2/3 regression: slice1 PASS (resting-middle, posDelta 0, clamp ±72°, 50\nboards, errorCount 0), slice2 PASS (boot no-clad, select allFourClad, posDelta 0).\nProof: scripts/verify-bootrace.mjs — pre-ready click queued+cue shown, replay selects\nboard durably (allFourClad, detail visible), normal click no-regression, errorCount 0.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"1303c94","date":"2026-06-28 21:18:29 -0700","author":"Steve","subject":"Finalize definitive build: remove dev 30s auto-reload scaffolding from showroom.html (contrarian whole-build sign-off polish)","body":""},{"hash":"18497fa","date":"2026-06-28 21:17:00 -0700","author":"Steve Abrams","subject":"auto-save: 2026-06-28T21:16:50 (125 files) — cta/rec/chrome.mp4 cta/shots/chrome/all-designs-after.png cta/shots/chrome/all-designs-before.png cta/shots/chrome/auto-tour-after.png cta/shots/chrome/auto-tour-before.png","body":""},{"hash":"229a7b0","date":"2026-06-28 20:39:00 -0700","author":"Steve","subject":"Slice3 gate hygiene: annotate headless FPS flag as HEADLESS-SKIPPED + commit real-GPU artifact (scripts/slice3-fps-realgpu-result.json — M2 Max Metal 72fps, gate55_met:true) — one authoritative truth source per /contrarian","body":""},{"hash":"0979b2b","date":"2026-06-28 19:10:43 -0700","author":"steve","subject":"Slice-3 F: FPS.ms.draws HUD readout + off-thread wall-clad decode (kills the select-moment dip)","body":"MEASURE: added window._perf {fps,ms,draws,pixelRatio} + 'N FPS . M ms . D draws'\nreadout to the bottom HUD (measurable on demand; read by the proof harness).\nReal-GPU (Apple M2 Max ANGLE/Metal via system Chrome) nav sweep BEFORE fix held\n72 FPS everywhere EXCEPT one 51-FPS select-moment dip (4-wall clad upload + hero\nflip on the same frame). FIX (low-risk): cladWalls() now decodes the wall image\nOFF-thread via createImageBitmap (was a main-thread new Image()), mirroring the\nhero path; applyWallImage uses CanvasTexture for ImageBitmap sources (correct\nflipY — walls verified right-side-up). AFTER: nav sweep pins at 72 FPS / 13.9ms\non EVERY sample incl. the select frame — gate >=55 met, errorCount 0.\n\nHeadless SwiftShader can't measure real FPS so the gate is verified on real GPU;\nthe headless harness gates the structural H + Slice 1/2 invariants."},{"hash":"1b5eb93","date":"2026-06-28 18:46:17 -0700","author":"Steve Abrams","subject":"Slice-3 H: globally-unique element build codes (1000QH<version>)","body":"assignElementCodes() runs once at registry load — iterates VERSION_ORDER, each\nversion's elements by ascending n, handing out base-1000 global seq across all 11\nbuilds; stamps elt.code (e.g. seq 0 + QH + V2 -> 1000QHV2). Deterministic + stable.\nBUILD_CODE='QH' isolated to one swappable constant. Pin identity, Chosen tray, and\nCopy-as-text now key on the durable global code, not the version-local 'V1.4' key\n(which collided across versions). Pins show the code chip + friendly badge n.\nqh_chosen localStorage migrates old key-only rows (backfills code, never crashes)."},{"hash":"c4a14bb","date":"2026-06-28 18:18:58 -0700","author":"steve","subject":"Slice-2 contrarian fixes: HOLE-3 detail starts COLLAPSED on every fresh selection (drop localStorage override footgun) + spec-legal proof harness (real ArrowL/R yaw to +/-72 clamp, NO mode switch). HOLE-1/HOLE-2 verified already-correct: hero opens flip=0.999 / 6.02x neighbour width; clad walls visible front (252 cells) + side at clamp (680/643 cells); posDelta 0; 16/16 assertions pass, errorCount 0.","body":""},{"hash":"34871bc","date":"2026-06-28 18:16:10 -0700","author":"Steve Abrams","subject":"auto-save: 2026-06-28T18:16:07 (2 files) — public/js/showroom.js scripts/diag-probe.mjs","body":""},{"hash":"0e9e564","date":"2026-06-28 18:04:13 -0700","author":"steve@designerwallcoverings.com","subject":"Open-face clean: hero board pushes forward + re-centres so the open 30x6 face reads full-bleed edge-to-edge with NO neighbour slat-bars/frames crossing the pattern","body":"Root cause: an open hero stayed in the rail plane at the same depth as its raked\nneighbours, so their tilted sliver faces + dark frame bars projected IN FRONT of\nthe open face (the gray/colored vertical bars crossing the pattern). The left-hinge\nface-offset also pushed the open face halfWidth to the right of dead-front.\n\nFix (rack.js flipUpdate): the opening hero now (1) slides forward along the inward\nnormal toward the room centre (OPEN_FORWARD_M=0.42m, flip-scaled) so it sits clearly\nin front of every raked neighbour — normal depth-testing then occludes them, nothing\ncrosses the pattern; (2) shifts left by faceHalf*flip so the 30in face is centred\ndead-front (same pattern left & right of the open middle). placeBoard records the rail\nbase pos + inward normal + faceHalf; relayout re-reads the clean base each carousel frame.\n\nProof (verify-openface.mjs occlusion raycast): 25/25 grid points over the open face\nhit the hero FIRST, 0 offenders, real texture full-bleed, face-centre world-X ~0,\nerrorCount 0. Carousel/clad/fixed-centre invariants intact (verify-carousel green,\nnow measuring the face centre not the left-hinge pivot).\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"f0eacf1","date":"2026-06-28 17:46:01 -0700","author":"Steve Abrams","subject":"auto-save: 2026-06-28T17:45:58 (1 files) — SHOWROOM-REFERENCE-FROM-VIDEOS.md","body":""},{"hash":"6b2e201","date":"2026-06-28 17:40:19 -0700","author":"Steve Abrams","subject":"Slice 2 (D+E) verified: click=selection (board opens flat, detail card collapsed-by-default, expandable to 27x27 China Seas specs) + every selection clads ALL 4 walls (front wall now in roomWalls[], wallsAutoClad default ON, clad unconditional on focus, 27in repeat). Boot stays Slice-1 clean: no selection/panel, neutral walls (cladToken race-guard + _qhBootSettled boot-guard prevents transient Hero-focus leak). 4/4 walls assert PASS, posDelta 0, yaw clamp +/-72 intact, errorCount 0. V1-V11 + V11 seated overlay untouched.","body":"Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"15572c3","date":"2026-06-28 17:36:23 -0700","author":"Steve","subject":"Add Playwright carousel verifier + npm verify-carousel script (Chunk I proof harness)","body":""},{"hash":"396a02d","date":"2026-06-28 17:34:36 -0700","author":"Steve","subject":"Chunk I: dry-cleaner/tie-rack carousel — click any sliver conveyor-shifts the whole rack so the clicked board eases to the dead-front centre slot, then fans open (camera stays fixed-centre)","body":""},{"hash":"0ae67ed","date":"2026-06-28 17:27:30 -0700","author":"Steve","subject":"snapshot before carousel build: Slice-2 boot-guard (_qhBootSettled) + cladToken + verify-slice2 artifacts","body":""},{"hash":"966628f","date":"2026-06-28 17:15:48 -0700","author":"Steve Abrams","subject":"auto-save: 2026-06-28T17:15:46 (1 files) — public/js/showroom.js","body":""},{"hash":"13c193b","date":"2026-06-28 17:07:03 -0700","author":"Steve Abrams","subject":"snapshot before Slice 2: cladToken boot-race guard (in-flight onload invalidation)","body":""},{"hash":"04ca167","date":"2026-06-28 17:04:12 -0700","author":"Steve","subject":"/5x sweep-1 report: Slice-1 build verified clean (HTTP/render/3-browser/0-errors/real-controls); clickthrough failures = harness vocabulary mismatch, not app defects","body":""},{"hash":"2c946ef","date":"2026-06-28 16:32:00 -0700","author":"Steve Abrams","subject":"Slice1 fix-it: 72° yaw clamp (non-empty), 50 boards, ~36° resting-open middle","body":"FIX-1 YAW_CLAMP 105°→72° + nook pulled forward (yaw +66°, within clamp) so\nboards + side wall stay in frame at both clamps; never an empty void.\nFIX-2 wing.totalCount 20→50 so the real data path fans all 50 slivers.\nFIX-3 OPEN_ANGLE_DEG 45→37 → resting-open middle sits ~36° off dead-on\n(restFlip 0.51), foreshortened ~24\" — unmistakably angled, not flat,\nno detail panel/selection. verify-slice1 tightened to gate non-empty clamp.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"37affeb","date":"2026-06-28 16:20:08 -0700","author":"Steve","subject":"Slice 1: open-angle slider (qh_open_angle, 45° default, 20-90°, live re-rake) + middle board opens at measured 45° on boot; bump density max to 50","body":""},{"hash":"c38484d","date":"2026-06-28 16:15:34 -0700","author":"Steve Abrams","subject":"auto-save: 2026-06-28T16:15:32 (1 files) — LIQUID-UI-SPEC.md","body":""},{"hash":"1fbedd2","date":"2026-06-28 16:09:54 -0700","author":"Steve","subject":"Slice 1 (A+B+C): 14x14 room, fixed-centre yaw-spin camera (clamped), 50 wings w/ ~2in slivers + middle open-at-angle on boot","body":""}]}