The partner-embed check becomes something a person can open

Under the Playwright policy only rega runs a browser here, so this
check's result has to come from someone else's eyes. But the partner
page only ever existed INSIDE check-embedded-rtl — two createServer calls
on ephemeral ports — so "open a partner page at a foreign origin" was not
a thing anyone but the gate could do. NEEDS-EYES rows 3, 4 and 6 name a
runnable probe or an exact URL; row 7 was mine, and it named a construct
that lived in my own process.

--emit writes the same pages to disk, with the two-terminal recipe and
what to look at, so the item is reproducible by whoever has the browser.

⚠ THEY GO IN documentation/, NOT _qa-cycle/. I wrote them there first and
then checked: _qa-cycle is gitignored (.gitignore:113), so the files
could never have travelled to the person who needs them — which is the
entire point. documentation/ is tracked and already carries
README-embed-example.md.

⚠ AND THE ?lang=ar FIXTURE CARRIES NO locale IN ITS profile-config —
1100 bytes against 1114. The two channels reach the document-direction
code by different routes and detectLocale() reads the URL FIRST, so a
config that also supplied the locale would let a reviewer believe the URL
had done it when it had not. The first emit produced all three files
BYTE-IDENTICAL, which would have handed someone three pages they could
not tell apart; I only caught it by checking the emitted sizes rather
than trusting that the flag had taken effect.

The CASES array now carries the ?lang= row itself, so this stands alone:
the emit produces every fixture committed beside it. It previously
depended on the library/ commit for that row, which left the repo with a
fixture the tool would not regenerate — worse than either half.

