api/internal/campaign/surface_rule library

The surface rule: nothing new ever replaces what's already on screen, and its live-test exception.

Pure and internal. Routing builds the SurfaceOccupants from the orchestrators and asks decideSurface; nothing here reads SDK state, so every cell of the table is testable on its own. The table is identical on Android and iOS — change all three together or not at all.

Classes

SurfaceBusy
Drop the arrival surface_busy; occupant is what blocked it.
SurfaceDecision
The answer for an organic arrival.
SurfaceOccupant
One occupant: a campaign routing accepted that has not settled yet.
SurfaceReplace
Inline, same slot, never displayed: settle occupant superseded and give the slot to the arrival.
SurfaceShow
Nothing in the way.

Enums

SurfaceArrival
The campaign that arrives — a row of the table. A floater (PiP or story) always arrives collapsed, so it has one row.
SurfaceKind
What is already on screen (or routed and still waiting to draw).

Functions

decideSurface(SurfaceArrival arrival, Iterable<SurfaceOccupant> occupants, {String? slot}) → SurfaceDecision
Decides an organic arrival against every occupant.
liveTestDisplaces(SurfaceArrival arrival, Iterable<SurfaceOccupant> occupants, {String? slot}) → List<SurfaceOccupant>
The live-test rule: a test is never busy. It dismisses every occupant the organic table would have it wait for — a blocking occupant, a collapsed floater under a floater test, its slot's inline occupant even if displayed — and keeps everything else. Real campaign or earlier test alike.