test(04-04): the BA7/BA8 drift gates + the search-tree GEO3 pin + the destination-preload capture leg

- BA7: the shipped JS must carry EVERY committed gazetteer id (the
  web half of bot/web id-set parity, T-04-39) — RED against the
  pre-gazetteer bundle, green after the resolver lands in the build
- BA8: the shipped JS carries NO deep-link crypto (JS assets only —
  the official Telegram SDK is external, D-11/T-04-40)
- no-storage: explicit web/src/search memory-only + no-crypto pin
- capture.mjs: the ARAHARAH-DEST signal race + the 'dest' wait mode
  (state=resolved asserted, then the rendered card's text is waited
  on — the card mounts at live, T-04-42) + the shell-dest-preload
  leg; 8/8 expected captures
This commit is contained in:
araharah-executor
2026-10-24 01:38:24 +08:00
parent c2d78b0c8f
commit 747eeb7b34
3 changed files with 106 additions and 6 deletions
+39 -5
View File
@@ -26,6 +26,7 @@
* shell-live-dark.png LIVE state, Telegram dark theme (PX-02)
* shell-denied.png denied card + ASK VIA CHAT (GEO-02 entry point)
* shell-chat-pin.png LIVE via the chat-pin relay (?pin=1, stubbed /api/location)
* shell-dest-preload.png the Mini App booted WITH a preloaded destination (?dest=<id>)
* gallery-compare.png DSGN-02 side-by-side vs navigate_map_overview/screen.png
*
* Also runs the GEO-03 runtime probe in-page after boot-to-live and fails
@@ -162,7 +163,7 @@ log.push(`serving web/dist-harness/ → ${base}/`);
const browser = await chromium.launch();
const SIGNALS = ['RAMS-BASEMAP-READY', 'ARAHARAH-LIVE'];
const SIGNALS = ['RAMS-BASEMAP-READY', 'ARAHARAH-LIVE', 'ARAHARAH-DEST'];
/*
* One capture leg = one context + one screenshot. The Phase-1 READY-race
@@ -186,7 +187,7 @@ async function captureLeg({ name, query, colorScheme, grantGeo, waitMode }) {
await context.setGeolocation({ latitude: 3.1513, longitude: 101.6958, accuracy: 12 });
}
const page = await context.newPage();
const seen = { 'RAMS-BASEMAP-READY': false, 'ARAHARAH-LIVE': false, live: 'no' };
const seen = { 'RAMS-BASEMAP-READY': false, 'ARAHARAH-LIVE': false, 'ARAHARAH-DEST': false, live: 'no' };
const consoleLines = [];
page.on('console', (m) => {
for (const s of SIGNALS) if (m.text().startsWith(s)) seen[s] = true;
@@ -194,7 +195,7 @@ async function captureLeg({ name, query, colorScheme, grantGeo, waitMode }) {
if (consoleLines.length > 20) consoleLines.shift();
});
const signalDiagnostics = () =>
`${seen['RAMS-BASEMAP-READY'] ? 'RAMS-BASEMAP-READY yes' : 'RAMS-BASEMAP-READY no'}; ${seen['ARAHARAH-LIVE'] ? 'ARAHARAH-LIVE yes' : 'ARAHARAH-LIVE no'}; last console: ${JSON.stringify(consoleLines.slice(-5))}`;
`${seen['RAMS-BASEMAP-READY'] ? 'RAMS-BASEMAP-READY yes' : 'RAMS-BASEMAP-READY no'}; ${seen['ARAHARAH-LIVE'] ? 'ARAHARAH-LIVE yes' : 'ARAHARAH-LIVE no'}; ${seen['ARAHARAH-DEST'] ? 'ARAHARAH-DEST yes' : 'ARAHARAH-DEST no'}; last console: ${JSON.stringify(consoleLines.slice(-5))}`;
try {
await page.goto(`${base}/?${query}`, { waitUntil: 'domcontentloaded', timeout: 60000 });
@@ -216,6 +217,33 @@ async function captureLeg({ name, query, colorScheme, grantGeo, waitMode }) {
throw new Error(`theme slot mismatch: expected ${colorScheme}, documentElement[data-tg-theme]=${String(themed)}`);
}
await page.waitForTimeout(2000); // tile render settle (plan: first render + 2s)
} else if (waitMode === 'dest') {
const signal = await Promise.race([
page.waitForEvent('console', {
predicate: (m) => m.text().startsWith('ARAHARAH-DEST'),
timeout: SIGNAL_TIMEOUT_MS,
}).then(() => 'signal'),
new Promise((r) => setTimeout(() => r('timeout'), SIGNAL_TIMEOUT_MS)),
]);
if (signal === 'timeout') {
throw new Error(`ARAHARAH-DEST race timed out after ${SIGNAL_TIMEOUT_MS}ms — ${signalDiagnostics()}`);
}
/* The destination must actually be the RESOLVED one — a signal that fired
* with state=absent means the payload never reached the resolver (T-04-42).
* The signal is a STATE TOKEN (no id — D-15 hygiene), so the leg then
* proves WHICH destination resolved via the rendered card's text. The card
* mounts when the chain reaches live (the boot stages replace the stack
* transiently), so the DOM check waits rather than sampling once. */
const destLine = consoleLines.filter((l) => l.startsWith('ARAHARAH-DEST')).pop() ?? '';
if (!destLine.includes('state=resolved')) {
throw new Error(`destination did not resolve: "${destLine}" — expected state=resolved`);
}
await page.waitForFunction(
() => (document.body.textContent ?? '').includes('Suria KLCC'),
undefined,
{ timeout: 15000, polling: 250 },
);
await page.waitForTimeout(500);
} else if (waitMode === 'denied') {
// Wait on the denied strip lamp specifically — the awaiting-pin variant of
// the card also carries a CTA during LINK 3's 60s poll, so the CARD alone
@@ -268,6 +296,11 @@ try {
* a pin; the chain reaches LIVE offline — the automated Desktop-leg counterpart. */
await captureLeg({ name: 'shell-chat-pin.png', query: 'mock=tdesktop&pin=1', colorScheme: 'light', grantGeo: false, waitMode: 'live' });
/* 7 — ONBD-02: the destination-preload boot. ?dest=<id> carries the plain
* gazetteer id in the URL's own query string (the inline-button carrier —
* RESEARCH Pitfall 1). The ARAHARAH-DEST signal resolves the race. */
await captureLeg({ name: 'shell-dest-preload.png', query: 'mock=android&dest=suria-klcc', colorScheme: 'light', grantGeo: true, waitMode: 'dest' });
/* GEO-03 runtime probe (its own live session) — GEO-03's "spot-checkable at
* phase exit" criterion made mechanical. Counts and key names only (T-03-24). */
{
@@ -316,7 +349,7 @@ try {
}
}
/* 7 — DSGN-02 fidelity artifact: the live-light capture beside the committed
/* 8 — DSGN-02 fidelity artifact: the live-light capture beside the committed
* navigate_map_overview mockup, each column labeled with its source path. */
{
const target = join(SHOTS, 'gallery-compare.png');
@@ -365,6 +398,7 @@ const expected = [
'shell-live-dark.png',
'shell-denied.png',
'shell-chat-pin.png',
'shell-dest-preload.png',
'gallery-compare.png',
];
const missing = expected.filter((f) => !existsSync(join(SHOTS, f)));
@@ -378,5 +412,5 @@ if (missing.length || undersized.length || failures.length) {
process.exit(1);
}
console.log(`CAPTURE OK — 7/7 non-blank captures under web/screenshots/`);
console.log(`CAPTURE OK — 8/8 non-blank captures under web/screenshots/`);
process.exit(0);