# Agent implementation contract

Hand this file and the reference project to an implementation agent. The
contract is intentionally stricter than a design brief: it defines the
observable result, legal ownership transitions, platform constraints, work
order and evidence required before the task may be called complete.

## Objective

Implement a feed-to-fullscreen media viewer for images and video with:

- tap-to-open from the currently visible feed rectangle;
- one continuous visual object during opening and closing;
- horizontal paging in the viewer;
- downward interactive dismissal from the entire viewport;
- cancellation back to the same mounted viewer;
- continuous video player state;
- return to the **current** source rectangle after feed scrolling, recycling,
  paging, rotation or inset changes;
- a defined fallback when the source cannot be recovered.

Integrate the neutral contracts in this project with the target repository's
own media, navigation and lifecycle models.

## Inputs the agent must identify before editing

Record these in the implementation PR or work log:

1. Feed list implementation and whether it virtualises or recycles rows.
2. Stable item identity and stable per-media identity.
3. Inline media component and its crop policy.
4. Fullscreen navigation/container and whether it creates a separate native
   window.
5. Pager implementation and active-page event.
6. Video player library, exact version and player/surface lifetime.
7. iOS minimum target, Android API target and New Architecture setting.
8. Existing gesture roots and any interactive media controls.
9. App background/foreground hooks.
10. Available device matrix and screen-recording method.

Stop if identity is only a URI or array index. Define the stable key first.

## Non-negotiable invariants

1. Exactly one representation owns presentation in every phase.
2. Player ownership and presentation ownership are separate.
3. Every async result is gated by monotonic session token + media key.
4. A surface callback is additionally gated by expected surface ID.
5. Opening geometry is never reused as the final return target.
6. A committed close performs reveal → next paint → current measurement.
7. Cancellation keeps the session, pager page, player and viewer mount.
8. `readyToPlay`, time progress and `playing` do not prove visible pixels.
9. A replacement destination clears its poster only after a first-frame signal.
10. A same-surface Teleport path may carry an already-proven native surface;
    document this as the reason no replacement-surface gate is required.
11. Android must not mount multiple `VideoView` instances against one
    `VideoPlayer`.
12. The source layout slot remains mounted while its pixels are elsewhere.
13. A recycled row can never receive a return intended for its former item.
14. Timeout recovery restores a legal owner; it never silently reports success.
15. Screen evidence, not command events alone, decides visual acceptance.

## Required ownership phases

The implementation may rename phases, but it must represent these states:

```text
idle
→ measuring source
→ opening
→ preparing viewer
→ settled
→ dragging
→ cancelling → settled
→ resolving return target
→ closing
→ handing back
→ idle
```

There must also be a failure/recovery state.

The required owners are:

| Phase | Presentation | Playback | Geometry |
| --- | --- | --- | --- |
| Idle | source | inline session | source registry |
| Measuring | source | inline session | measured source + token |
| Opening | transport | same player session | locked open plan |
| Preparing | transport or viewer poster | same player | destination surface |
| Settled | viewer | active viewer page | viewer layout |
| Dragging | viewer drag stage | same player | gesture shared values |
| Cancelling | viewer drag stage | same player | cancellation spring |
| Resolving return | viewer | same player | source registry |
| Closing | transport | same player until policy handback | locked return plan |
| Handing back | source | explicit inline/viewer policy | current source |

## Required component boundaries

Preserve these responsibilities even if target names differ:

- **coordinator/store:** token allocation, legal phase changes, ownership;
- **source registry:** register, reveal and measure current sources by key;
- **video session registry:** own player creation, replacement and release;
- **routed video surface:** hold one `VideoView` and select a Teleport host;
- **transport layer:** animate source/destination geometry outside list clipping;
- **viewer/pager:** page identity and full-viewport interaction;
- **release policy:** pure distance/velocity decision;
- **platform adapter:** optional native live-layer movement only;
- **evidence recorder:** phase, owner, key, surface and timing events.

Do not put player creation in both inline and fullscreen components. Do not let
feed rows subscribe to per-frame gesture progress.

## Implementation order

Complete and verify each stage before continuing:

1. **Identity:** tests prove two items sharing a URI remain different.
2. **Source registry:** current mounted source measures in window coordinates.
3. **Static image open:** source slot stays fixed; no clipping or duplicate.
4. **Static image close:** fresh return measurement is used.
5. **Token gates:** deliberately deliver stale callbacks; they do nothing.
6. **State machine:** illegal phase/owner combinations fail loudly in dev.
7. **Video registry:** one app-owned player survives surface movement.
8. **Teleport routing:** one `VideoView` visits inline, overlay and viewer hosts.
9. **First-frame policy:** poster only yields to a proven destination surface,
   or to the same already-proven teleported surface.
