OllinDocs
Data flow & contracts

Live data & mock fallback

When the app serves real solves, when it fabricates, and how you always know which.

The app has two data sources and one rule: fabricated numbers are always labeled.

The switch

OLLIN_STUDY_API_URL decides:

  • Set and reachable → live fetches, server-side, cached per the API's immutable-stamp model.
  • Unset, unreachable, or any non-2xx → the deterministic local mock dataset. The fetch layer returns null on any failure and each fetcher falls back per-request, so a flaky API degrades gracefully rather than erroring.

Every fetch result carries source: "live" | "mock"; a page computes its overall source and renders the ribbon if anything on it is mock.

The demo ribbon

Amber, at the top of the affected screen: "Demo data — fabricated locally. Set OLLIN_STUDY_API_URL (or check that the API is reachable) to serve real solves." If you see it unexpectedly, the usual cause is the localhost/127.0.0.1 gotcha or an expired GCS token API-side (see the runbook).

One deliberate non-fallback: in live mode, a preflop line the live pack cannot walk is treated as an illegal line (redirect to the depth root), not a mock case — live and mock trees never mix on one screen.

What the mock dataset is

src/lib/api/mock/ fabricates a complete, deterministic product experience (seeded RNG — stable across reloads):

  • 21 postflop families across the 10–100bb ladder and all four line types (limp, SRP, 3-bet, 4-bet), each with the real 184-board canonical subset (src/lib/poker/boards.ts, drawn from the 1,755 iso-distinct flops) and per-combo root strategies generated from plausible policy shapes.
  • A full 8-max preflop state machine (mock/preflop.ts): blinds + 0.125bb antes, raise ladders, all-in closure — serving every depth of the ladder lazily in the exact pack-v1 node shape. Tight UTG, wide BTN, SB limp/raise mix, jam-or-fold at 10bb. Arriving ranges are exact products of taken-action frequencies, and HU boundaries bridge to the mock postflop families.

The mock exists so the product is fully walkable — every screen, every interaction — with zero infrastructure. It is not a strategy reference, and the ribbon makes sure it cannot be mistaken for one.

Testing

The mock layer is also the test substrate: the Vitest suite exercises view models and components against it deterministically, with no network. Run pnpm test (56 tests) and pnpm typecheck before any PR.

On this page