diff --git a/documentation/needs-eyes/README.md b/documentation/needs-eyes/README.md new file mode 100644 index 000000000..52bfd54d9 --- /dev/null +++ b/documentation/needs-eyes/README.md @@ -0,0 +1,60 @@ +# needs-eyes — partner-embed fixtures + +Under the Playwright policy (Driver, 2026-09-25) only **rega** runs a browser on the VPS, and the +Driver's laptop is the other pair of eyes. So a check that can only be seen has to be *openable by +someone else* — not a construct that lives inside one agent's gate process. + +These pages are generated, not hand-written: + +``` +node internal/check-embedded-rtl.mjs --emit +``` + +`check-embedded-rtl` builds its partner page on two ephemeral ports inside its own process. That is +fine for a headless assertion and useless for a human: "open a partner page at a foreign origin" +was not a thing anyone but the gate could do. `--emit` writes the same page to disk so the +needs-eyes item is reproducible. + +## What each file is for + +| file | channel under test | open it as | +|---|---|---| +| `partner-poster-wall.html` | `profile-config.locale` — the channel a **host** sets | plain | +| `partner-upg-100-mfe.html` | same, on the composite | plain | +| `partner-poster-wall-lang-ar.html` | `?lang=` — the channel a **reader** follows | **with `?lang=ar`** | + +⚠ **The `?lang=ar` file deliberately carries no `locale` in its `profile-config`** (1100 bytes vs +1114). The two channels reach the document-direction code by different routes, and +`detectLocale()` reads the URL *first*. If the config also supplied the locale, a reader could +believe the URL had done it when it had not — and the defect this fixture exists for only fires on +the URL channel. An earlier version of the emit produced all three files byte-identical, which +would have handed a reviewer three pages they could not tell apart. + +## How to use it + +Two terminals, and **the origins must differ** — same-origin hides the bug: + +``` +# 1) the tree, WITH CORS (a partner embed uses crossorigin="anonymous", so the +# browser demands Access-Control-Allow-Origin; `bun run serve` does not send it, +# a real CDN does) +npx http-server public -p 47395 --cors -s + +# 2) these fixtures +npx http-server documentation/needs-eyes -p 47393 -s + +# 3) open +http://127.0.0.1:47393/partner-poster-wall-lang-ar.html?lang=ar +``` + +## What to look at + +The partner page's own heading — **"Partner page"** — must stay **left**-aligned and the +document's `` must carry no `dir`, while the plugin below it mirrors right-to-left. + +If the heading jumps to the right, the embedded plugin flipped its host's whole page and the check +has **failed**. That was the real defect: an RTL locale was written to +`document.documentElement.dir` whenever it resolved, so a reader following a `?lang=ar` link to a +partner's site got the partner's headings, nav and footer flipped. The fix +(`window.MAP_OWNS_PAGE`, packet/vertical 2a502dd8) is that only a page the generator wrote may +have its direction set; absence of the flag means restraint. diff --git a/documentation/needs-eyes/partner-poster-wall-lang-ar.html b/documentation/needs-eyes/partner-poster-wall-lang-ar.html new file mode 100644 index 000000000..a2373b6ca --- /dev/null +++ b/documentation/needs-eyes/partner-poster-wall-lang-ar.html @@ -0,0 +1,17 @@ + +partner + +

Partner page

+ + + + \ No newline at end of file diff --git a/documentation/needs-eyes/partner-poster-wall.html b/documentation/needs-eyes/partner-poster-wall.html new file mode 100644 index 000000000..4278fc0e9 --- /dev/null +++ b/documentation/needs-eyes/partner-poster-wall.html @@ -0,0 +1,17 @@ + +partner + +

Partner page

+ + + + \ No newline at end of file diff --git a/documentation/needs-eyes/partner-upg-100-mfe.html b/documentation/needs-eyes/partner-upg-100-mfe.html new file mode 100644 index 000000000..cc9366c42 --- /dev/null +++ b/documentation/needs-eyes/partner-upg-100-mfe.html @@ -0,0 +1,17 @@ + +partner + +

