---
type: Axis
title: pagination-style (result-windowing axis)
description: The pagination-style/result-windowing architecture axis — how a list endpoint windows large result sets (none → offset → cursor). A system-singleton axis whose commitment surface is the interface/wire contract (the pagination shape callers code against). DEEP (RFC #75 5A.4 / P1 #851 → promoted 4.5.0a6 #856); worked example is the swimlane lazy-scroll tier-3 windowing (#496) and the offset→cursor breaking-wire-change caution.
tags: [axis, architecture, pagination, paging, cursor, offset, windowing, rfc-75, deep]
timestamp: 2026-06-20T00:00:00Z
---

# Pagination-style (result-windowing axis)

> **DEEP axis (P1 #851 → 4.5.0a6 #856).** Full §6 depth with a worked example
> from the skill's own history — the swimlane UI's lazy-scroll tier-3 (#496)
> windows a long card list (live-verified chromium+firefox), and the recurring
> wire-contract caution is the offset→cursor break: a list surface that ships as
> 'return everything' and later needs deep concurrent-stable paging must version
> the contract, not silently swap it. Distinct from `searchability` (which access
> *paths* find a record) and `quota` (how much a consumer may *use*):
> pagination-style is the wire-contract shape by which a list endpoint hands back
> a *window* of a large result set.

## Concept

Pagination-style is the **result-windowing** axis (RFC #75 §3): how a list
endpoint returns a window of a large result set. The ladder runs none (return
the whole set; fine only while it is bounded and small) → offset
(`limit`/`offset` or page-number windowing; simple, but skips/duplicates under
concurrent writes and degrades on deep pages) → cursor (an opaque continuation
token over a stable sort key; stable under concurrent writes, scales to deep
pages, but the caller cannot jump to an arbitrary page). Its **commitment surface
is the interface/wire contract** (§4): the pagination shape is part of the API —
the offset↔cursor switch is a *breaking* wire-contract change (#496), since
callers code against page numbers or against opaque tokens, not both. Its **scope
is system-singleton** (§5): a system-wide convention for list endpoints, chosen
once. (It is system-singleton rather than per-entity because it is a property of
the *API style*, not of any one stored entity.)

## Why it is an axis (discriminator test — §2)

1. **Ordered tiers** — each rung handles a larger / more concurrent result set
   correctly (none ⊂ offset ⊂ cursor in robustness).
2. **Cross-cutting bundle** — a rung dictates the wire contract (the page
   parameters + the response envelope), the query (a stable sort key + an index
   for cursors), the client's paging loop, and the windowing tests.
3. **Asymmetric migration cost** — none → offset is additive, but offset →
   cursor is a *breaking* wire-contract change: every client's paging loop must
   be rewritten to thread an opaque token instead of incrementing a page number.
4. **Requirement-keyed selection** — falsifiable probes pick the rung.

## Schema view (the §6 entry — validated by `axis_catalog`)

```yaml
id: pagination-style
kind: architecture-axis
scope: system-singleton
selection:
  probes:
    - "Is the result set ALWAYS small + bounded? → none (return it all)."
    - "Is a simple page-number/offset window acceptable, with no concurrent-insert correctness need? → offset."
    - "Must paging be stable under concurrent inserts AND scale to deep pages? → cursor."
    - "Are clients seeing skipped/duplicated rows while paging a live list? → offset's failure mode → go cursor."
    - "Are you about to switch offset→cursor? → that is a BREAKING wire-contract change, version it."
composes_with:
  - "windowed search = pagination-style × searchability (paging a search result needs a stable sort)"
applies_when: []
tiers:
  - tier: none
    commitment_delta:
      surface: interface-wire-contract
      delta: "the endpoint returns the entire result set in one response. Correct only while the set is bounded and small; unbounded growth makes it a latency/memory hazard."
    admin_tool: "none."
    enforcement:
      - "the endpoint returns all rows; the contract makes no windowing promise."
    tests:
      - "the full set is returned; the bound on its size is documented/asserted."
    forecloses: "serving a large result set without a latency/memory blowup."
    migration_cost_to_next: "low — add limit/offset (or page) parameters + a total-count field (additive)."
  - tier: offset
    commitment_delta:
      surface: interface-wire-contract
      delta: "limit/offset (or page-number) windowing + a total count. Simple, allows jumping to a page; but concurrent inserts skip/duplicate rows and deep offsets get slow."
    admin_tool: "none beyond the query."
    enforcement:
      - "the endpoint honors limit/offset and returns the page plus a total; deep-page cost is accepted."
    tests:
      - "successive pages cover the set with the given limit; a known concurrent-insert skew is documented."
    forecloses: "stable paging under concurrent writes; cheap deep pagination."
    migration_cost_to_next: "medium — adopt an opaque cursor over a stable sort key — a BREAKING wire change (version the contract)."
  - tier: cursor
    commitment_delta:
      surface: interface-wire-contract
      delta: "an opaque continuation token over a stable sort key; the next page resumes after the cursor. Stable under concurrent inserts, scales to deep pages; no arbitrary-page jump."
    admin_tool: "cursor-decode/debug tool."
    enforcement:
      - "the endpoint returns a next-cursor; a follow-up resumes deterministically; the sort key is indexed + stable."
    tests:
      - "paging via the cursor covers the set without skip/duplicate even under concurrent inserts; an invalid cursor errors cleanly."
    forecloses: "jumping to an arbitrary page number; a total count may be unavailable."
    migration_cost_to_next: "n/a — top of the ladder."
examples:
  - "The skill's surfaces mostly sit at NONE — and that is the honest, correct rung for them, because the result sets are bounded. The swimlane composer returns a project's full card tree, /search returns a capped FTS result, the issue/discussion listings are one project's worth: small enough to ship whole, and the 'none' contract is honest because the bound is real. The one place result-windowing became a live problem was the swimlane UI's long card list, which the D6 lazy-scroll work (#496, tier-3, live-verified across chromium+firefox) windowed — a CLIENT-side incremental render rather than a wire-level offset/cursor, which is the telling detail: the bound was on render cost, not on an unbounded server set, so the fix lived at the view layer. The axis's load-bearing caution is the migration this skill has deliberately NOT made: if any list endpoint (say, an action-log or issue feed over a growing table) ever needed deep, concurrent-insert-stable paging, the none→offset step is additive but offset→cursor is a BREAKING wire-contract change — every consumer's paging loop must be rewritten from incrementing a page number to threading an opaque token, so it has to be a versioned contract bump (the api-evolution axis), not a silent swap. The skill has never paid that because no surface has crossed from bounded to unbounded; an action-log query returning `none` over a monotonically growing table would be the systems-analysis finding this axis flags."
```

## Selection procedure (§8)

Bidirectional check for the whole system per list endpoint convention. The
escalation pass asks "is the set unbounded? must paging be stable under
concurrent inserts?" — a yes forces offset then cursor. The offset→cursor move is
flagged as a breaking wire-contract change requiring a version bump. Recorded in
the system record; a list endpoint returning an unbounded set (`none` over a
growing table) is a systems-analysis finding (5A.5).

## Composition (§5)

Pagination-style × searchability = **windowed search** — paging a search result
needs a stable sort key the cursor can resume from; a full-text relevance sort
complicates the cursor (the score is not a stable, monotone key). Rule authored
in 5A.3.
