# Bounded browser windows

Olenji opens a session with a replaceable **200-turn window**. It does not download
the full source and all translation tracks just to show the latest turns. Earlier
and later navigation, full-session search, speaker filters and playback seeking
request another bounded window from the same durable database.

The current execution evidence is in [VALIDATION.md](/docs/1.4.0/source/#source-81cf95d5c07b3b7671cd). The earlier
[browser performance review](/docs/1.4.0/source/#source-3749160dc5e904c83e7b) is preserved with its original
full-snapshot behavior and measurements.

## Reading and ownership

`POST /api/sessions/{id}/window` reads source, connected translation groups,
speaker context, a notes page, metadata and their event cursor in one SQLite read
transaction. `window_ids` identifies the turns to display. `segments` can also
contain source context required to interpret a translation group correctly.
Context rows are not additional search matches or visible turns.

Translation context follows connected groups in the requested language, including
stale records that can still determine human/current/latest priority. The browser
uses the same stable ordering as complete snapshots. A translation spanning a
page boundary remains one group; a human correction outside the visible page can
still protect its overlapping source turns.

| Request field | Meaning and limit |
| --- | --- |
| `limit`, `offset` | 1–500 visible matches; `offset:null` selects the latest window |
| `target_language` | Translation track to include and search; matching is case-insensitive |
| `search` | Literal case-insensitive Unicode substring in source or selected translation text; `%` and `_` remain literal characters |
| `speaker_ids` | Up to 500 fixed speaker IDs |
| `recent_speakers` | 1–20 most recent distinct source speakers when fixed IDs are absent |
| `committed_only` | Restrict visible matches to finalized source turns |
| `seek_seconds` / `anchor_id` | Choose one timeline or source anchor; the window includes up to 20 preceding matches |
| `expected_cursor` | Reject content changes since the previous page instead of silently mixing snapshots |
| `after` | Return an unchanged cursor when content is unchanged; use only with an identical query/window configuration |
| `annotation_offset`, `annotation_limit` | Independently page notes; browser default 50, maximum 100 |
| `speaker_offset`, `speaker_limit` | Independently page speaker metadata; browser default 50, maximum 100 |
| `view_id` | Optionally include one view belonging to this session; the browser ordinarily reads its view directly |

The response includes the snapshot-shaped fields, `window_ids`, `offset`, `limit`,
`total`, `next_offset`, `previous_offset`, `target_language`, `recent_speaker_ids`,
global `speaker_counts`, `annotations_page`, `speakers_page` and `complete`.
Speaker context includes referenced and explicitly filtered speakers, even if a
search returns no visible turns. Inspector counts describe the full session.

An explicit maximum of **1,000 connected translation tracks**, **5,000 additional
source context IDs** and an **8 MiB JSON response budget** bounds a window. Excess
complexity returns `WINDOW_TOO_COMPLEX` (409). The browser offers a smaller window
and export recovery. It never silently drops a protected group or falls back to
an unbounded snapshot. A single unusually dense group can still exceed the limit
even at one visible turn; exports retain complete source data.

## Search, navigation and live changes

Search covers the entire session, including words outside the current window.
Only the selected translation for a source group contributes translated-text
matches; suppressed overlapping text must not manufacture a match. Global
translation selection uses a disk-backed SQLite temporary table and bounded
Python row processing. It scans the selected language’s tracks, so search latency
remains a workload-dependent cost.

Page navigation sends the current cursor. If relevant content changed, the API
returns `SNAPSHOT_CHANGED`; the browser replaces the requested window at the new
cursor and explains that it refreshed. It does not accumulate pages. A traversal
under concurrent edits is a sequence of current consistent views, not a promise
to preserve a historical snapshot indefinitely.

Small complete unfiltered windows still apply suitable committed events locally.
Filtered or partial windows refresh on relevant content events because a changed
turn can enter or leave a filter, move between pages or alter translation
ownership. Growth beyond the small-window limits switches to a replacement read.
Unchanged recovery reads keep existing objects and DOM. Requests superseded by a
selection, filter, language or seek change are cancelled or ignored.

Agent view revisions are acknowledged after the requested presentation is loaded.
Rapid seek commands and Return to live supersede both old HTTP reads and queued
animation callbacks. The existing audio element remains owned during transcript
window changes. Automatic playback navigation waits for initialized media and
does not overtake a queued explicit seek. Session initialization owns its window
until the view and URL are attached; a concurrent note-save or background refresh
waits for that ownership to finish. New typing survives an older local view-setting response;
changing another control coalesces a pending local search.

Merge dialogs do not infer adjacency from visible or context rows. The bounded
`GET /api/sessions/{id}/segments/{segment_id}/neighbors` route returns the actual
source, previous and next rows plus their atomic cursor, ordered globally by
`(start,id)`. The merge still supplies both current revision preconditions, so a
later source edit fails safely. A late neighbor read cannot replace a newer dialog.

## Evidence and practical limits

Store tests use real SQLite transactions and a second connection that commits at
the selected read boundary. A 50,000-source/50,000-translation fixture checks
bounded decoding and distant translated-text search. Browser tests exercise
production assets with controlled HTTP/RAF/device objects; native browser checks
exercise the actual API, service and presentation separately.

Native Chromium memory figures are sampled JavaScript heap observations, not
whole-process memory, a guaranteed peak, or a long-duration leak proof. Linux
Chromium/Firefox/WebKit execution does not establish native macOS/Windows capture
or Safari application behavior. The [native handoff](/docs/1.4.0/source/#source-6567312d2db02a2200c5) retains
those hardware gates.
