This document describes how ODS is structured at the workspace level —
the three-layer mental model, why each layer exists, how data flows
from a spec to a rendered app, and what invariants must hold across
renderers. For contributor-level internals of each renderer, see the
per-framework ARCHITECTURE.md in
Frameworks/flutter-local/ and
Frameworks/react-web/.
┌─────────────────────────┐
│ Specification │ ← the contract
│ (JSON schema) │
└──────────┬──────────────┘
│ spec.json
▼
┌──────────────────────────────┐
│ Frameworks │ ← the renderers
│ ┌──────────┐ ┌──────────┐ │
│ │ React │ │ Flutter │ │
│ │ (web) │ │ (local) │ │
│ └──────────┘ └──────────┘ │
└──────────────┬───────────────┘
│ rendered UI + data
▼
┌──────────────────────────┐
│ Running App │
└──────────────────────────┘
┌─────────────────────────┐
│ BuildHelpers │ ← the assistants
│ (AI prompts/tools) │
└─────────────────────────┘
assists the builder in producing a valid spec.json
The Specification repo defines the JSON format. It has:
ods-schema.json — machine-readable schemaspec.md (in each framework, kept in sync) — human-readable
referenceExamples/ — working apps (todo list, customer feedback, kanban,
expense tracker, etc.) used both as documentation and as regression
fixturesTemplates/ — starter specs for common app shapesThemes/ — color palettes a spec can reference by nameThe spec is the contract. Any renderer that claims ODS conformance must render any valid spec in a way that produces equivalent observable behavior — not pixel-perfect output, but same data, same actions, same state transitions. See the conformance driver contract for how “equivalent” is enforced.
A framework is a runtime that parses a spec and renders it as an app. Two are implemented today:
React web (Frameworks/react-web/)
Flutter local (Frameworks/flutter-local/)
sqflite + sqflite_common_ffiBoth implement the same spec. That’s the whole point — a builder writes one spec and picks the runtime that fits their target.
ODS specs are JSON. Writing them by hand is fine for developers but friction for the “citizen developer” target audience. BuildHelpers are AI-assistant prompts (Claude and ChatGPT variants) that turn a conversational description of an app into a valid spec. They’re not part of the runtime; they’re authoring tooling.
spec.json ──▶ parser ──▶ OdsApp model ──▶ engine (state) ──▶ renderer ──▶ DOM / Widgets
│
▼
DataService / DataStore
│
▼
PocketBase / SQLite
Every framework follows the same pipeline:
OdsApp with OdsPage,
OdsComponent, OdsDataSource, etc.).DataService in React, DataStore in Flutter —
is the storage abstraction. It provisions collections/tables
lazily and runs queries/mutations.The interesting invariant: the parser’s output, the engine’s state shape, and the data layer’s I/O are the same across renderers. Only the renderer and the data layer’s backend are framework-specific.
The spec, the renderers, and the build helpers used to live in three
separate repos with different release cadences. We consolidated into
one monorepo (ods-pages) on 2026-04-20 — the upstream repos are
archived; their histories live there.
What changed our minds:
Frameworks/conformance/specs/
are read by both drivers from the same bytes. Three repos meant
duplicating them and watching them drift.What stays separate at the org level: each ODS family gets its own
repo (ods-pages, ods-chat planned, ods-workflow planned,
ods-game planned). Within a family, the spec + renderers + helpers
go together.
These hold across every renderer. A renderer that violates one is non-conformant.
onClick is a list of
action objects (navigate, submit, update, delete,
showMessage), not imperative code. Every renderer interprets the
same action list.dataSource
name. Tables/collections are provisioned automatically; the
spec author never sees “CREATE TABLE.”appPrefix. The same data-source
name in two apps does NOT share storage. Every runtime prepends a
sanitized app prefix to collection/table names.If you want to add a new component type (e.g., calendar, map),
the path is:
spec.md.The last step is what keeps the two frameworks honest.