# RD.2 TV / cast and watch-party integration

Date: 2026-07-22

## Existing-infrastructure audit

The repository had no Google Cast CAF receiver/sender,
`react-native-google-cast`, AirPlay route picker, or equivalent device-discovery
implementation in the OSHUN customer apps. Searches across app dependencies and
native/client source found only descriptive casting catalogs in Shakti and
low-level DRM/AirPlay capability declarations. Those are not a session transport
and were not reused as if they were one.

The reusable authorities that did exist were:

- RB.5's existing Rail mobile route and Veritas external-broadcast companion;
- the V10 kernel's single primary `VideoLaneArbiter` and `RingPolicy`;
- Stage's coupled media offer and V4 Match's first-party-live program manifest;
- RB.3's canonical, tenant/resource-exact `LiveMediaTenantLiveChatControl`.

RD.2 therefore adds a receiver-grade web TV surface and six-digit phone pairing,
without inventing a false native discovery SDK. A TV browser, installed web
receiver, or cast receiver URL can open `/rail/tv`; the existing mobile Rail
opens `/rail-tv-remote` and becomes the remote after pairing.

## Contract and policy

`libs/contracts/src/v10/tv-surface.ts` owns `v10.tv-rail.1` and the pairing and
remote-command envelopes. The contract makes these invariants structural:

- only `v3.stage` concerts and `v4.match-channel` esports are party programs;
- the program ring is exactly `games`;
- session/resource ids and six-digit pairing codes are strict;
- playback is monotonic and revisioned, with bounded position/duration;
- enabled chat carries one exact tenant/stream room and can exist only for a
  first-party program;
- `docked-third-party` chat is always absent with `hg-5-review-required`—there
  is deliberately no legal-review boolean a caller can flip;
- an unconfigured RB.3 chat organ is also represented honestly as playback-only,
  never as a working chat affordance.

`TvWatchPartyCoordinator` is the authoritative in-process state machine. It runs
the manifest through `RingPolicy.watchPartyChannels` before allocating any
session or pairing code, tracks distinct paired remotes, authenticates a remote
token, deduplicates command ids, rejects stale revisions, and applies only
transport capabilities granted by the source. The adult ring is therefore
excluded before serialization, not hidden by a client.

## RB.3 chat binding

`RailTvWatchPartyChat` is a narrow adapter over RB.3's actual
`LiveMediaTenantLiveChatControl`. Join, send, and history calls accept no caller
stream id; the adapter derives the tenant and exact stream from the validated TV
session. A disabled capability is rejected before resolving or invoking any chat
control. `V10RailLiveChatControl` fixes the Rail tenant over the shared organ,
and BFF endpoints expose join/send/history only when that control is configured.

The deployed program authority is all-or-none through
`OSHUN_V10_RAIL_TV_PROGRAM_URL` and `OSHUN_V10_RAIL_TV_PROGRAM_TOKEN`. Its
strict response must echo the authenticated user, requested channel, and program
id; malformed, cross-subject, cross-program, or cross-channel responses are
rejected. An absent authority returns an honest 503 rather than sample media.

## Surfaces

The TV receiver is a fixed, full-viewport video plane with progressive, HLS, and
DASH loading, a restrained lower third, a pairing code, and party-policy status.
It polls the authenticated session and reconciles video play/pause and position
to the authoritative revision. It does not render a dashboard or chat composer
for third-party content.

The existing mobile Rail now exposes a second TV row alongside the Veritas
companion. Its remote mode accepts a bounded six-digit code, keeps the remote
token separate from V1 auth, polls authoritative state, and sends revisioned
play/pause/seek commands. Stale-command failures refresh rather than overwriting
the TV state. The design uses one sea-glass accent, cardless divisions, and
reduced chrome in accordance with the mandatory frontend guidance.

## HG-5

HG-5 remains open. No legal review occurred in this implementation. The code
enforces the pre-review state: third-party playback remains possible, but its
chat capability has no room and every attempted bridge operation fails before
RB.3 is called. The ledger's human-gate checkbox must stay unchecked until the
named review happens.

## Verification

Passing focused checks on 2026-07-22:

- TV contract: 4 tests;
- watch-party coordinator: 5 tests;
- BFF route, deployed program source, and RB.3 bridge: 8 tests;
- V3 Stage suite after enabling the TV surface: 79 passed, 3 intentionally
  skipped integration tests;
- mobile client/surface/navigation: 30 Jest tests;
- TV receiver: 3 Vitest tests;
- Chromium Playwright: 2/2, including full-viewport geometry and an axe scan;
- contract, kernel, focused BFF, and web TypeScript checks;
- targeted ESLint and Prettier checks;
- direct production BFF esbuild bundle;
- Maestro device-matrix dry run and YAML flow wiring.

The full mobile TypeScript command was also run. It reached only five existing,
unrelated `noUncheckedIndexedAccess` errors in
`libs/contracts/src/study/{evaluation-metrics,performance-environment-matrix}.ts`;
the RD.2 Jest transform and focused TypeScript-bearing suites are green. Native
Maestro execution is unavailable because `adb devices` is empty and this Linux
host has no iOS runtime. A Next production build was not run: on this 15 GiB
host the repository's hard gate requires explicit approval for that build. The
targeted Next dev Playwright run and web TypeScript check pass. A nominal Nx BFF
build was stopped when Nx expanded it to 324 dependency tasks; the exact BFF
esbuild production command then passed directly.
