[object Object]

← back to Quadrille Showroom

Age View Phase-5.3: cross-engine render gate (Chromium/Firefox/WebKit)

d4295bdde2226b0e2fbda3abba16553631693543 · 2026-07-01 09:56:47 -0700 · Steve

Playwright harness drives the live showroom, turns Age View on, sets the REAL
#av-slider to ages 12/45/85, and asserts per engine: 0 console errors, in-page
seniorGate() all-pass (APCA + type/target on 65/75/85 + slider85), and China Seas
wing wall intact. RESULT: PASS on all 3 engines, 0 console errors anywhere, readouts
correct (12→Tween, 45→Connoisseur, 85→Legacy).

FPS is reported but advisory only — headless uses software GL (swiftshader/llvmpipe)
so it under-represents real-hardware FPS; the 5.1 FPS gate still needs a real-browser
run on-device. npm run verify-ageview-crossengine (exit 1 on hard fail).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Files touched

Diff

commit d4295bdde2226b0e2fbda3abba16553631693543
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Jul 1 09:56:47 2026 -0700

    Age View Phase-5.3: cross-engine render gate (Chromium/Firefox/WebKit)
    
    Playwright harness drives the live showroom, turns Age View on, sets the REAL
    #av-slider to ages 12/45/85, and asserts per engine: 0 console errors, in-page
    seniorGate() all-pass (APCA + type/target on 65/75/85 + slider85), and China Seas
    wing wall intact. RESULT: PASS on all 3 engines, 0 console errors anywhere, readouts
    correct (12→Tween, 45→Connoisseur, 85→Legacy).
    
    FPS is reported but advisory only — headless uses software GL (swiftshader/llvmpipe)
    so it under-represents real-hardware FPS; the 5.1 FPS gate still needs a real-browser
    run on-device. npm run verify-ageview-crossengine (exit 1 on hard fail).
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---
 package.json                           |  3 +-
 scripts/verify-ageview-crossengine.mjs | 52 ++++++++++++++++++++++++++++++++++
 2 files changed, 54 insertions(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 56fe27e..02a9a58 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,8 @@
     "build-data": "node scripts/build-showroom-data.js",
     "gen-assets": "node scripts/gen-assets.js",
     "verify-carousel": "node scripts/verify-carousel.mjs",
-    "verify-ageview-gate": "node scripts/verify-ageview-gate.mjs"
+    "verify-ageview-gate": "node scripts/verify-ageview-gate.mjs",
+    "verify-ageview-crossengine": "node scripts/verify-ageview-crossengine.mjs"
   },
   "license": "ISC",
   "description": "The Quadrille House — 3D Endless Wings Wallcovering Showroom (China Seas)",
diff --git a/scripts/verify-ageview-crossengine.mjs b/scripts/verify-ageview-crossengine.mjs
new file mode 100644
index 0000000..a3b8fd7
--- /dev/null
+++ b/scripts/verify-ageview-crossengine.mjs
@@ -0,0 +1,52 @@
+// verify-ageview-crossengine.mjs — Phase-5.3 cross-engine render gate for the Age View.
+// Drives Chromium / Firefox / WebKit against the live showroom, turns Age View on,
+// sets the REAL #av-slider to ages 12/45/85, and asserts per engine:
+//   • 0 console errors / pageerrors
+//   • window._ageview.seniorGate() all-pass (APCA + type/target on 65/75/85 + slider85)
+//   • China Seas wing wall intact (window._wingBoards.length > 0)
+// FPS (window._perf.fps) is REPORTED per engine/age but advisory — headless WebGL uses
+// software GL (swiftshader/llvmpipe), so it under-represents real-hardware FPS.
+import pw from '/Users/stevestudio2/.npm-global/lib/node_modules/playwright/index.js';
+const { chromium, firefox, webkit } = pw;
+const URL='http://127.0.0.1:7690/', CRED={username:'admin',password:'DWSecure2024!'};
+const AGES=[12,45,85];
+const engines=[['chromium',chromium],['firefox',firefox],['webkit',webkit]];
+let hardFails=0;
+for(const [name,launcher] of engines){
+  let browser;
+  try{ browser=await launcher.launch(); }
+  catch(e){ console.log(`\n### ${name}: SKIP (launch failed: ${e.message.split('\n')[0]})`); continue; }
+  const ctx=await browser.newContext({httpCredentials:CRED, viewport:{width:1440,height:900}});
+  const page=await ctx.newPage();
+  const errs=[];
+  page.on('console',m=>{ if(m.type()==='error') errs.push(m.text().slice(0,160)); });
+  page.on('pageerror',e=>errs.push('PAGEERROR '+String(e).slice(0,160)));
+  console.log(`\n### ${name}`);
+  try{
+    await page.goto(URL,{waitUntil:'domcontentloaded',timeout:30000});
+    await page.waitForFunction(()=>window._ageview && window._wingBoards && window._wingBoards.length>0,{timeout:60000});
+    await page.evaluate(()=>window._ageview.setOn(true));
+    for(const age of AGES){
+      await page.evaluate(a=>{ const s=document.getElementById('av-slider'); s.value=a; s.dispatchEvent(new Event('input',{bubbles:true})); }, age);
+      await page.waitForTimeout(1300); // let _perf + textures settle
+      const snap=await page.evaluate(()=>({
+        shownAge:document.getElementById('av-age')?.textContent,
+        band:document.getElementById('av-band')?.textContent,
+        fps:(window._perf&&window._perf.fps)||null,
+        wings:(window._wingBoards&&window._wingBoards.length)||0,
+        bodyClass:document.body.className
+      }));
+      console.log(`  age ${String(age).padStart(2)} → readout ${snap.shownAge} · band "${snap.band}" · wings ${snap.wings} · fps ${snap.fps}`);
+      if(snap.wings<1){ console.log(`    ✗ wing wall empty at age ${age}`); hardFails++; }
+    }
+    const gate=await page.evaluate(()=>window._ageview.seniorGate());
+    const gPass=[65,75,85].every(a=>gate[a].pass) && gate.slider85.pass;
+    console.log(`  seniorGate(): ${gPass?'✅ all pass':'❌ FAIL'} — 65:${gate[65].pass} 75:${gate[75].pass} 85:${gate[85].pass} slider85:${gate.slider85.pass}`);
+    if(!gPass) hardFails++;
+    if(errs.length){ console.log(`  ❌ ${errs.length} console error(s):`); errs.slice(0,6).forEach(e=>console.log('     · '+e)); hardFails++; }
+    else console.log('  ✅ 0 console errors');
+  }catch(e){ console.log(`  ❌ ${name} threw: ${String(e).split('\n')[0]}`); hardFails++; }
+  finally{ await browser.close(); }
+}
+console.log('\n'+(hardFails?`❌ cross-engine gate: ${hardFails} hard failure(s)`:'✅ cross-engine gate PASS (console-clean + seniorGate + wings intact, all engines)'));
+process.exit(hardFails?1:0);

← 1ea5d2b Age View Phase-5 gate: fix under-spec type sizes on Senior/E  ·  back to Quadrille Showroom  ·  Age View Phase-5.5 (contrarian pass): coalesce avatar rebuil bb091f8 →