[object Object]

← back to Agent Cabinet

proof(live-ux-canary): gate marker against the bundle hash computed at startup, not a literal

56486bdc53a09bdf2fdc59a7d84c4a814d7b5515 · 2026-09-23 13:47:31 -0700 · Steve

Compute the expected marker the way redeploy-ux-delta.sh does (sha256[:12] of the canonical bundle) so a routine rebuild no longer reds the whole fleet, while a site left on a stale bundle is still caught. Unreadable bundle -> markerCanonical false with a surfaced reason, never a silent pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UzbjT22E2Q7Q64Wy8xMeH5

Files touched

Diff

commit 56486bdc53a09bdf2fdc59a7d84c4a814d7b5515
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Sep 23 13:47:31 2026 -0700

    proof(live-ux-canary): gate marker against the bundle hash computed at startup, not a literal
    
    Compute the expected marker the way redeploy-ux-delta.sh does (sha256[:12] of the canonical bundle) so a routine rebuild no longer reds the whole fleet, while a site left on a stale bundle is still caught. Unreadable bundle -> markerCanonical false with a surfaced reason, never a silent pass.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01UzbjT22E2Q7Q64Wy8xMeH5
---
 front-page-audit/proof/live-ux-canary.mjs | 29 +++++++++++++++++++++++------
 1 file changed, 23 insertions(+), 6 deletions(-)

diff --git a/front-page-audit/proof/live-ux-canary.mjs b/front-page-audit/proof/live-ux-canary.mjs
index 756f917..f99e059 100644
--- a/front-page-audit/proof/live-ux-canary.mjs
+++ b/front-page-audit/proof/live-ux-canary.mjs
@@ -9,14 +9,30 @@ process.env.NODE_PATH = '/Users/macstudio3/.npm-global/lib/node_modules';
 require('module').Module._initPaths();
 const { chromium, webkit } = require('playwright');
 import fs from 'fs';
+import crypto from 'crypto';
 const [,, sitesFile, engineName='chromium', outFile=`live-canary-${new Date().toISOString().replace(/[:.]/g,'-')}.json`] = process.argv;
 const sites = fs.readFileSync(sitesFile,'utf8').split('\n').map(s=>s.trim()).filter(Boolean);
 const BUNDLE_RX = /ux-primitives|UXGrid|ModalRig|Bento|CommandPalette|Toast|Skeleton|EmptyState|copyToClipboard/i;
-// Expected bundle marker. Set UX_MARKER to pin a specific build (mirror the deploy script's $CANON as the
-// single source of truth) when you want exact-match; otherwise accept any well-formed marker so a routine
-// rebuild — which rotates the content hash — doesn't red the whole fleet with a perfectly healthy bundle.
-const EXPECT_MARKER = process.env.UX_MARKER || null;
-const MARKER_RX = /^ux-primitives\.bundle v\d+\.\d+\.\d+#[0-9a-f]{6,}$/;
+// The deploy script (_shared/scripts/redeploy-ux-delta.sh) computes CANON=$(shasum -a 256
+// ux-primitives.bundle.js | cut -c1-12) and injects `ux-primitives.bundle v1.1.1#$CANON`. That hash is
+// content-derived, so any rebuild rotates it — a hardcoded literal reds the whole fleet after a routine
+// rebuild even though every site is correctly updated. Compute the expected marker the SAME way the deployer
+// does, from the canonical bundle, at startup, and gate against it so a site left on a STALE bundle (wrong
+// hash) is still caught. A caller may pin an exact marker via EXPECTED_MARKER (UX_MARKER kept for back-compat).
+// If the bundle can't be read we do NOT silently pass: markerCanonical goes false with a surfaced reason.
+const CANON_BUNDLE = process.env.UX_BUNDLE || '/Users/macstudio3/Projects/_shared/ux-primitives.bundle.js';
+const UX_VERSION = process.env.UX_VERSION || '1.1.1';
+let EXPECT_MARKER = process.env.EXPECTED_MARKER || process.env.UX_MARKER || null;
+let markerReason = null;
+if (!EXPECT_MARKER) {
+  try {
+    const h = crypto.createHash('sha256').update(fs.readFileSync(CANON_BUNDLE)).digest('hex').slice(0,12);
+    EXPECT_MARKER = `ux-primitives.bundle v${UX_VERSION}#${h}`;
+  } catch (e) {
+    markerReason = `bundle unreadable at ${CANON_BUNDLE}: ${String(e.message||e).slice(0,120)}`;
+  }
+}
+console.log(`expected marker: ${EXPECT_MARKER || '(UNRESOLVED — '+markerReason+')'}`);
 const engine = engineName==='webkit'?webkit:chromium;
 const browser = await engine.launch({ headless:true });
 const out=[];
@@ -52,7 +68,8 @@ for (const site of sites){
     rec.bundleErrors=[...consoleErr,...pageErr].filter(x=>BUNDLE_RX.test(x));
     rec.otherErrors=[...consoleErr,...pageErr].filter(x=>!BUNDLE_RX.test(x)).length;
     rec.pageErrorsAll=pageErr.length;
-    rec.criteria={ uxInit: st.ux==='object', markerCanonical: EXPECT_MARKER ? (st.marker===EXPECT_MARKER) : (typeof st.marker==='string' && MARKER_RX.test(st.marker)), gridWired: st.hasGrid,
+    rec.markerExpected=EXPECT_MARKER; if(markerReason) rec.markerReason=markerReason;
+    rec.criteria={ uxInit: st.ux==='object', markerCanonical: EXPECT_MARKER ? (st.marker===EXPECT_MARKER) : false, gridWired: st.hasGrid,
       skeletonSeen: maxSkel>0, skeletonCleared: st.skel===0, productsRender: st.products>0, noEmptyWhenProducts: st.empty===0,
       controls: st.sort && st.density, noBundleErr: rec.bundleErrors.length===0 };
     // skeletonSeen is INFORMATIONAL on live (a fast CDN load can legitimately beat the 50ms sampler); it does not gate pass

← 65c6777 proof(front-page): harden ux-primitives activation gate + de  ·  back to Agent Cabinet  ·  proof(prove-activation): kill every spawned server on any th 5b74e92 →