Also: --emit was being consumed as the baseUrl positional ("SKIP —
nothing served at --emit"). Flags are filtered out of it now. That is the
same bug, in the same shape, that check-leaf-english-leaks had three days
ago — worth knowing it recurs in any gate that takes both a positional
and flags.
This commit is contained in:
Doxa Maps 2026-09-25 23:41:17 +00:00
parent 4bb0dbed47
commit b76cf8f74f
5 changed files with 164 additions and 2 deletions

View file

@ -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 `<html>` 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.

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><title>partner</title>
<style>html,body{margin:0}#w{height:900px}</style></head>
<body><h1 id="partner-heading">Partner page</h1><div id="w"></div>
<!-- A REAL PARTNER EMBED SETS THIS. The generated leaf pages bake window.MAP_APP_API_URL, and
without it every data fetch resolves same-origin against the partner and fails, so the wall
renders zero cards and the locale has nothing to translate. The first run of this gate
omitted it and reported "never mounted" — a harness gap, not the RTL defect. -->
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<script src="http://127.0.0.1:47395/dx-maps/my-upg-100-list/my-upg-100-list.js" crossorigin="anonymous"></script>
<script>
var el = document.createElement("my-upg-100-list-map");
el.setAttribute('profile-config', JSON.stringify({ profile: "poster-wall",
assetsBase: "http://127.0.0.1:47395/dx-maps/assets" }));
el.style.cssText = 'display:block;width:100%;height:900px';
document.getElementById('w').appendChild(el);
</script></body></html>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><title>partner</title>
<style>html,body{margin:0}#w{height:900px}</style></head>
<body><h1 id="partner-heading">Partner page</h1><div id="w"></div>
<!-- A REAL PARTNER EMBED SETS THIS. The generated leaf pages bake window.MAP_APP_API_URL, and
without it every data fetch resolves same-origin against the partner and fails, so the wall
renders zero cards and the locale has nothing to translate. The first run of this gate
omitted it and reported "never mounted" — a harness gap, not the RTL defect. -->
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<script src="http://127.0.0.1:47395/dx-maps/my-upg-100-list/my-upg-100-list.js" crossorigin="anonymous"></script>
<script>
var el = document.createElement("my-upg-100-list-map");
el.setAttribute('profile-config', JSON.stringify({ profile: "poster-wall", locale: 'ar',
assetsBase: "http://127.0.0.1:47395/dx-maps/assets" }));
el.style.cssText = 'display:block;width:100%;height:900px';
document.getElementById('w').appendChild(el);
</script></body></html>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><title>partner</title>
<style>html,body{margin:0}#w{height:900px}</style></head>
<body><h1 id="partner-heading">Partner page</h1><div id="w"></div>
<!-- A REAL PARTNER EMBED SETS THIS. The generated leaf pages bake window.MAP_APP_API_URL, and
without it every data fetch resolves same-origin against the partner and fails, so the wall
renders zero cards and the locale has nothing to translate. The first run of this gate
omitted it and reported "never mounted" — a harness gap, not the RTL defect. -->
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<script src="http://127.0.0.1:47395/dx-maps/my-upg-100-list/my-upg-100-list.js" crossorigin="anonymous"></script>
<script>
var el = document.createElement("my-upg-100-list-map");
el.setAttribute('profile-config', JSON.stringify({ profile: "upg-100-mfe", locale: 'ar',
assetsBase: "http://127.0.0.1:47395/dx-maps/assets" }));
el.style.cssText = 'display:block;width:100%;height:900px';
document.getElementById('w').appendChild(el);
</script></body></html>

View file

@ -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 <html dir> 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) => `<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><title>partner</title>
<style>html,body{margin:0}#w{height:900px}</style></head>
@ -56,7 +80,7 @@ const hostHtml = (c) => `<!DOCTYPE html>
<script src="${TREE}/${TREE_NAME}/${c.id}/${c.id}.js" crossorigin="anonymous"><\/script>
<script>
var el = document.createElement(${JSON.stringify(c.tag)});
el.setAttribute('profile-config', JSON.stringify({ profile: ${JSON.stringify(c.profile)}, locale: 'ar',
el.setAttribute('profile-config', JSON.stringify({ profile: ${JSON.stringify(c.profile)}${c.urlLang ? '' : ", locale: 'ar'"},
assetsBase: ${JSON.stringify(TREE + `/${TREE_NAME}/assets`)} }));
el.style.cssText = 'display:block;width:100%;height:900px';
document.getElementById('w').appendChild(el);
@ -88,6 +112,33 @@ const srv = createServer((req, res) => {
})
await new Promise((r) => srv.listen(HOST_PORT, '127.0.0.1', r))
if (EMIT) {
const { writeFileSync, mkdirSync } = await import('node:fs')
// ⚠ documentation/, NOT _qa-cycle/. _qa-cycle is GITIGNORED (.gitignore:113), so pages written
// there cannot travel to whoever has the browser — which is the entire point of emitting them.
// documentation/ is tracked and already carries README-embed-example.md, so a partner-embed
// fixture belongs beside it.
const outDir = resolve(ROOT, 'documentation/needs-eyes')
mkdirSync(outDir, { recursive: true })
for (let i = 0; i < CASES.length; i++) {
const c = CASES[i]
const name = `partner-${c.profile}${c.urlLang ? `-lang-${c.urlLang}` : ''}.html`
writeFileSync(resolve(outDir, name), hostHtml(c))
console.log(` wrote documentation/needs-eyes/${name}`
+ `${c.urlLang ? ` open it WITH ?lang=${c.urlLang}` : ''}`)
}
console.log('\n HOW TO USE (two terminals, and the ORIGINS MUST DIFFER — same-origin hides the bug):')
console.log(` 1) serve the tree WITH CORS on ${TREE_PORT}:`)
console.log(` npx http-server public -p ${TREE_PORT} --cors -s`)
console.log(` 2) serve this folder on ${HOST_PORT}:`)
console.log(` npx http-server documentation/needs-eyes -p ${HOST_PORT} -s`)
console.log(` 3) open http://127.0.0.1:${HOST_PORT}/partner-poster-wall-lang-ar.html?lang=ar`)
console.log('\n WHAT TO LOOK AT: the partner page\'s own heading, "Partner page", must stay')
console.log(' LEFT-aligned and its <html dir> unset — while the plugin below it mirrors RTL.')
console.log(' If the heading jumps to the right, the embed flipped its host and this FAILED.')
process.exit(0)
}
const browser = await chromium.launch({ args: ['--use-gl=swiftshader', '--enable-unsafe-swiftshader'] })
const WALK = `(root)=>{const o=[];const rec=(r)=>{for(const e of r.querySelectorAll('*')){o.push(e);if(e.shadowRoot)rec(e.shadowRoot)}};rec(root);return o}`
const rows = []