10. **Pager:** adjacent pages are posters; only active video owns the player.
11. **Gesture arena:** horizontal intent reaches pager; downward intent reaches
    dismissal; controls keep priority.
12. **Cancellation:** ten cancel cycles preserve player ID and page.
13. **Recycled return:** source is revealed and remeasured by identity.
14. **Lifecycle:** background every phase and recover one legal owner.
15. **Platform proof:** run the required device matrix and inspect recordings.

Do not begin cosmetic tuning before stages 1–9 pass.

## Teleport-specific rules

- New Architecture/Fabric must be enabled.
- Use a native development build; Expo Go is insufficient.
- Keep the routed component outside the virtualised row when row unmounting
  could destroy it. Rows provide named inline `PortalHost`s.
- The overlay host lives above list clipping.
- The viewer host lives inside the active pager page.
- If a native modal creates another window, place the relevant host in that
  window.
- Keep the feed slot dimensions stable while its native child is away.

## Video-specific rules

- Create the player with app-owned lifetime (`createVideoPlayer` for Expo Video)
  and call `release()` exactly once.
- Keep playback intent (`should be playing`) distinct from observed playback.
- Android transition surfaces use `textureView` when overlap/transform is
  required; choose at mount time.
- Disable the ExoPlayer shutter if the application poster owns readiness.
- Never infer first visible frame from status or time updates.
- Reset first-frame state on player/source/surface replacement.
- A second-surface architecture needs an expected-surface first-frame gate.
- A same-surface Teleport architecture needs proof that it is the same native
  surface, not merely the same React props.

## Gesture release policy

The release decision must be a pure function. The reference commits when
distance is decisive, or when a meaningful downward drag plus projected
velocity is decisive. It cancels ambiguous releases.

On cancel:

- spring the existing viewer transform to zero;
- preserve player identity, page identity and controls;
- do not run close cleanup;
- declare `settled` only after the spring finishes.

On commit:

- stop accepting a second close;
- resolve the current target;
- lock that return plan once the transport is underway;
- preserve the drag geometry at the first close frame.

## Failure handling

Implement bounded watchdogs for:

- source measurement;
- destination surface readiness;
- return target resolution;
- native transition completion;
- player replacement.

Every watchdog must choose one of:

- restore source ownership;
- complete viewer ownership under a poster;
- close with a direct fade;
- safely release the session.

It must never leave all representations hidden.

## Evidence events

Emit at least:

```text
OPEN_REQUESTED
SOURCE_MEASURED
TRANSPORT_MOUNTED
SOURCE_HIDDEN
OPEN_STARTED
VIEWER_HOST_READY
FIRST_VISIBLE_FRAME
VIEWER_OWNS_PRESENTATION
DRAG_STARTED
DRAG_CANCELLED | DISMISS_COMMITTED
RETURN_REVEAL_REQUESTED
RETURN_TARGET_MEASURED
CLOSE_STARTED
SOURCE_OWNS_PRESENTATION
SESSION_RELEASED
FAILURE_RECOVERY
```

Each event carries timestamp, session token, media key, phase, presentation
owner and surface ID where applicable.

## Required automated checks

- identity equality and stable key;
- contain/interpolation geometry endpoints;
- distance/velocity release decisions;
- stale token rejection;
- wrong media rejection;
- wrong expected-surface rejection;
- legal open path;
- cancellation returns to the same session;
- current return target required before close;
- reducer never admits an illegal phase/owner pair.

## Required device scenarios

Run every core scenario in `docs/device-proof.md`. At minimum:

- portrait image open/close;
- landscape image open/close;
- playing portrait and landscape video open/close;
- three cancelled drags then commit;
- pager page away/back;
- gesture beginning on letterbox;
- source scrolled offscreen before close;
- row recycled for another item before close;
- background during opening, settled playback, drag and close;
- immediate item A → close → item B;
- rotation/inset change while viewer is open.

## Definition of done

The task is complete only when:

- typecheck and contract tests pass;
- both iOS and Android native builds pass;
- the device matrix is filled with build/device identifiers;
- recordings contain no blank, duplicate, wrong-media or stale-poster frame;
- video player identity and playback intent survive open, cancel and return;
- stale events are visible in evidence as rejected;
- missing return source uses the documented fallback;
- implementation notes state whether the product uses one teleported surface or
  a platform adapter;
- dependency versions and platform-adapter assumptions are documented.

If any one of these is unproven, report it as remaining work rather than
softening the acceptance language.
