# V10 Rail kernel

The ring policy in `src/ring-policy.ts` is the sole authority for static channel
ring assignment and placement walls. Adult channels are fail-closed and cannot
be opted in until V1 identity supplies a current age-verification assertion.
Manifest-aware placement also intersects those hard walls with the channel's
declared surface matrix, so a recommendation cannot appear on a surface the
channel marked unavailable.

The absence-safety boundary is enforced after registration as well as before it.
Drips, rendered and restored tile payloads, and micro-act input/result values
all pass the same runtime signal scan; undeclared drip types and schema-valid
payloads that introduce punishment semantics fail closed.

## Dogfood journal and weekly report

`src/dogfood.ts` owns the local exit-test artifact. Its strict
`v10.rail-dogfood.1` schema contains only the existing closed Rail metric union
plus one heartbeat-bounded active docked session; event identifiers remain
unique and storage is capped at 10,000 events / 35 days. Session mutations
reject backward time, prevent concurrent owners from double-counting, close
abandoned owners at their last heartbeat, and never turn a zero-duration mount
into a fabricated docked event. Sub-second mounts are discarded as lifecycle
noise.

`railDogfoodReport` clips completed and fresh active sessions to the seven days
ending at the requested instant, rejects overlapping user sessions, and reports
hours by face/channel, micro-act outcomes, and every granted or denied elevation
decision. A stale active session is capped at its heartbeat. The report carries
`ALLOWED_RAIL_METRICS` unchanged; it introduces no click, conversion, or
engagement counter.

Prediction micro-acts are limited to points and bragging rights. They must
declare `kind: 'prediction'` and `stakes: 'points-only'`. Any wager-shaped flow,
including schemas that combine value-in with value-out, is rejected and requires
its own product, legal, safety, and payments review outside the Rail concept.

## Audio lane authority

`AudioLaneArbiter` owns the single audio-holder slot. A channel receives only a
bound `AudioLaneChannelPort`, which can submit or withdraw one pending offer and
has no acquire, grab, accept, or handoff operation. The Rail host may accept an
offer after an explicit user action or connect exactly one policy listener to
real `DaypartEngine` transition events. Competing channel requests therefore
remain ordered offers until trusted Rail policy selects one.

Acquisition creates a paused holder at full volume, so accepting an offer never
autoplays it. Only the arbiter's Rail-facing `playByUser`, `pauseByUser`,
`setVolumeByUser`, and `releaseHolderByUser` methods can change that lifecycle,
and play/pause or volume actions fail when the source did not declare the
corresponding transport capability. A release returns the final holder state and
frees that channel to offer again; a handoff replaces the holder rather than
creating another one.

`reconcileLoudness` applies the loudness engine to the live holder without
turning policy permission into autoplay. An already-playing holder continues
through deep work at effective loudness 1, while a paused holder remains paused
even when a louder daypart permits sound. Effective loudness 0 policy-pauses an
active holder, clears any voice overlay, and records `loudness-policy` as the
transport authority.

Brief voice interjections use `startDuckingOverlay`, never the holder slot. The
holder must be playing and declare `supportsDucking`; only one overlay may be
active, its requested volume is a ceiling, and its duration is capped at 30
seconds. `expireDuckingOverlay` removes it at the declared deadline, while a
pause, release, or handoff removes it immediately. The overlay carries only the
current `holderId`, timing, speaker, and ducking metadata, so it cannot behave
as a second audio source.

## Video lane authority

`VideoLaneArbiter` owns the single primary-video holder and applies the same
offer/accept boundary as the audio lane. A bound `VideoLaneChannelPort` exposes
only `offer` and `withdraw`; explicit Rail user actions or one connected
daypart-transition policy are the only acceptance authorities. Contending offers
remain ordered, a handoff replaces the previous holder, and release frees the
channel to offer again.

Every candidate is validated as a `VideoSourceSpec` carrying exactly one of the
settled content classes—`first-party-live`, `overlay`, or `docked-third-party`.
Available classes continue through offers, holders, handoffs, and snapshots; an
unavailable class is rejected before it enters the queue. The lane does not
invent provider playback state or embed policy; those controls remain separate
RA.4 responsibilities built on this arbitration boundary.

