mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-12 06:06:37 +03:00
Surfaced during hands-on testing against a real Next 16 + Turbopack app (EACManagement). All four compound to produce unusable live iteration for React users; fixed bottom-up because each one blocked testing the next. ## 1. Picker bar snaps to (0,0) on first variant arrival In startVariantObserver, `showVariantInDOM(sessionId, 1)` hides the original via display:none but we never re-pointed selectedElement. Next frame, getBoundingClientRect() on the hidden original returns a zero rect and the bar positions at (0,0). Clicking Next masked the bug because cycleVariant already calls updateSelectedElement. Fix: after showVariantInDOM, re-point selectedElement via pickVariantContent(wrapper, visibleVariant) — same call the no-HMR fallback and updateSelectedElement already use. ## 2. React NotFoundError on accept/discard (Next 16 / Turbopack) handleAccept and cleanup both called `wrapper.parentElement.replaceChild(...)` eagerly, before the agent's source rewrite had propagated through HMR. That yanks children out from under React's reconciler; when React later tries to remove/replace the wrapper, its fiber tree no longer matches the DOM and it throws. Fix, both paths: - cleanup (discard): `wrapper.style.display = 'none'` so variants disappear immediately, no structural DOM mutation. - handleAccept: skip the eager replaceChild entirely. The accepted variant is already the only visible child of the wrapper thanks to the display: contents pattern; HMR cleans up the wrapper itself. - Both paths schedule a 2s fallback replaceChild that runs only if HMR hasn't cleaned up — keeps static-server / no-HMR flows working. - Capture sessionId + visibleVariant in closure variables before the 1800ms cleanup timer zeros them, so the fallback still has context. ## 3. Server serves stale live.js forever loadBrowserScripts() read live-browser.js once at startup into a liveScript string. The /live.js handler served that cached string with no cache headers. Every edit to the browser script was invisible until a full server restart — silently broke the iteration loop on fixes #1 and #2 for the user. Fix: - loadBrowserScripts returns { detectScript, livePath } — existence check only, no caching. - /live.js handler re-reads livePath on every request and prepends __IMPECCABLE_TOKEN__ / __IMPECCABLE_PORT__ each time. - Response headers: Cache-Control: no-store, no-cache, must-revalidate, max-age=0 + Pragma: no-cache. detect.js stays cached — it rarely changes during a session. ## 4. Picker stuck in GENERATING when HMR doesn't fire The only 'done' fallback fired when arrivedVariants === 0 and called injectVariantsFromSource, which parses raw source via DOMParser. That can't work for TSX/JSX/Vue/Svelte — JSX expressions aren't valid HTML. If HMR flaked or was slow (500+ line inserts on Next 16 are prone to this), state stayed in GENERATING and the spinner ran forever. Fix: give HMR a 2s grace window, then `window.location.reload()`. resumeSession already counts variants off the rendered DOM on load and transitions straight to CYCLING — reload is the universal recovery path that works for any framework, HTML, static server, anything. injectVariantsFromSource is now dead code on the 'done' path. Kept for potential pure-HTML-no-HMR future use. ## Credit Precise repro + root-cause diagnosis from the other agent in the EACManagement session. #2 and #4 are the high-impact ones for Next 16 / Turbopack; #3 is the meta-fix that made iterating on #1 and #2 possible at all. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>