# Notification registration capture driver

This is manual runtime evidence, with no test assertions. The base and head native apps and web clients run from separate installed worktrees. Both Android clients use the cached development build without Firebase configuration, with the same Pixel 9 / API 36 viewport and light appearance. Account sign-in uses only the isolated T3 Connect test stage.

## Native observations

Run `runtime-driver.mjs` with `PROOF_CHECKOUT` pointing at the checkout and `METRO_PORT` pointing at its Metro server. `eval` evaluates its next argument in Hermes; `log <file>` saves native JavaScript console events. `observe.js` installs transparent observers around native push-token lookup and the relay client's registration method. The original native lookup and original hosted registration are still executed. Its native error text is the actual Firebase initialization failure, not an injected error.

Set `global.proofRelayOrigin` to the device-reachable local device endpoint before evaluating `observe.js`. After the hosted test relay accepts a registration, the observer submits that same registration to the local source relay, changing only its display label to `Proof Pixel 9`. It logs notification preferences and whether the outgoing payload has a token; it does not log authentication headers.

The enable reproduction: open Settings > Notifications, toggle Device Notifications, grant Android's notification permission, and capture the resulting alert. The base announces success despite a failed native lookup; head announces incomplete setup. The detailed head trace also captures a repeat enable attempt after permission is granted.

For the refresh reproduction, seed an accepted session token through Expo's native token event:

```js
const moduleId = [...__r.getModules()].find(([id,m]) => m.verboseName?.endsWith('PushTokenManager.native.js'))[0];
__r(moduleId).default.emit('onDevicePushToken', {devicePushToken:'synthetic-proof-token-26557'});
```

This token is intentionally synthetic; it seeds registration state and is never used to claim FCM delivery. Wait for its accepted registration and at least 60 seconds (Android's registration replay interval). Record Home, then foreground the same app without restarting its JS session. The next native lookup really fails because Firebase is unconfigured. Refresh the web Mobile clients page and capture the stored preference/list result. Base sends a missing token and disabled preference. Head reused the accepted session token and preserved the enabled preference in the recorded run.

## Source relay

Run `node relay-driver.mjs <checkout> <port> <base-or-head>` separately for each commit. This imports that checkout's unmodified `Devices` service and Drizzle Effect driver, applies all of its relay migrations to an isolated PGlite PostgreSQL-compatible database, and exposes registration and device-list HTTP endpoints. The SQL binding executes real queries; it does not substitute hardcoded device responses or notification predicates.

The hosted test stage runs main's relay, so its response cannot prove a changed relay predicate. The local source endpoint supplies the changed behavior. It is an isolated device-service server, not a deployment of the full Cloudflare Worker, and does not exercise hosted authorization or FCM delivery.

POST `tokenless-seed.json` identically to each local `/v1/mobile/devices` endpoint. This deliberately seeds an enabled preference with no push token, independently of the mobile client's computed preference. Read `/snapshot` to see both the stored row and the actual Devices service response. Base lists the tokenless fixture as notifications enabled; head lists it as disabled.

## Real web client

In each commit's real signed-in web app, open Account > Mobile clients. Set `window.proofRelayOrigin` to that commit's local device endpoint and evaluate `web-source-relay.js`. Only the relay client's device-list transport is redirected: it fetches the actual running source relay response. No rendered text, badges, or notification readiness logic is replaced. Press the page's Refresh button. The static final frames pair the registered refresh fixture and the identical tokenless fixture.

`relay-driver.mjs`, `runtime-driver.mjs`, `observe.js`, `web-source-relay.js`, and `tokenless-seed.json` are the reusable drivers/seeds. Public logs retain only proof events and source-service results. The isolated accounts, authentication headers, local checkout paths, and machine addresses are excluded.

For the final head alert, repeat enable after a cold JS restart (permission remains granted) using `observe-enable.js`, which logs original lookup and original registration without mirroring. The final alert pair is pixel-identical; its runtime log records the real Firebase error for that same attempt. The first permission-grant recording is also provided.