`resolveDockedVideoPolicy` is the RA.4 posture boundary for hosted third-party
players. It consumes only a resolved daypart, effective loudness, and persisted
ambient-pin presence. Loudness 0 hides the player; levels 1–2 require a muted,
paused-until-user-acts start; and deep work holds the player unless the user pin
is present. The pin uses the statically registered wellness channel
`v10.rail-docked-video` and moment key `ambient-video`, so the existing V1
subscription/loudness stores remain the only account authority. The decision
reports whether level 3 permits autoplay capability, but the web dock currently
keeps both official providers configured with autoplay disabled.

`VideoOverlaySlot` is the separate overlay/second-screen primitive. It accepts
only an `overlay` `VideoSourceSpec` delivered by the `external-clock` provider
and is bound to one validated channel. The clock starts unsynced and advances
only after an explicit user sync; pause, resume, bounded manual offset, and
pause-tolerant re-sync remain user-authorized actions with inspectable action
metadata. These operations synchronize the companion model only: they neither
control nor inspect the external player.

The owning companion consumer may atomically publish up to 500 bounded,
reference-only cues. A frame projects active and next cues against the adjusted
clock while keeping claim, receipt, caption, and media payloads in the owning
channel. RB.1 will use this slot for the real Veritas ticker. There is
intentionally no provider-time method in Phase A; a later adapter must introduce
a distinct audited authority instead of presenting provider observations as user
actions.

`FIRST_PARTY_LIVE_VIDEO_GATE` is the Phase-A availability authority for the
third video class. It reports `{ configured: false, code: 'not_configured' }`
and identifies HLS and DASH as the only valid future delivery protocols. A
`first-party-live` declaration is rejected during `RailChannelHost`
registration, and the same gate rejects a direct `VideoLaneArbiter` offer, so a
channel cannot bypass registration and queue an unavailable source.

The current `@oshun/streaming` package is event/Kafka infrastructure, not the
shared media substrate. Aphrodite's HLS implementation is still tenant-specific.
V10 imports neither and creates no parallel player. The later substrate
extraction must replace this single gate with real shared-media readiness before
any first-party channel can register or any HLS/DASH playback path can open.

## V1 identity and Rail state ownership

The Rail joins V1's account graph by the authenticated `userId`; it does not
create a second identity or account store.

| State                       | Current V1 owner                                                                                                                                                                                                                                                                                                                                                                                               | Rail integration rule                                                                                                                                                                                                                                  |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Account and session         | `libs/oshun/auth` defines the customer/session contracts. `libs/shared/identity` validates cross-domain claims, and `libs/shared/auth` supplies service/repository abstractions. `apps/oshun/bff/src/middleware/authz.ts` turns the accepted token into `request.authContext.userId`. The BFF's current customer account implementation is the process-local `apps/oshun/bff/src/auth/customer-auth-store.ts`. | Every Rail read or write is BFF-authenticated and keyed by `request.authContext.userId`; the native shell may retain only the session material permitted by the existing surface policy.                                                               |
| Product entitlement         | `libs/oshun/auth/src/entitlements.ts` owns the tier/domain/feature model and `entitlement-context.ts` maps the customer profile into it. `apps/oshun/bff/src/middleware/entitlements.ts` resolves the signed-in account's plan before applying its restricted non-production header seam.                                                                                                                      | Face selection and channel access consume the BFF's entitlement result. The shell neither asserts nor caches an authoritative tier.                                                                                                                    |
| Grants and Rail preferences | Existing client stores such as `OshunPreferencesStore` persist surface preferences in caller-provided key-value storage; they are not an authoritative account graph. Rail subscriptions, loudness grants and pins, daypart overrides, discretion preferences, and ring opt-ins have no V1 persistence yet. The similarly named V2 cross-product grants in `libs/shared/identity` are a separate contract.     | Add user-owned records behind `apps/oshun/bff` and migrations under `libs/oshun/persistence/prisma/migrations`, using the V1 Prisma schema/client in `libs/oshun/persistence`. The shell is a client/cache only and owns no account-shaped Rail state. |
