From 5dc1bd392502ff5d7a542311139241ccab82714a Mon Sep 17 00:00:00 2001 From: Eugenio Paluello Date: Tue, 21 Jul 2026 18:38:04 +0200 Subject: [PATCH 1/9] test(playground): UI targets for the headless full-bundle driver --- playground/tests/e2e-headless/targets.ts | 29 ++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 playground/tests/e2e-headless/targets.ts diff --git a/playground/tests/e2e-headless/targets.ts b/playground/tests/e2e-headless/targets.ts new file mode 100644 index 00000000..cda9515b --- /dev/null +++ b/playground/tests/e2e-headless/targets.ts @@ -0,0 +1,29 @@ +// Discovered UI targets for the headless full-bundle spike. +// Values verified against playground/src on this branch — see +// .superpowers/sdd/task-1-report.md for file:line evidence. Update here if +// the playground UI moves; this is the single place UI knowledge lives — +// no other task may hardcode a selector. + +// The Diagnosis screen is deep-linked from the root page via a `view` query +// param (see playground/src/app/page.tsx: VIEW_PARAM / SERVICE_FOR_VIEW / +// selectionFromUrl). Loading this path directly renders DiagnosisView with +// no need to click through the left-rail "Diagnosis" entry first. +export const DIAGNOSIS_PATH = "/?view=diagnosis"; + +// The Diagnosis run does NOT auto-start on mount: `runDiagnosis` is only +// invoked from DiagnosisView's onRun callback, which is wired to this +// button's onClick. There is no effect anywhere that calls it automatically. +export const RUN_ALL_SELECTOR: string | null = '[data-testid="diagnosis-run"]'; + +// Text content is "{passCount} success · {failCount} failed"; the existing +// e2e test parses the failed count from this same element with +// /(\d+)\s+failed\b/. +export const FAILED_COUNT_SELECTOR = '[data-testid="diagnosis-summary"]'; + +// Report body; carries data-report-ready="true" once the run has finished +// rendering its markdown (DiagnosisView.tsx:173-174 — the `
` sibling of
+// diagnosis-copy-report, rendered only when `hasResults && !isRunning`;
+// data-report-ready is `reportMarkdown.length > 0`, which React stringifies
+// to the literal attribute value "true").
+export const REPORT_READY_SELECTOR =
+  '[data-testid="diagnosis-report-markdown"][data-report-ready="true"]';

From 479db1c7de1d16d91d8b61f9dd8478c3ab326663 Mon Sep 17 00:00:00 2001
From: Eugenio Paluello 
Date: Tue, 21 Jul 2026 18:38:37 +0200
Subject: [PATCH 2/9] feat(truapi-host-cli): browserless full-bundle shim
 runner (bun + happy-dom)

---
 rust/crates/truapi-host-cli/js/bun.lock       |  34 +++++
 rust/crates/truapi-host-cli/js/package.json   |   8 +
 .../truapi-host-cli/js/scripts/load-bundle.ts | 143 ++++++++++++++++++
 3 files changed, 185 insertions(+)
 create mode 100644 rust/crates/truapi-host-cli/js/bun.lock
 create mode 100644 rust/crates/truapi-host-cli/js/package.json
 create mode 100644 rust/crates/truapi-host-cli/js/scripts/load-bundle.ts

diff --git a/rust/crates/truapi-host-cli/js/bun.lock b/rust/crates/truapi-host-cli/js/bun.lock
new file mode 100644
index 00000000..1be3f987
--- /dev/null
+++ b/rust/crates/truapi-host-cli/js/bun.lock
@@ -0,0 +1,34 @@
+{
+  "lockfileVersion": 1,
+  "configVersion": 1,
+  "workspaces": {
+    "": {
+      "name": "truapi-host-cli-js",
+      "dependencies": {
+        "@happy-dom/global-registrator": "20.11.0",
+        "happy-dom": "20.11.0",
+      },
+    },
+  },
+  "packages": {
+    "@happy-dom/global-registrator": ["@happy-dom/global-registrator@20.11.0", "", { "dependencies": { "@types/node": ">=20.0.0", "happy-dom": "^20.11.0" } }, "sha512-rHb/2dOy3APuHKNU4aqPQt+HbEt6BkulTTyCks89z5a+EipJecnFFd+3cz3meaFfCOXWsoxplPTLrGjFIEhd0A=="],
+
+    "@types/node": ["@types/node@26.1.1", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw=="],
+
+    "@types/whatwg-mimetype": ["@types/whatwg-mimetype@3.0.2", "", {}, "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA=="],
+
+    "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
+
+    "buffer-image-size": ["buffer-image-size@0.6.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ=="],
+
+    "entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
+
+    "happy-dom": ["happy-dom@20.11.0", "", { "dependencies": { "@types/node": ">=20.0.0", "@types/whatwg-mimetype": "^3.0.2", "@types/ws": "^8.18.1", "buffer-image-size": "^0.6.4", "entities": "^7.0.1", "whatwg-mimetype": "^3.0.0", "ws": "^8.21.0" } }, "sha512-XogN4asPd1a56di9prVG6bZxteNcXsZxxKmAvcEfc5Px5Ca2hMyMgk8wvqK2K1V8zXg40j9VANXsDaJYh9DeNA=="],
+
+    "undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="],
+
+    "whatwg-mimetype": ["whatwg-mimetype@3.0.0", "", {}, "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q=="],
+
+    "ws": ["ws@8.21.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw=="],
+  }
+}
diff --git a/rust/crates/truapi-host-cli/js/package.json b/rust/crates/truapi-host-cli/js/package.json
new file mode 100644
index 00000000..3cb2430f
--- /dev/null
+++ b/rust/crates/truapi-host-cli/js/package.json
@@ -0,0 +1,8 @@
+{
+  "name": "truapi-host-cli-js",
+  "private": true,
+  "dependencies": {
+    "@happy-dom/global-registrator": "20.11.0",
+    "happy-dom": "20.11.0"
+  }
+}
diff --git a/rust/crates/truapi-host-cli/js/scripts/load-bundle.ts b/rust/crates/truapi-host-cli/js/scripts/load-bundle.ts
new file mode 100644
index 00000000..606852dd
--- /dev/null
+++ b/rust/crates/truapi-host-cli/js/scripts/load-bundle.ts
@@ -0,0 +1,143 @@
+// Browserless full-bundle driver: runs the UNCHANGED playground static export
+// under happy-dom on the main realm and bridges its MessagePort to the
+// headless host's WS frame server. One Uint8Array = one binary WS frame.
+// Realm discipline matters: a separate realm breaks the transport's
+// `instanceof Uint8Array` guard and frames drop silently.
+import { GlobalRegistrator } from "@happy-dom/global-registrator";
+import { readFileSync, readdirSync } from "node:fs";
+import { join, resolve } from "node:path";
+import {
+  DIAGNOSIS_PATH,
+  FAILED_COUNT_SELECTOR,
+  REPORT_READY_SELECTOR,
+  RUN_ALL_SELECTOR,
+} from "../../../../../playground/tests/e2e-headless/targets";
+
+const OUT = resolve(
+  process.env.PLAYGROUND_OUT ?? join(import.meta.dir, "../../../../../playground/out"),
+);
+const FRAME_URL = process.env.TRUAPI_FRAME_URL ?? "ws://127.0.0.1:9955";
+const DEADLINE_MS = Number(process.env.FULL_BUNDLE_DEADLINE_MS ?? 1_200_000);
+
+// Serve the export so happy-dom's CSS/font fetches resolve.
+const MIME: Record = {
+  ".html": "text/html", ".js": "text/javascript", ".css": "text/css",
+  ".woff2": "font/woff2", ".txt": "text/plain", ".json": "application/json",
+};
+// Native Response, captured before happy-dom can shadow it: this handler only
+// ever runs post-registration (it serves happy-dom's own asset fetches), where
+// a bare `Response` resolves to happy-dom's class — Bun.serve rejects that and
+// Bun.inspects the object, whose Headers carry the entire Window (~13MB of
+// stdout per run, and the asset request aborts mid-response).
+const NativeResponse = globalThis.Response;
+const server = Bun.serve({
+  port: 0,
+  async fetch(req) {
+    let path = new URL(req.url).pathname;
+    if (path === "/") path = "/index.html";
+    const file = Bun.file(join(OUT, path));
+    if (!(await file.exists())) return new NativeResponse("not found", { status: 404 });
+    const ext = path.slice(path.lastIndexOf("."));
+    return new NativeResponse(file, { headers: { "content-type": MIME[ext] ?? "application/octet-stream" } });
+  },
+});
+
+// Native WebSocket, captured before happy-dom can shadow it.
+const NativeWebSocket = globalThis.WebSocket;
+GlobalRegistrator.register({ url: `http://127.0.0.1:${server.port}${DIAGNOSIS_PATH}` });
+
+// Defensive cap: happy-dom / the bundle can console.error whole objects on
+// early hydration hiccups; render each argument compactly and bounded so no
+// future path can flood stdout. (The ~13MB dump previously seen here was NOT
+// console.error — it was Bun.serve rejecting a shadowed Response, fixed above
+// via NativeResponse.)
+const rawConsoleError = console.error.bind(console);
+console.error = (...args: unknown[]) => {
+  const compact = args.map((a) =>
+    typeof a === "object" && a !== null ? String(a) : a,
+  );
+  rawConsoleError(...compact.map((a) => String(a).slice(0, 2_000)));
+};
+
+// Bridge: product MessagePort <-> host WS frame server.
+const ws = new NativeWebSocket(FRAME_URL);
+ws.binaryType = "arraybuffer";
+const channel = new MessageChannel();
+const pending: Uint8Array[] = [];
+let wsOpen = false;
+ws.addEventListener("open", () => {
+  wsOpen = true;
+  for (const frame of pending.splice(0)) ws.send(frame);
+});
+ws.addEventListener("message", (event: MessageEvent) => {
+  channel.port2.postMessage(new Uint8Array(event.data as ArrayBuffer));
+});
+ws.addEventListener("close", () => console.log("SHIM_STATUS ws closed"));
+ws.addEventListener("error", () => {
+  console.log("SHIM_STATUS ws error");
+  process.exit(2);
+});
+channel.port2.onmessage = (event: MessageEvent) => {
+  const frame = event.data as Uint8Array;
+  if (wsOpen) ws.send(frame);
+  else pending.push(frame);
+};
+channel.port2.start?.();
+
+(window as any).__HOST_WEBVIEW_MARK__ = true;
+(window as any).__HOST_API_PORT__ = channel.port1;
+
+// Execute the shipped bundle in this realm: index.html scripts in document
+// order, then every hashed chunk not already referenced (eager evaluation
+// satisfies dynamic imports with zero network).
+const html = readFileSync(join(OUT, "index.html"), "utf8");
+document.documentElement.innerHTML = html
+  .replace(/^/i, "")
+  .replace(/^]*>|<\/html>$/g, "");
+const indirectEval = eval;
+const executed = new Set();
+for (const script of Array.from(document.querySelectorAll("script"))) {
+  const src = script.getAttribute("src");
+  if (src) {
+    const clean = src.split("?")[0];
+    executed.add(clean.replace(/^\//, ""));
+    indirectEval(readFileSync(join(OUT, clean), "utf8") + `\n//# sourceURL=${clean}`);
+  } else if (script.textContent) {
+    indirectEval(script.textContent);
+  }
+}
+const chunkDir = join(OUT, "_next/static/chunks");
+for (const name of readdirSync(chunkDir).filter((n) => n.endsWith(".js")).sort()) {
+  const rel = `_next/static/chunks/${name}`;
+  if (executed.has(rel)) continue;
+  indirectEval(readFileSync(join(chunkDir, name), "utf8") + `\n//# sourceURL=/${rel}`);
+}
+
+// Let React hydrate, then drive the product's own Diagnosis run.
+await new Promise((r) => setTimeout(r, 3_000));
+console.log("SHIM_STATUS chip:", document.querySelector(".status__label")?.textContent ?? "n/a");
+const runButton = document.querySelector(RUN_ALL_SELECTOR!) as { click(): void } | null;
+if (!runButton) {
+  console.log("SHIM_RESULT fail (run control not found)");
+  process.exit(2);
+}
+runButton.click();
+
+const deadline = Date.now() + DEADLINE_MS;
+let lastSummary = "";
+while (Date.now() < deadline) {
+  await new Promise((r) => setTimeout(r, 2_000));
+  const summary = document.querySelector(FAILED_COUNT_SELECTOR)?.textContent ?? "";
+  if (summary && summary !== lastSummary) {
+    console.log("SHIM_SUMMARY", summary);
+    lastSummary = summary;
+  }
+  if (document.querySelector(REPORT_READY_SELECTOR)) {
+    const failed = Number(/(\d+)\s+failed\b/.exec(lastSummary)?.[1] ?? NaN);
+    const ok = failed <= 1; // parity with the known baseline: 43 passed, 1 failed
+    console.log(`SHIM_RESULT ${ok ? "ok" : "fail"} (summary: ${lastSummary})`);
+    process.exit(ok ? 0 : 1);
+  }
+}
+console.log(`SHIM_RESULT fail (deadline; last summary: ${lastSummary})`);
+process.exit(1);

From 4053b2ac4c163d5877aabf79df5ab1c2d1217c25 Mon Sep 17 00:00:00 2001
From: Eugenio Paluello 
Date: Tue, 21 Jul 2026 18:52:13 +0200
Subject: [PATCH 3/9] feat(truapi-host-cli): full-bundle orchestrator

---
 .../crates/truapi-host-cli/e2e/full-bundle.sh | 121 ++++++++++++++++++
 1 file changed, 121 insertions(+)
 create mode 100755 rust/crates/truapi-host-cli/e2e/full-bundle.sh

diff --git a/rust/crates/truapi-host-cli/e2e/full-bundle.sh b/rust/crates/truapi-host-cli/e2e/full-bundle.sh
new file mode 100755
index 00000000..b62b322c
--- /dev/null
+++ b/rust/crates/truapi-host-cli/e2e/full-bundle.sh
@@ -0,0 +1,121 @@
+#!/usr/bin/env bash
+# Full-bundle driver: the real playground bundle, served statically and run
+# browserless under bun + happy-dom, drives a pairing host through the
+# MessagePort->WS bridge; a signing host answers the pairing deeplink; every
+# operation lands in METRICS_JSONL.
+#
+#   make headless && e2e/full-bundle.sh
+#
+# Env:
+#   PRODUCT_ID               product id the pairing host serves (default truapi-playground.dot)
+#   FRAME                    frame-server address (default 127.0.0.1:9955)
+#   METRICS_JSONL            metrics sink (default /tmp/full-bundle-metrics.jsonl)
+#   SKIP_BUILD               set to reuse an existing playground/out
+#   HOST_CLI_SIGNER_MNEMONIC / TRUAPI_HOST_BASE_PATH  as in run.sh (e2e/.env supported)
+set -euo pipefail
+
+ROOT="$(cd "$(dirname "$0")/../../../.." && pwd)"
+BIN="$ROOT/target/debug/truapi-host"
+ENV_FILE="$(dirname "$0")/.env"
+[ -f "$ENV_FILE" ] && { set -a; . "$ENV_FILE"; set +a; }
+
+PRODUCT_ID="${PRODUCT_ID:-truapi-playground.dot}"
+FRAME="${FRAME:-127.0.0.1:9955}"
+export METRICS_JSONL="${METRICS_JSONL:-/tmp/full-bundle-metrics.jsonl}"
+
+[ -x "$BIN" ] || { echo "missing $BIN — run: make headless" >&2; exit 2; }
+
+if [ -z "${SKIP_BUILD:-}" ] || [ ! -d "$ROOT/playground/out" ]; then
+  (cd "$ROOT/js/packages/truapi" && npm run build)
+  (cd "$ROOT/playground" && yarn build)
+fi
+
+LOG="$(mktemp)"
+PAIR_PID=""
+SIGNER_PID=""
+WATCHER_PID=""
+KEEPALIVE_PID=""
+cleanup() {
+  [ -n "$WATCHER_PID" ] && kill "$WATCHER_PID" 2>/dev/null || true
+  [ -n "$SIGNER_PID" ] && kill "$SIGNER_PID" 2>/dev/null || true
+  # Independent of $SIGNER_PID: if the script exits before the main flow
+  # reaps the signer (e.g. an early/interrupted exit), this is the only path
+  # left that still catches it.
+  if [ -f "$LOG.signer" ]; then
+    kill "$(cat "$LOG.signer")" 2>/dev/null || true
+    rm -f "$LOG.signer"
+  fi
+  if [ -n "$PAIR_PID" ]; then
+    pkill -TERM -P "$PAIR_PID" 2>/dev/null || true
+    kill -TERM "$PAIR_PID" 2>/dev/null || true
+  fi
+  # The keepalive tail runs under a process-substitution wrapper shell; kill
+  # the wrapper's children before the wrapper itself (killing only the wrapper
+  # reparents the tail to init and leaks it — verified empirically).
+  if [ -n "$KEEPALIVE_PID" ]; then
+    pkill -TERM -P "$KEEPALIVE_PID" 2>/dev/null || true
+    kill -TERM "$KEEPALIVE_PID" 2>/dev/null || true
+  fi
+  # Success path removes $LOG itself before this trap runs; if it's still
+  # here, this exit was a failure -- keep it for post-mortem instead of
+  # silently deleting the only evidence.
+  [ -f "$LOG" ] && echo "host log preserved: $LOG" >&2
+}
+trap cleanup EXIT
+
+: > "$METRICS_JSONL"
+# stdin keepalive: with  >(tee "$LOG") 2>&1 &
+PAIR_PID=$!
+
+# Don't race the shim run against a socket that isn't bound yet: wait
+# for the host's own "listening" line, checking every 0.5s for up to 60s that
+# it hasn't died in the meantime.
+for _ in $(seq 1 120); do
+  grep -q '^FRAMES_LISTENING' "$LOG" && break
+  kill -0 "$PAIR_PID" 2>/dev/null || { echo "pairing host exited before FRAMES_LISTENING" >&2; exit 1; }
+  sleep 0.5
+done
+grep -q '^FRAMES_LISTENING' "$LOG" || { echo "pairing host: no FRAMES_LISTENING within 60s" >&2; exit 1; }
+
+# The deeplink appears only once the bundle calls requestLogin, so watch for
+# it concurrently with the shim run and answer with the signing host.
+(
+  for _ in $(seq 1 600); do
+    deeplink="$(grep -m1 -oE 'PAIRING_DEEPLINK .+' "$LOG" | cut -d' ' -f2- || true)"
+    if [ -n "$deeplink" ]; then
+      "$BIN" signing-host --deeplink "$deeplink" --auto-accept &
+      echo "$!" > "$LOG.signer"
+      exit 0
+    fi
+    sleep 0.5
+  done
+  echo "watcher: no deeplink within 300s" >&2
+) &
+WATCHER_PID=$!
+
+STATUS=0
+(cd "$ROOT/rust/crates/truapi-host-cli/js" && { [ -d node_modules ] || bun install; } \
+  && TRUAPI_FRAME_URL="ws://$FRAME" FULL_BUNDLE_DEADLINE_MS="${FULL_BUNDLE_DEADLINE_MS:-1200000}" \
+     bun scripts/load-bundle.ts) || STATUS=$?
+[ -f "$LOG.signer" ] && SIGNER_PID="$(cat "$LOG.signer")" && rm -f "$LOG.signer"
+
+[ "$STATUS" -eq 0 ] || exit "$STATUS"
+[ -s "$METRICS_JSONL" ] || { echo "no metrics recorded in $METRICS_JSONL" >&2; exit 1; }
+# A single "category":"signing" grep passes even if every signing op failed.
+# Gate on each battery-critical op individually succeeding (op field always
+# precedes outcome in HostMetricRecord's field order, so one pattern per line
+# suffices -- see rust/crates/truapi-host-cli/src/metrics.rs).
+for op in signing_sign_raw signing_sign_payload signing_create_transaction; do
+  grep -q "\"op\":\"$op\".*\"outcome\":\"success\"" "$METRICS_JSONL" || {
+    echo "no successful $op recorded in $METRICS_JSONL" >&2; exit 1; }
+done
+echo "full-bundle spike OK -> $METRICS_JSONL ($(wc -l < "$METRICS_JSONL") records)"
+rm -f "$LOG"

From 7fee49e5fd5bafd73309d0bc348de11bc8bbcab5 Mon Sep 17 00:00:00 2001
From: Eugenio Paluello 
Date: Wed, 22 Jul 2026 10:39:10 +0200
Subject: [PATCH 4/9] fix(truapi-host-cli): print failed diagnosis rows in the
 shim

---
 playground/tests/e2e-headless/targets.ts              |  4 ++++
 rust/crates/truapi-host-cli/js/scripts/load-bundle.ts | 11 +++++++++++
 2 files changed, 15 insertions(+)

diff --git a/playground/tests/e2e-headless/targets.ts b/playground/tests/e2e-headless/targets.ts
index cda9515b..c55aaf52 100644
--- a/playground/tests/e2e-headless/targets.ts
+++ b/playground/tests/e2e-headless/targets.ts
@@ -27,3 +27,7 @@ export const FAILED_COUNT_SELECTOR = '[data-testid="diagnosis-summary"]';
 // to the literal attribute value "true").
 export const REPORT_READY_SELECTOR =
   '[data-testid="diagnosis-report-markdown"][data-report-ready="true"]';
+
+// A single Diagnosis result row in the failed state (DiagnosisView.tsx:207-208:
+// data-testid="diagnosis-row" with data-status={r.status}, status "fail").
+export const FAILED_ROW_SELECTOR = '[data-testid="diagnosis-row"][data-status="fail"]';
diff --git a/rust/crates/truapi-host-cli/js/scripts/load-bundle.ts b/rust/crates/truapi-host-cli/js/scripts/load-bundle.ts
index 606852dd..eedfe5b6 100644
--- a/rust/crates/truapi-host-cli/js/scripts/load-bundle.ts
+++ b/rust/crates/truapi-host-cli/js/scripts/load-bundle.ts
@@ -9,6 +9,7 @@ import { join, resolve } from "node:path";
 import {
   DIAGNOSIS_PATH,
   FAILED_COUNT_SELECTOR,
+  FAILED_ROW_SELECTOR,
   REPORT_READY_SELECTOR,
   RUN_ALL_SELECTOR,
 } from "../../../../../playground/tests/e2e-headless/targets";
@@ -135,9 +136,19 @@ while (Date.now() < deadline) {
   if (document.querySelector(REPORT_READY_SELECTOR)) {
     const failed = Number(/(\d+)\s+failed\b/.exec(lastSummary)?.[1] ?? NaN);
     const ok = failed <= 1; // parity with the known baseline: 43 passed, 1 failed
+    if (!ok) printFailedRows();
     console.log(`SHIM_RESULT ${ok ? "ok" : "fail"} (summary: ${lastSummary})`);
     process.exit(ok ? 0 : 1);
   }
 }
+printFailedRows();
 console.log(`SHIM_RESULT fail (deadline; last summary: ${lastSummary})`);
 process.exit(1);
+
+// Which rows failed, so a red run is interpretable from stdout alone.
+function printFailedRows(): void {
+  for (const row of Array.from(document.querySelectorAll(FAILED_ROW_SELECTOR))) {
+    const text = (row.textContent ?? "").replace(/\s+/g, " ").trim().slice(0, 200);
+    console.log("SHIM_FAILED_ROW", text);
+  }
+}

From 4761e4b274c1c815ea54e06b3edbf75d2233eb82 Mon Sep 17 00:00:00 2001
From: Eugenio Paluello 
Date: Wed, 22 Jul 2026 11:05:59 +0200
Subject: [PATCH 5/9] fix(truapi-host-cli): fresh pairing-host base path per
 full-bundle run

---
 rust/crates/truapi-host-cli/e2e/full-bundle.sh | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/rust/crates/truapi-host-cli/e2e/full-bundle.sh b/rust/crates/truapi-host-cli/e2e/full-bundle.sh
index b62b322c..e17fa555 100755
--- a/rust/crates/truapi-host-cli/e2e/full-bundle.sh
+++ b/rust/crates/truapi-host-cli/e2e/full-bundle.sh
@@ -35,7 +35,13 @@ PAIR_PID=""
 SIGNER_PID=""
 WATCHER_PID=""
 KEEPALIVE_PID=""
+# Fresh pairing-host state per run: the host persists its paired session under
+# the base path, so a reused path silently restores the previous run's session
+# and the run is not reproducible. The signing host keeps its own (default)
+# base path on purpose, to reuse its attested account across runs.
+PAIR_BASE="$(mktemp -d)"
 cleanup() {
+  rm -rf "$PAIR_BASE"
   [ -n "$WATCHER_PID" ] && kill "$WATCHER_PID" 2>/dev/null || true
   [ -n "$SIGNER_PID" ] && kill "$SIGNER_PID" 2>/dev/null || true
   # Independent of $SIGNER_PID: if the script exits before the main flow
@@ -72,6 +78,7 @@ trap cleanup EXIT
 exec 3< <(tail -f /dev/null)
 KEEPALIVE_PID=$!
 "$BIN" pairing-host --product-id "$PRODUCT_ID" --frame-listen "$FRAME" \
+  --base-path "$PAIR_BASE" \
   --auto-accept <&3 > >(tee "$LOG") 2>&1 &
 PAIR_PID=$!
 

From 074534621ed822e88fb777e6a0518ea7a6675abf Mon Sep 17 00:00:00 2001
From: Eugenio Paluello 
Date: Wed, 22 Jul 2026 11:32:17 +0200
Subject: [PATCH 6/9] feat(truapi-host-cli): login-first orchestration and
 live-chain default for the full-bundle run

---
 .../crates/truapi-host-cli/e2e/full-bundle.sh | 28 +++++++++++++++++--
 .../truapi-host-cli/js/scripts/pre-login.ts   |  6 ++++
 2 files changed, 31 insertions(+), 3 deletions(-)
 create mode 100644 rust/crates/truapi-host-cli/js/scripts/pre-login.ts

diff --git a/rust/crates/truapi-host-cli/e2e/full-bundle.sh b/rust/crates/truapi-host-cli/e2e/full-bundle.sh
index e17fa555..1eff5d45 100755
--- a/rust/crates/truapi-host-cli/e2e/full-bundle.sh
+++ b/rust/crates/truapi-host-cli/e2e/full-bundle.sh
@@ -22,6 +22,11 @@ ENV_FILE="$(dirname "$0")/.env"
 PRODUCT_ID="${PRODUCT_ID:-truapi-playground.dot}"
 FRAME="${FRAME:-127.0.0.1:9955}"
 export METRICS_JSONL="${METRICS_JSONL:-/tmp/full-bundle-metrics.jsonl}"
+# Live-chain routing for the Chain/* methods, matching the dotli baseline.
+# Upstream keeps this opt-in because live chainHead streaming over the shared
+# product connection can still drop mid-run (see src/chain.rs) — expect the
+# head-subscription methods to be the flaky tail.
+export E2E_LIVE_CHAIN="${E2E_LIVE_CHAIN:-1}"
 
 [ -x "$BIN" ] || { echo "missing $BIN — run: make headless" >&2; exit 2; }
 
@@ -42,6 +47,7 @@ KEEPALIVE_PID=""
 PAIR_BASE="$(mktemp -d)"
 cleanup() {
   rm -rf "$PAIR_BASE"
+  rm -f "$LOG.signerlog"
   [ -n "$WATCHER_PID" ] && kill "$WATCHER_PID" 2>/dev/null || true
   [ -n "$SIGNER_PID" ] && kill "$SIGNER_PID" 2>/dev/null || true
   # Independent of $SIGNER_PID: if the script exits before the main flow
@@ -92,13 +98,14 @@ for _ in $(seq 1 120); do
 done
 grep -q '^FRAMES_LISTENING' "$LOG" || { echo "pairing host: no FRAMES_LISTENING within 60s" >&2; exit 1; }
 
-# The deeplink appears only once the bundle calls requestLogin, so watch for
-# it concurrently with the shim run and answer with the signing host.
+# The deeplink appears on the first requestLogin, so watch for it concurrently
+# and answer with the signing host (its output goes to $LOG.signerlog so the
+# allowance wait below can grep it).
 (
   for _ in $(seq 1 600); do
     deeplink="$(grep -m1 -oE 'PAIRING_DEEPLINK .+' "$LOG" | cut -d' ' -f2- || true)"
     if [ -n "$deeplink" ]; then
-      "$BIN" signing-host --deeplink "$deeplink" --auto-accept &
+      "$BIN" signing-host --deeplink "$deeplink" --auto-accept > >(tee "$LOG.signerlog") 2>&1 &
       echo "$!" > "$LOG.signer"
       exit 0
     fi
@@ -108,6 +115,21 @@ grep -q '^FRAMES_LISTENING' "$LOG" || { echo "pairing host: no FRAMES_LISTENING
 ) &
 WATCHER_PID=$!
 
+# Login-first, like the dotli baseline and the fleet: a minimal requestLogin
+# script triggers pairing, then we wait for the device-key statement-store
+# allowance — submits race the allowance registration otherwise.
+echo "pre-login: requesting session"
+(cd "$ROOT/rust/crates/truapi-host-cli/js" && TRUAPI_FRAME_URL="ws://$FRAME" \
+  TRUAPI_PRODUCT_ID="$PRODUCT_ID" \
+  TRUAPI_SCRIPT="$ROOT/rust/crates/truapi-host-cli/js/scripts/pre-login.ts" \
+  bun runner.ts) || { echo "pre-login failed" >&2; exit 1; }
+for _ in $(seq 1 240); do
+  grep -q "SIGNING_HOST_ALLOWANCE device seq=" "$LOG.signerlog" 2>/dev/null && break
+  sleep 0.5
+done
+grep -q "SIGNING_HOST_ALLOWANCE device seq=" "$LOG.signerlog" 2>/dev/null \
+  || echo "pre-login: device allowance not confirmed within 120s; continuing" >&2
+
 STATUS=0
 (cd "$ROOT/rust/crates/truapi-host-cli/js" && { [ -d node_modules ] || bun install; } \
   && TRUAPI_FRAME_URL="ws://$FRAME" FULL_BUNDLE_DEADLINE_MS="${FULL_BUNDLE_DEADLINE_MS:-1200000}" \
diff --git a/rust/crates/truapi-host-cli/js/scripts/pre-login.ts b/rust/crates/truapi-host-cli/js/scripts/pre-login.ts
new file mode 100644
index 00000000..4165a8ff
--- /dev/null
+++ b/rust/crates/truapi-host-cli/js/scripts/pre-login.ts
@@ -0,0 +1,6 @@
+// Establishes the host session before the main driver runs: the pairing host
+// emits its deeplink on the first requestLogin, and the runtime holds the
+// resulting session for every later product connection. Mirrors how the dotli
+// e2e baseline signs in before driving Diagnosis.
+const result = await truapi.account.requestLogin();
+console.log("PRELOGIN_OK", JSON.stringify(result).slice(0, 160));

From a3350820ce898480b8a5d43e2bca4fd79f3f0143 Mon Sep 17 00:00:00 2001
From: Eugenio Paluello 
Date: Wed, 22 Jul 2026 11:33:32 +0200
Subject: [PATCH 7/9] fix(truapi-host-cli): call requestLogin with its payload
 in the pre-login script

---
 rust/crates/truapi-host-cli/js/scripts/pre-login.ts | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/rust/crates/truapi-host-cli/js/scripts/pre-login.ts b/rust/crates/truapi-host-cli/js/scripts/pre-login.ts
index 4165a8ff..a73cdf28 100644
--- a/rust/crates/truapi-host-cli/js/scripts/pre-login.ts
+++ b/rust/crates/truapi-host-cli/js/scripts/pre-login.ts
@@ -2,5 +2,8 @@
 // emits its deeplink on the first requestLogin, and the runtime holds the
 // resulting session for every later product connection. Mirrors how the dotli
 // e2e baseline signs in before driving Diagnosis.
-const result = await truapi.account.requestLogin();
-console.log("PRELOGIN_OK", JSON.stringify(result).slice(0, 160));
+const login = await truapi.account.requestLogin({ reason: undefined });
+if (!(login.isOk() && login.value === "Success")) {
+  throw new Error(`pre-login failed: ${login.isOk() ? String(login.value) : JSON.stringify(login.error)}`);
+}
+console.log("PRELOGIN_OK");

From 07313946e2d2291707abbe8ce58f450f38ba2053 Mon Sep 17 00:00:00 2001
From: Eugenio Paluello 
Date: Wed, 22 Jul 2026 12:05:12 +0200
Subject: [PATCH 8/9] fix(truapi-host-cli): success-only exit-1 in the cleanup
 trap under set -e

---
 rust/crates/truapi-host-cli/e2e/full-bundle.sh | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/rust/crates/truapi-host-cli/e2e/full-bundle.sh b/rust/crates/truapi-host-cli/e2e/full-bundle.sh
index 1eff5d45..2ff528d0 100755
--- a/rust/crates/truapi-host-cli/e2e/full-bundle.sh
+++ b/rust/crates/truapi-host-cli/e2e/full-bundle.sh
@@ -71,7 +71,9 @@ cleanup() {
   # Success path removes $LOG itself before this trap runs; if it's still
   # here, this exit was a failure -- keep it for post-mortem instead of
   # silently deleting the only evidence.
-  [ -f "$LOG" ] && echo "host log preserved: $LOG" >&2
+  # `|| true`: as the trap's last command, a false [ -f ] would otherwise
+  # become the script's exit status and turn every SUCCESSFUL run into exit 1.
+  [ -f "$LOG" ] && echo "host log preserved: $LOG" >&2 || true
 }
 trap cleanup EXIT
 

From d12a4c8e7e1c83f1d7ee5d90aeef22daeee22848 Mon Sep 17 00:00:00 2001
From: Eugenio Paluello 
Date: Wed, 22 Jul 2026 12:24:39 +0200
Subject: [PATCH 9/9] refactor(truapi-host-cli): tighten selectors contract and
 cleanup paths

---
 playground/tests/e2e-headless/targets.ts      | 39 ++++++-------
 .../crates/truapi-host-cli/e2e/full-bundle.sh | 18 ++++--
 .../truapi-host-cli/js/scripts/load-bundle.ts | 56 +++++++++++++------
 .../truapi-host-cli/js/scripts/pre-login.ts   |  1 +
 4 files changed, 67 insertions(+), 47 deletions(-)

diff --git a/playground/tests/e2e-headless/targets.ts b/playground/tests/e2e-headless/targets.ts
index c55aaf52..29d7bc74 100644
--- a/playground/tests/e2e-headless/targets.ts
+++ b/playground/tests/e2e-headless/targets.ts
@@ -1,33 +1,26 @@
-// Discovered UI targets for the headless full-bundle spike.
-// Values verified against playground/src on this branch — see
-// .superpowers/sdd/task-1-report.md for file:line evidence. Update here if
-// the playground UI moves; this is the single place UI knowledge lives —
-// no other task may hardcode a selector.
+// UI targets for the headless full-bundle driver — the single place selector
+// knowledge lives; update here when the playground UI moves.
 
-// The Diagnosis screen is deep-linked from the root page via a `view` query
-// param (see playground/src/app/page.tsx: VIEW_PARAM / SERVICE_FOR_VIEW /
-// selectionFromUrl). Loading this path directly renders DiagnosisView with
-// no need to click through the left-rail "Diagnosis" entry first.
+// Deep-links straight to DiagnosisView (playground/src/app/page.tsx), skipping
+// the left-rail click-through.
 export const DIAGNOSIS_PATH = "/?view=diagnosis";
 
-// The Diagnosis run does NOT auto-start on mount: `runDiagnosis` is only
-// invoked from DiagnosisView's onRun callback, which is wired to this
-// button's onClick. There is no effect anywhere that calls it automatically.
-export const RUN_ALL_SELECTOR: string | null = '[data-testid="diagnosis-run"]';
+// Diagnosis never auto-starts on mount (DiagnosisView) — this button's onClick
+// is the only trigger.
+export const RUN_ALL_SELECTOR = '[data-testid="diagnosis-run"]';
 
-// Text content is "{passCount} success · {failCount} failed"; the existing
-// e2e test parses the failed count from this same element with
-// /(\d+)\s+failed\b/.
+// Connection status chip rendered by StatusChip (playground/src/app/page.tsx).
+export const STATUS_CHIP_SELECTOR = ".status__label";
+
+// Summary text reads "{passCount} success · {failCount} failed" (DiagnosisView).
 export const FAILED_COUNT_SELECTOR = '[data-testid="diagnosis-summary"]';
 
-// Report body; carries data-report-ready="true" once the run has finished
-// rendering its markdown (DiagnosisView.tsx:173-174 — the `
` sibling of
-// diagnosis-copy-report, rendered only when `hasResults && !isRunning`;
-// data-report-ready is `reportMarkdown.length > 0`, which React stringifies
-// to the literal attribute value "true").
+// Parses the "N success · M failed" summary text.
+export const FAILED_COUNT_PATTERN = /(\d+)\s+failed\b/;
+
+// Set once the run has finished rendering its report (DiagnosisView).
 export const REPORT_READY_SELECTOR =
   '[data-testid="diagnosis-report-markdown"][data-report-ready="true"]';
 
-// A single Diagnosis result row in the failed state (DiagnosisView.tsx:207-208:
-// data-testid="diagnosis-row" with data-status={r.status}, status "fail").
+// A single Diagnosis result row in the failed state (DiagnosisView).
 export const FAILED_ROW_SELECTOR = '[data-testid="diagnosis-row"][data-status="fail"]';
diff --git a/rust/crates/truapi-host-cli/e2e/full-bundle.sh b/rust/crates/truapi-host-cli/e2e/full-bundle.sh
index 2ff528d0..f584e60a 100755
--- a/rust/crates/truapi-host-cli/e2e/full-bundle.sh
+++ b/rust/crates/truapi-host-cli/e2e/full-bundle.sh
@@ -11,7 +11,11 @@
 #   FRAME                    frame-server address (default 127.0.0.1:9955)
 #   METRICS_JSONL            metrics sink (default /tmp/full-bundle-metrics.jsonl)
 #   SKIP_BUILD               set to reuse an existing playground/out
-#   HOST_CLI_SIGNER_MNEMONIC / TRUAPI_HOST_BASE_PATH  as in run.sh (e2e/.env supported)
+#   PLAYGROUND_OUT           static export dir the shim serves (default playground/out)
+#   FULL_BUNDLE_DEADLINE_MS  shim deadline ms (default 1200000 -- TS-side default)
+#   E2E_LIVE_CHAIN           live-chain routing for Chain/* (default 1)
+#   HOST_CLI_SIGNER_MNEMONIC as in run.sh (e2e/.env supported)
+#   TRUAPI_HOST_BASE_PATH    signing host only -- the pairing host always gets a fresh temp path
 set -euo pipefail
 
 ROOT="$(cd "$(dirname "$0")/../../../.." && pwd)"
@@ -46,7 +50,6 @@ KEEPALIVE_PID=""
 # base path on purpose, to reuse its attested account across runs.
 PAIR_BASE="$(mktemp -d)"
 cleanup() {
-  rm -rf "$PAIR_BASE"
   rm -f "$LOG.signerlog"
   [ -n "$WATCHER_PID" ] && kill "$WATCHER_PID" 2>/dev/null || true
   [ -n "$SIGNER_PID" ] && kill "$SIGNER_PID" 2>/dev/null || true
@@ -60,10 +63,14 @@ cleanup() {
   if [ -n "$PAIR_PID" ]; then
     pkill -TERM -P "$PAIR_PID" 2>/dev/null || true
     kill -TERM "$PAIR_PID" 2>/dev/null || true
+    sleep 0.5
+    pkill -KILL -P "$PAIR_PID" 2>/dev/null || true
+    kill -KILL "$PAIR_PID" 2>/dev/null || true
   fi
+  rm -rf "$PAIR_BASE"
   # The keepalive tail runs under a process-substitution wrapper shell; kill
   # the wrapper's children before the wrapper itself (killing only the wrapper
-  # reparents the tail to init and leaks it — verified empirically).
+  # reparents the tail to init and leaks it).
   if [ -n "$KEEPALIVE_PID" ]; then
     pkill -TERM -P "$KEEPALIVE_PID" 2>/dev/null || true
     kill -TERM "$KEEPALIVE_PID" 2>/dev/null || true
@@ -79,7 +86,7 @@ trap cleanup EXIT
 
 : > "$METRICS_JSONL"
 # stdin keepalive: with /dev/null \
 
 STATUS=0
 (cd "$ROOT/rust/crates/truapi-host-cli/js" && { [ -d node_modules ] || bun install; } \
-  && TRUAPI_FRAME_URL="ws://$FRAME" FULL_BUNDLE_DEADLINE_MS="${FULL_BUNDLE_DEADLINE_MS:-1200000}" \
-     bun scripts/load-bundle.ts) || STATUS=$?
+  && TRUAPI_FRAME_URL="ws://$FRAME" bun scripts/load-bundle.ts) || STATUS=$?
 [ -f "$LOG.signer" ] && SIGNER_PID="$(cat "$LOG.signer")" && rm -f "$LOG.signer"
 
 [ "$STATUS" -eq 0 ] || exit "$STATUS"
diff --git a/rust/crates/truapi-host-cli/js/scripts/load-bundle.ts b/rust/crates/truapi-host-cli/js/scripts/load-bundle.ts
index eedfe5b6..69208d30 100644
--- a/rust/crates/truapi-host-cli/js/scripts/load-bundle.ts
+++ b/rust/crates/truapi-host-cli/js/scripts/load-bundle.ts
@@ -4,14 +4,16 @@
 // Realm discipline matters: a separate realm breaks the transport's
 // `instanceof Uint8Array` guard and frames drop silently.
 import { GlobalRegistrator } from "@happy-dom/global-registrator";
-import { readFileSync, readdirSync } from "node:fs";
+import { existsSync, readFileSync, readdirSync } from "node:fs";
 import { join, resolve } from "node:path";
 import {
   DIAGNOSIS_PATH,
+  FAILED_COUNT_PATTERN,
   FAILED_COUNT_SELECTOR,
   FAILED_ROW_SELECTOR,
   REPORT_READY_SELECTOR,
   RUN_ALL_SELECTOR,
+  STATUS_CHIP_SELECTOR,
 } from "../../../../../playground/tests/e2e-headless/targets";
 
 const OUT = resolve(
@@ -19,20 +21,27 @@ const OUT = resolve(
 );
 const FRAME_URL = process.env.TRUAPI_FRAME_URL ?? "ws://127.0.0.1:9955";
 const DEADLINE_MS = Number(process.env.FULL_BUNDLE_DEADLINE_MS ?? 1_200_000);
+const HYDRATE_SETTLE_MS = 3_000;
+const POLL_MS = 2_000;
+
+if (!existsSync(join(OUT, "index.html")) || !existsSync(join(OUT, "_next/static/chunks"))) {
+  console.error(
+    `missing playground export under ${OUT} — set PLAYGROUND_OUT or unset SKIP_BUILD to rebuild`,
+  );
+  process.exit(2);
+}
 
 // Serve the export so happy-dom's CSS/font fetches resolve.
 const MIME: Record = {
   ".html": "text/html", ".js": "text/javascript", ".css": "text/css",
   ".woff2": "font/woff2", ".txt": "text/plain", ".json": "application/json",
 };
-// Native Response, captured before happy-dom can shadow it: this handler only
-// ever runs post-registration (it serves happy-dom's own asset fetches), where
-// a bare `Response` resolves to happy-dom's class — Bun.serve rejects that and
-// Bun.inspects the object, whose Headers carry the entire Window (~13MB of
-// stdout per run, and the asset request aborts mid-response).
+// Captured before happy-dom can shadow it: Bun.serve rejects happy-dom's own
+// Response class if used here instead.
 const NativeResponse = globalThis.Response;
 const server = Bun.serve({
   port: 0,
+  hostname: "127.0.0.1",
   async fetch(req) {
     let path = new URL(req.url).pathname;
     if (path === "/") path = "/index.html";
@@ -48,10 +57,7 @@ const NativeWebSocket = globalThis.WebSocket;
 GlobalRegistrator.register({ url: `http://127.0.0.1:${server.port}${DIAGNOSIS_PATH}` });
 
 // Defensive cap: happy-dom / the bundle can console.error whole objects on
-// early hydration hiccups; render each argument compactly and bounded so no
-// future path can flood stdout. (The ~13MB dump previously seen here was NOT
-// console.error — it was Bun.serve rejecting a shadowed Response, fixed above
-// via NativeResponse.)
+// hydration hiccups; keep every argument compact and bounded so no future path can flood stdout.
 const rawConsoleError = console.error.bind(console);
 console.error = (...args: unknown[]) => {
   const compact = args.map((a) =>
@@ -73,7 +79,14 @@ ws.addEventListener("open", () => {
 ws.addEventListener("message", (event: MessageEvent) => {
   channel.port2.postMessage(new Uint8Array(event.data as ArrayBuffer));
 });
-ws.addEventListener("close", () => console.log("SHIM_STATUS ws closed"));
+ws.addEventListener("close", () => {
+  console.log("SHIM_STATUS ws closed");
+  // A closed socket can never finish the run; don't poll for 20 minutes.
+  if (!document.querySelector(REPORT_READY_SELECTOR)) {
+    console.log("SHIM_RESULT fail (ws closed)");
+    process.exit(2);
+  }
+});
 ws.addEventListener("error", () => {
   console.log("SHIM_STATUS ws error");
   process.exit(2);
@@ -85,8 +98,15 @@ channel.port2.onmessage = (event: MessageEvent) => {
 };
 channel.port2.start?.();
 
-(window as any).__HOST_WEBVIEW_MARK__ = true;
-(window as any).__HOST_API_PORT__ = channel.port1;
+declare global {
+  interface Window {
+    __HOST_WEBVIEW_MARK__: boolean;
+    __HOST_API_PORT__: MessagePort;
+  }
+}
+
+window.__HOST_WEBVIEW_MARK__ = true;
+window.__HOST_API_PORT__ = channel.port1;
 
 // Execute the shipped bundle in this realm: index.html scripts in document
 // order, then every hashed chunk not already referenced (eager evaluation
@@ -115,9 +135,9 @@ for (const name of readdirSync(chunkDir).filter((n) => n.endsWith(".js")).sort()
 }
 
 // Let React hydrate, then drive the product's own Diagnosis run.
-await new Promise((r) => setTimeout(r, 3_000));
-console.log("SHIM_STATUS chip:", document.querySelector(".status__label")?.textContent ?? "n/a");
-const runButton = document.querySelector(RUN_ALL_SELECTOR!) as { click(): void } | null;
+await new Promise((r) => setTimeout(r, HYDRATE_SETTLE_MS));
+console.log("SHIM_STATUS chip:", document.querySelector(STATUS_CHIP_SELECTOR)?.textContent ?? "n/a");
+const runButton = document.querySelector(RUN_ALL_SELECTOR) as { click(): void } | null;
 if (!runButton) {
   console.log("SHIM_RESULT fail (run control not found)");
   process.exit(2);
@@ -127,14 +147,14 @@ runButton.click();
 const deadline = Date.now() + DEADLINE_MS;
 let lastSummary = "";
 while (Date.now() < deadline) {
-  await new Promise((r) => setTimeout(r, 2_000));
+  await new Promise((r) => setTimeout(r, POLL_MS));
   const summary = document.querySelector(FAILED_COUNT_SELECTOR)?.textContent ?? "";
   if (summary && summary !== lastSummary) {
     console.log("SHIM_SUMMARY", summary);
     lastSummary = summary;
   }
   if (document.querySelector(REPORT_READY_SELECTOR)) {
-    const failed = Number(/(\d+)\s+failed\b/.exec(lastSummary)?.[1] ?? NaN);
+    const failed = Number(FAILED_COUNT_PATTERN.exec(lastSummary)?.[1] ?? NaN);
     const ok = failed <= 1; // parity with the known baseline: 43 passed, 1 failed
     if (!ok) printFailedRows();
     console.log(`SHIM_RESULT ${ok ? "ok" : "fail"} (summary: ${lastSummary})`);
diff --git a/rust/crates/truapi-host-cli/js/scripts/pre-login.ts b/rust/crates/truapi-host-cli/js/scripts/pre-login.ts
index a73cdf28..13a067c6 100644
--- a/rust/crates/truapi-host-cli/js/scripts/pre-login.ts
+++ b/rust/crates/truapi-host-cli/js/scripts/pre-login.ts
@@ -1,3 +1,4 @@
+/// 
 // Establishes the host session before the main driver runs: the pairing host
 // emits its deeplink on the first requestLogin, and the runtime holds the
 // resulting session for every later product connection. Mirrors how the dotli