Partner page

+ + + + \ No newline at end of file diff --git a/internal/check-embedded-rtl.mjs b/internal/check-embedded-rtl.mjs index 4a3ca56a0..6a3215207 100644 --- a/internal/check-embedded-rtl.mjs +++ b/internal/check-embedded-rtl.mjs @@ -28,8 +28,21 @@ import { servesThisTree } from './_serves-this-tree.mjs' import { TREE_NAME } from './_tree-name.mjs' const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..') -const BASE = process.argv[2] || process.env.BASE_URL || 'http://127.0.0.1:4173' +// ⚠ FILTER FLAGS OUT OF THE POSITIONAL. `--emit` was consumed as the baseUrl and the gate +// reported "SKIP — nothing served at --emit". Exactly the bug I hit on check-leaf-english-leaks +// last week; the same shape, in a different file, three days apart. +const BASE = process.argv.slice(2).find((a) => !a.startsWith('--')) + || process.env.BASE_URL || 'http://127.0.0.1:4173' const HOST_PORT = Number(process.env.RTL_HOST_PORT || 47393) +// ★ --emit WRITES THE PARTNER PAGE TO DISK INSTEAD OF MEASURING IT. +// Under the Playwright policy (Driver, 2026-09-25) only rega runs a browser, so this check's +// result now has to be produced by someone else's eyes. But the partner page only ever existed +// INSIDE this process — two createServer calls on ephemeral ports — so "open a partner page at a +// foreign origin" was not a thing anyone could actually do. NEEDS-EYES rows 3, 4 and 6 name a +// runnable probe or an exact URL; row 7 (mine) named a construct that lived in my own memory. +// --emit closes that: it writes a self-contained page plus a two-line serve recipe, so the item +// is reproducible by whoever has the browser. +const EMIT = process.argv.includes('--emit') let reachable = false try { const r = await fetch(`${BASE}/${TREE_NAME}/manifest.json`, { signal: AbortSignal.timeout(4000) }); reachable = r.ok } catch { /* precondition */ } @@ -38,12 +51,23 @@ const treeCheck = await servesThisTree(BASE, ROOT) if (!treeCheck.ok) { console.error(`EMBEDDED-RTL: NOT MEASURED — ${treeCheck.why}`); process.exit(2) } // A PARTNER PAGE on a foreign origin: its own is LTR and must stay that way. +// ⚠ BOTH CHANNELS. profile-config is how a HOST sets the language; ?lang= on the partner's OWN +// url is how a READER does, and detectLocale() reads the URL FIRST. They reach the +// document-direction code by different routes, so a check that drives only one proves nothing +// about the other — which is why the URL case exists as a row here rather than as a variant of +// the config one. The emit writes a fixture per row, so this array IS the needs-eyes inventory. const CASES = [ { id: 'my-upg-100-list', tag: 'my-upg-100-list-map', profile: 'upg-100-mfe' }, { id: 'my-upg-100-list', tag: 'my-upg-100-list-map', profile: 'poster-wall' }, + { id: 'my-upg-100-list', tag: 'my-upg-100-list-map', profile: 'poster-wall', urlLang: 'ar' }, ] const TREE_PORT = Number(process.env.RTL_TREE_PORT || 47395) const TREE = `http://127.0.0.1:${TREE_PORT}` +// ⚠ THE URL CASE MUST NOT ALSO SET locale IN profile-config. The two channels reach the +// document-dir code by different routes and the whole point of the ?lang= case is to exercise the +// URL one alone. Emitting all three files byte-identical (measured: 1114 bytes each) would hand a +// reader a page that cannot distinguish them — and would let the config channel supply the locale +// while the reader believed the URL had. const hostHtml = (c) => ` partner @@ -56,7 +80,7 @@ const hostHtml = (c) => `