GitHub – expo/exact · GitHub


.

  • Names are CSS’s, because names are what you write: font-size, object-fit,
    text-overflow, line-clamp, light-dark(), env(safe-area-inset-top). The compiler
    refuses an alias and names the web’s spelling.
  • Unsaid presentation is the platform’s. Text is the system’s label colour and size (17
    points on an iPhone, 13 on a Mac, the reader’s setting in a browser), a button is the
    system’s button, a pushed screen has the system’s Back. An app that says less looks
    more at home.
  • The browser is the parity oracle for layout and for what you wrote. Native layout
    given the same inputs, Canvas 2D, SVG, gradients, and authored motion are checked
    against what Chrome draws.
  • The web is also the development loop. You work on the seconds-long web loop, and the
    native hosts are swept behind you.
  • Any unavoidable deviation in layout is declared, with its reason. For example, Taffy
    has no position: static.
  • Writing the web doesn’t mean the lowest common denominator. Where a platform can do
    more (real HTML in an SVG foreignObject on the web, MapKit through a native module on
    Apple), Exact lets it.

    flowchart LR
      subgraph author["You write"]
        C["app.contract
    view · state · actions"] D["app.ts or a Rust crate
    data sources"] end C --> K["Contract compiler"] --> P["plan
    tables + bytecode"] P --> W["Web
    one ES module, ~20 KB runtime,
    the browser's DOM and CSS"] P --> R["Rust runner
    + layout kernel (Taffy)"] R --> M["macOS
    AppKit + CoreText"] R --> I["iOS
    UIKit + CoreText"] R --> L["Linux
    vello GPU / tiny-skia CPU"] D -.->|answers, checked against declared shapes| W D -.-> R



    Loading


    • Contract (contract/) is compiled in four passes: syntax, types, analysis, and
      lowering. At build time the plan is baked: constant data is evaluated into it, so
      the first frame needs nothing else.
    • The runner (runner/) is the plan’s virtual machine. It handles keyed component
      instances, events, timers on a seekable clock, and the data seam.
    • The kernel (kernel/) is a columnar node arena with CSS layout through a patched
      Taffy. The host measures text: CoreText on Apple,
      cosmic-text on Linux. On the web, the browser lays out the same CSS itself, so the web
      build carries no layout engine.
    • Hosts (host/) present the tree. The web host writes DOM nodes, and the browser
      runs CSS transitions. The Apple host is a static library with a C ABI under AppKit and
      UIKit presenters. The Linux host paints the tree itself, onto DRM/KMS with evdev
      input, or into an offscreen buffer.
    • Data sources sit below one seam. TypeScript runs on Hermes on native hosts and in
      the browser’s own engine on the web. Rust runs natively, and can be replaced live
      (as a shared library, or as Wasm on iOS and the web). Every answer is checked against
      the shape Contract declared. Grants control network, files, SQLite, and secrets.
    • Optional pieces are separate artifacts: a wgpu GPU module (Metal on Apple, WebGPU
      in the browser), native modules (a hyphenated tag backed by a platform widget, like
      MapKit), and a game engine add-on.

    These steps were run on macOS on Apple Silicon. Linux works for the web loop and the
    Linux host.

    • Rust through rustup. The pinned stable toolchain in
      rust-toolchain.toml installs itself the first time Cargo runs.

    • Bun 1.4.2, the version package.json pins. Node and npm are
      not needed.

    • Google Chrome. The agent drives the web through headless Chrome. Set CHROME to
      use another Chromium.

    • Xcode, for the macOS and iOS hosts.

    • Hermes, only for TypeScript apps on native hosts. Exact consumes Ibex’s
      pinned, attested 260318099.0.4 release bundle; ordinary Cargo builds are
      offline and never download or compile an engine. Install it once for each
      target before building (for this Mac, use aarch64-apple-darwin):

      cargo run --manifest-path vendor/ibex/crates/hermes-lean-sys-installer/Cargo.toml -- --target <triple>

      The installer verifies the bundle and publishes it under
      ~/.cargo/hermes-lean-sys/. The v4 set supports macOS, Linux, Windows, iOS
      devices and the universal iOS Simulator, plus tvOS devices and arm64 tvOS
      Simulators. Linux and Windows select Ibex’s English intl tier; Apple keeps
      Hermes’s OS-backed Intl.
      HERMES_LEAN_SYS_DIR is only an explicit development
      override, not normal setup.

    To install the pinned Bun beside any existing installation:
    curl -fsSL https://bun.sh/install | BUN_INSTALL=~/.bun-1.4.2 bash -s bun-v1.4.2.
    Use ~/.bun-1.4.2/bin/bun for the commands below if it is not on your PATH.
    bun scripts/exact.mjs setup installs the declared stable and web nightly Rust
    toolchains, their components/targets (the nightly’s clippy lints a game’s web bake),
    matching wasm-bindgen, pinned Binaryen and Bun dependencies, installs the pinned
    Hermes host bundle and the iOS/tvOS bundles this Mac builds, and fetches the crates
    of exact2’s lock and the game SDK’s (game/app/shells.lock), since every bake
    resolves offline. It keeps Binaryen in ~/.cache/exact/binaryen; builds find it
    automatically. setup --check invokes Ibex’s installer in its offline check mode for
    that same target set. The resolver authenticates the canonical receipts, archives,
    compiler and host/target HBC pairing without installing anything; its exit status and
    diagnostic are the check’s result.

    2. Run Caltrain in the browser

    git clone https://github.com/expo/exact.git
    cd exact
    bun scripts/exact.mjs setup
    bun host/web/dev.mjs            # Caltrain, at http://127.0.0.1:8765/

    The first run compiles the toolchain, which takes a few minutes. After that, open
    apps/caltrain/app.contract, change some text, and save.
    The page rebuilds and reloads in about a tenth of a second. Add --lan to open the
    same page from a phone on your network. The server answers only to the names it
    prints (a DNS-rebinding guard; any other Host gets 421); to share it through a
    tunnel, name the tunnel’s host with --allow-host
    (repeatable; name:port pins a port). An allowed name reaches the page and its
    reload stream, never the local installer’s token.

    bun host/apple/build.mjs --run              # macOS
    bun host/apple/build.mjs --ios --run        # an iOS Simulator (--device --run for a connected iPhone)
    cargo build --profile host-dev -p caltrain-linux   # Linux: a DRM/KMS console, or headless anywhere (--release to ship)

    A first native build takes ten to fifteen minutes on a laptop; later builds reuse it
    and take a minute or two. Watch its output rather than waiting blind. iOS commands use an
    iPhone simulator that’s already booted, or boot the newest iPhone Pro. To choose one,
    set EXACT_SIM to its name or UDID (or pass --sim to build.mjs), and keep the same
    setting for agent.mjs ios.

    On Linux, a development build can serve its screen with EXACT_VNC=1 and
    EXACT_VNC_PASSWORD set to 1–8 bytes. It listens on 127.0.0.1:5900;
    an explicit EXACT_VNC=host:port selects another address. Production bakes
    ignore both variables.

    4. Drive it the way an agent does

    bun host/web/build.mjs caltrain-web         # the build the agent's web host serves
    bun scripts/agent.mjs web tree "tap change-station" "type station-search Palo" state "screenshot out.png"
    bun scripts/agent.mjs web --test apps/caltrain/app.test.contract

    Swap web for macos, ios, or linux once that host is built. Caltrain’s three
    tests take about two seconds on the web host. Each operation is one shell argument, so
    quote it: "type new-todo Buy milk" types Buy milk (everything after the target is
    the text). "screenshot film.png over 600 every 50"
    films motion as a contact sheet; use an .apng name to get an animation.

    bun scripts/exact.mjs new ../hello          # or run `bun link` once, then `exact new ../hello`
    cd ../hello
    bun exact.mjs contract types app.contract -o app.contract.d.ts   # first run builds the compiler
    bun exact.mjs web                           # the dev loop, at http://127.0.0.1:8765/
    bun exact.mjs test web tests/*.test.contract # the app's *.test.contract, or the files named
    bun exact.mjs mac --run                     # this Mac
    bun exact.mjs ios --run                     # an iOS Simulator

    exact new creates a standalone app: app.contract (the view), app.ts (its data),
    app.json (the manifest: name, bundle id, hosts, deploy policy), and small web/ and
    apple/ host crates. Its AGENTS.md (and CLAUDE.md) points a coding agent at
    docs/start-here.md, the one guide it must read, and at bun exact.mjs help for the commands, including bun exact.mjs contract … for the compiler
    and contract vocab for the card of what an app writes (--all: every tag and property Contract accepts). Before any of
    it, run bun scripts/exact.mjs setup once; setup --check names everything this machine
    is missing without changing it. It has its own Cargo workspace, which uses your exact2 checkout
    by path. To drive it from exact2, point EXACT_APP_DIR at it:

    EXACT_APP_DIR=../hello bun host/web/build.mjs hello-web
    EXACT_APP_DIR=../hello bun scripts/agent.mjs web --app hello tree "screenshot hello.png"

    Look at every screenshot, not just the test result: a test passes on a layout that
    spills off the screen. bun exact.mjs agent "screenshot out.png" shows the app
    on each host in its own session; for a copy you launched yourself, use
    xcrun simctl io screenshot out.png on iOS.

    bun scripts/exact.mjs also runs apps from this repository as real Mac apps:
    exact run markdown README.md, or exact install markdown to put mdview on your
    PATH. exact list shows what’s here.

    An app’s AGENTS.md asks its agent to record friction in one line, bun exact.mjs feedback note "". A failed or slow drive, build or verify, and a passing
    test or verify, end with one line naming it, at most once a day per project. A note stays in the app’s
    .exact/ with exact2’s commit, the last command’s verb, exit code and duration, and the
    OS; never any command output, or the app’s code or files. It goes to Expo (https://exact-diaries.ccheever-0f6.workers.dev) only when
    the project’s answer allows: always, or a yes to ask, the default, after
    bun exact.mjs feedback shows exactly what would go. bun exact.mjs feedback never
    stops it for a project; EXACT_FEEDBACK=0 silences the nudges and refuses every send
    on the machine. docs/diary.md has the details.

    An agent building an app starts at start-here.md, the only
    required reading, then looks up the recipe for each question. For more, read the complete guide for humans,
    or look things up in the guide for agents or the
    grammar and vocabulary reference.

    Contract describes what an app shows and how its state changes. It doesn’t fetch, read
    files, or run arbitrary code. That’s what data sources are for. Here is a complete todo
    app: a Contract file, a TypeScript file, and a test. This exact app was built for web,
    macOS, and the iOS Simulator, and its test passed on all three.

    To try it in an app created with exact new, replace app.contract and
    app.test.contract with the examples below. In app.ts, keep the generated
    export const appId = … line and replace the rest with the TypeScript below.

    // app.contract: the view, its state, and what each action changes. It names no colour,
    // font or control size: each host draws its own heading, field, button, checkbox and list.
    shape Todo
      id: string
      title: string
      done: bool
    
    shape Change
      count: number
    
    component Todos
      state draft = ""
      mutation changed as shape Change refreshes todos
      resource todos = todos() as shape Todo[]
      derive left = todos.filter(t => not t.done).length
    
      action edit(value: string)
        draft = value
      action add
        if draft.trim() != ""
          send changed = addTodo(draft.trim())
          draft = ""
      action toggle(id: string, done: bool)
        send changed = setDone(id, done)
      action remove(id: string)
        send changed = removeTodo(id)
    
      view
        list appearance="auto" height="100%"
          section
            header
              text `${left} left` role="heading" testId="count"
            row
              input value=draft input=edit submit=add placeholder="What needs doing?" aria-label="New todo" testId="new-todo" flex=1 min-width=0
              button press=add buttonStyle="filled" testId="add"
                text "Add"
          section
            for t in todos key=t.id
              row testId=`todo-${t.id}`
                input type="checkbox" checked=t.done change=toggle(t.id) aria-label=t.title testId=`toggle-${t.id}`
                text t.title flex=1
                button press=remove(t.id) destructive=true testId=`remove-${t.id}`
                  text "Delete"
    
    todos,
    addTodo: (GitHub – expo/exact · GitHub) => {
    todos = […todos, { id: String(next++), title, done: false }];
    return { count: todos.length };
    },
    setDone: ([id, done]) => {
    todos = todos.map((t) => (t.id === id ? { …t, done } : t));
    return { count: todos.length };
    },
    removeTodo: ([id]) => {
    todos = todos.filter((t) => t.id !== id);
    return { count: todos.length };
    },
    };

    export const answer: Answer = (source, args, store, storage, native) =>
    sources[source](args, store, storage, native);”>

    // app.ts: keeps the list, and answers what app.contract asks for
    import type { Answer, Sources } from './app.contract.d.ts';
    
    export const grants = '';  // e.g. 'net.fetch https://api.example.com'
    
    type Todo = { id: string; title: string; done: boolean };
    let todos: Todo[] = [];
    let next = 1;
    
    const sources: Sources = {
      todos: () => todos,
      addTodo: ([title]) => {
        todos = [...todos, { id: String(next++), title, done: false }];
        return { count: todos.length };
      },
      setDone: ([id, done]) => {
        todos = todos.map((t) => (t.id === id ? { ...t, done } : t));
        return { count: todos.length };
      },
      removeTodo: ([id]) => {
        todos = todos.filter((t) => t.id !== id);
        return { count: todos.length };
      },
    };
    
    export const answer: Answer = (source, args, store, storage, native) =>
      sources[source](args, store, storage, native);
    // app.test.contract: runs on any host with `scripts/agent.mjs  --test`
    test "add, finish and delete"
      expect text "count" == "0 left"
      type "new-todo" "Buy milk"
      tap "add"
      type "new-todo" "Walk the dog"
      type "new-todo" key "Enter"
      expect text "count" == "2 left"
      tap "toggle-1"
      expect text "count" == "1 left"
      tap "remove-1"
      expect tree missing "todo-1"
      expect tree has "todo-2"
    

    The view never edits the list itself. An action sends a mutation to a source,
    app.ts changes its data and answers, and refreshes todos asks for the list again.
    Data lives in one place, and every change to it is a named, typed call an agent can see.
    This list lives in memory. To keep it across launches, give app.ts a grant like
    sqlite.open app:/data/todos.db and use storage.sqlite, as
    Fieldnotes does.

    Concept What it is
    shape A closed record type. Fields are number, string, bool, another shape, option, or T[].
    component A unit of UI. The first one in a file is the root. Components take props, can inject what an ancestor provides, and can fill a slot with children.
    state A value the component owns, its type inferred or declared (state picked: option = none). Under a for, child state belongs to that keyed row.
    derive A value computed from others, recomputed when they change.
    resource Data from a source: resource x = source(args) as shape T. When the arguments change, the source is asked again.
    mutation / send A change made through a source: send x = source(args). refreshes r asks resource r again afterward, pending(x) and failed(x) show progress, and failure(x) says why a resource failed.
    action The only place state changes. What it writes is inferred from its body, and let binds a local inside it.
    task Work on a schedule: every(1000, tick), after(ms, a), every(frame, a).
    view Indented elements, if … else, for … key=… (a key is required), match over options, and calls to other components.
    style / class= A named set of CSS properties. The node’s own attributes win, and there is no cascade.
    fn, xs.map / xs.filter / xs.join Pure, single-expression helpers. Recursion is refused.
    Built-in functions The web’s string and array methods by their names, called on the value (xs.length, s.trim(), s.includes(t), xs.at(-1)), and functions (first(xs), formatDate, formatNumber); all are listed with their types under stdlib in plan/tables/format.json.
    routes A router: a location and a retained stack per tab, moved with open, push, replace, and back, and read with top.
    font Declares a font family and binds it to files at build time.
    use … from "./x.contract" Imports components, shapes, styles, and fns from another Contract file.
    testId A stable name for the agent, tests, and accessibility tools.

    What Contract leaves out, and why

    • No JavaScript in the view, and no I/O. Expressions have no effects. Effects
      happen only in actions, as assignments, send, refresh, or commands like
      focus(…) and share(…). Data crosses one seam: a source answers, and the runner
      checks the answer.
    • No loops in actions, no await. A list is a view’s keyed for, a computation is a derive or a
      fn (an action may name a value with let), and anything slower lives in a
      source. A list that changes changes where its data lives, through a mutation, as
      in the example above. That’s what lets the plan be baked, diffed, inspected, and
      executed the same way on four hosts.
    • No escape hatch; access hatches. Nothing steps outside the plan. Where an app
      needs a platform widget, it uses a native module: a hyphenated tag like
      exact-map, backed by Swift, Kotlin or Rust, laid out by the kernel like any other box.
      Where it needs the platform object Exact itself built (a UIView, a navigation
      controller, a DOM element), it marks the node hatch="word" and its native code is
      handed that object: an access hatch, the panel a machine is built with so its
      insides can be reached. It configures what Exact made and changes Contract state
      only by acting on authored nodes, as a person would.

    These limits serve the principles. An agent can’t wire up a data race it can’t write.
    A plan with no JavaScript in it starts fast. A view written in CSS’s own words lays out
    the same on every host and looks at home on each.

    $ cargo run -q -p contract -- build app.contract
    app.contract:4:5 [type-assign] `count` is `number`, cannot assign `string`
    app.contract:7:23 [lower-unknown-attr] `text` has no attribute `size`; `size` is spelled `font-size` here, the web's name

    One run reports up to twenty independent errors, not just the first. Other subcommands:
    --json for tools, symbols (definitions and references as JSON), fmt
    (source-preserving), types (generates app.contract.d.ts for TypeScript sources),
    and rust (shapes for a Rust data crate). The full language is specified in
    docs/contract-grammar.md.

    Each app lives in apps//. Run one on the web with
    bun host/web/dev.mjs --app , on macOS with
    bun host/apple/build.mjs -apple --run, and on iOS by adding --ios. All of
    these screenshots come from the web host, taken by scripts/agent.mjs.

    To learn from, start with Shelf, the recipe app. It names no colour and no
    font size, and scripts/no-tells.mjs holds it to that, so it shows what an app that
    leaves presentation to the platform looks like on each host. Then read these four for
    what each teaches. They were written before Exact left presentation to the platform
    and paint their own colours and sizes, so take their structure, not their style:

    App What it teaches
    Caltrain A Rust data crate, authored tests, routes, an optional GPU module
    Weatherlight TypeScript with fetch and grants
    Fieldnotes Storage: SQLite, files, backup and restore
    Recorder The microphone through @exact/media-capture and @exact/recording


    Weatherlight Spark Expose
    Caltrain
    The app that defines v1: nearby stations, live departure boards, search, theming, a Canvas 2D line map, and an aurora GPU sky. Rust data. Every host.
    Weatherlight
    Weather from Open-Meteo under a GPU-animated sky that follows the hour you pick. TypeScript data and a wgpu module.
    Spark
    A swipe deck: throw a card with your finger, and it flies off with your release velocity. Pan gestures and springs on every host.
    Expose
    A pretend phone OS (lock screen, home, Messages with an AI assistant, Reader) set in custom fonts. TypeScript. The assistant needs an OpenRouter key.
    Calendar Sparkline Photo Editor Video Player
    Calendar
    Month pages, draggable sheets, events dragged between days, wallpaper themes, SQLite. It lives outside apps/, so set EXACT_APP_DIR to run it.
    Sparkline
    A market list of animated SVG charts that draw in and pulse.
    Photo Editor
    Rotate, pan, and crop, through a native module.
    Video Player
    A bundled clip that shrinks out of the way when the keyboard opens.


    Exact Live Still, the interaction gallery
    Exact Live
    A creative-production workspace: a photo grid you reorder by dragging, a crew chat that can take a 10,000-message burst, and a Markdown runbook. Rust data.
    Still (Interaction Gallery)
    Drag to reorder, pan and zoom photos, a resizable sheet with a nested list, and 100 to 25,000 records.
    Reflow Textflow
    Reflow
    Seven typography studies after Cheng Lou’s Pretext demos: text flowing around moving balls, a dragon, masonry, and a magazine spread. Line heights come from font metrics read at build time.
    Textflow
    Text around shapes with CSS’s shape-outside and wrap-flow: draggable orbs, a dancer, an editorial layout. One Rust walker shared by every host.
    Markdown reader Conduit, the RealWorld app
    Markdown
    A Markdown reader that opens files from Finder or the command line (mdview README.md). Its sibling LLP (llpview) reads a folder of numbered design documents.
    Conduit (RealWorld)
    The RealWorld Medium clone against its hosted API. It’s the benchmark that compared Exact’s web build with React 19. Web only.


    SVG Gallery
    SVG Gallery: SVG as the browser renders it, one page per feature, held to Chrome’s pixels on every host.
    Canvas Gallery
    Canvas Gallery: the HTML Canvas 2D API drawn by the browser, Core Graphics, or tiny-skia, and compared to Chrome.

    More apps, labs, and stress tests

    App What it shows Notes
    Fieldnotes A local notebook with pins, search, SQLite storage, and JSON backup and restore TypeScript + Rust
    Messages A port of Expo’s chat demo, talking to models through OpenRouter Needs bun apps/messages/service.ts and an OpenRouter key
    Type Tour Phone-OS screens as a type specimen for the Expose fonts No JavaScript at all
    Map Demo Native modules: MapKit on Apple and OpenStreetMap on the web
    Carousel A horizontal virtualized list of 25,000 cards
    Motion Gallery Animated GIF and WebP images and CSS keyframes, compared across hosts
    Update Lab Live replacement of Contract, TypeScript, and Rust in a running app Hand-testing lab
    Messages Stress, Completion Storm, Markdown Stress 100k-message histories, 128 parallel requests, and 4 MiB documents, under load Opt-in workloads
    Native Fixture, Auth Fixture The native-module interface; OAuth with PAR, DPoP, and PKCE against a local server Test fixtures
    Beacons, Tennis, … Games on the optional engine add-on: Rust gameplay, Contract menus bun game/dev.mjs beacons

    What works today, and what doesn’t yet

    The v1 bar: one real application, not a demo, runs from a
    single Contract source on web, macOS, iOS, and Linux, within the time budgets in
    rules/RULES.md.
    Caltrain is that application, and it runs on all four. Everything
    else here was admitted because a real app needed it.

    • Four hosts. Web, macOS (AppKit, no SwiftUI), iOS (UIKit, on the Simulator and
      on devices), and Linux (GPU or CPU painting, DRM/KMS or headless). Existing Swift apps
      can embed Exact sessions through ExactKit.
    • Layout and text. CSS block, flex, and grid; position: static and containing
      blocks; aspect-ratio; declared fonts; text around shapes.
    • Paint. Gradients, shadows, backdrop-filter, glass materials, complete SVG,
      Canvas 2D on every host, and a wgpu GPU canvas loaded after first pixel.
    • Motion. CSS transitions, springs, @keyframes, follow-and-release gestures (pan,
      swipe, pinch) with release velocity, layout transitions, exit animations, and
      scroll- or drag-linked timelines.
    • Lists. Virtualized lists, vertical or horizontal, one level deep. The rows of a
      vertical list are its platform’s: UIKit’s separators and highlight, a table on the Mac, one
      line between rows on the web, with nothing drawn by the app.
    • Controls and media. Inputs, select, a WYSIWYG Markdown editor on the web, macOS,
      and iOS, images including animated ones, video, pickers for an app’s declared file
      types, and share.
    • Data. TypeScript or Rust sources, optionally on a worker. Grant-checked fetch and
      streams, files and SQLite (native and in the browser), Keychain secrets, and OAuth
      sign-in. A router keeps a stack per tab.
    • Native modules. Platform widgets as hyphenated tags.
    • The web, server side. Pages pre-rendered at build time or per request by a Rust
      renderer, then adopted in place instead of hydrated.
    • Delivery. scripts/deploy.mjs publishes the web root and signed update bundles,
      which installed apps fetch after first pixel. Apple apps check every fifteen
      minutes while active under every activation policy; deploy.check.interval
      overrides the interval in seconds. Checks stage a candidate; deploy.activate
      controls when it applies. TypeScript and Rust logic can be
      replaced live in development. Every web build gets an install page, and
      exact release signs and notarizes a Mac app.
    • Agents and tests. The ten operations on every host, including a physical
      iPhone; Contract tests; source maps; filmed screenshots.
    • Windows. Apps default to ink on a DirectX 11 composition swap chain,
      or CPU when the manifest declares game. host.windows.painter: "ink"
      or "cpu" selects either painter, and EXACT_PAINTER overrides both.
      Skirmish is a standalone game built with Exact’s optional game engine;
      the CPU default preserves its module-owned window presentation.
      Windows Desk demonstrates
      native controls over Contract and the kernel with an app-local Win32 presenter;
      general Windows control parity and delivery remain unfinished.
    • Android. Early. An app builds for Android and runs on an emulator or a phone:
      bun exact.mjs android builds it, bun exact.mjs test android runs its tests, and
      bun exact.mjs agent android … drives it. It needs the Android SDK, the Rust target aarch64-linux-android, and the Hermes Android bundle
      (bun scripts/hermes-android.mjs build) until an Ibex release ships one. Delivery and
      store packaging don’t exist yet.
    • No JSX or React tier. Nothing runs JavaScript above the data seam. The door stays
      open, but no one is building it.
    • TypeScript can’t import npm packages’ code yet. app.ts imports its own local
      files; from a package it imports only types (import type), which the build checks
      and never runs.
    • Code updates in production. Signed delivery covers plans and assets. Signed
      delivery of TypeScript or Rust modules isn’t implemented, and app-store rules limit
      it anyway.
    • Linux editing. The web and Apple hosts edit; Linux reads. Linux also has no
      word segmenter, so flowed Thai, Lao, Khmer, and Myanmar text breaks only at spaces.
    • Out on purpose for v1. Camera access, a gesture arena, general layout animation,
      grid or masonry virtualization, a general webview beyond iframe, and a devtools UI.
    • No packaged CLI. You work from a source checkout. exact new apps refer to it by
      path.

    Path What’s there
    kernel/ exact-kernel: the node arena, wire frames, transactions, Taffy layout, and the export. kernel/tables/schema.json declares every node type, property, and opcode.
    contract/ The Contract compiler (syntax → types → analyze → lower), its CLI, and the corpus
    plan/, runner/ The plan format, generated from one JSON authority, and the VM that runs it
    motion/ CSS transitions, springs, and the seekable clock for non-web hosts
    host/ web/, web-js/ (the JS target), apple/ (Rust core, ExactKit Swift package, AppKit and UIKit), linux/, render/ (server pages), plus update adapters and rasterizers
    js/, logic/, data/ The TypeScript executor (Hermes, and the browser’s engine on the web), Rust logic modules, and the data seam
    gpu/, canvas/, textflow/, markdown/ Capabilities loaded on demand: the wgpu canvas, Canvas 2D, text around shapes, and the Markdown editor
    route/, update/, bake/, filesystem/ The router, the signed update store, delivery baking, and app-owned files
    apps/, examples/, game/ The apps above, the Calendar example, and the optional game engine
    scripts/ exact.mjs, agent.mjs, smoke.mjs, metrics.mjs, deploy.mjs, and the checks
    docs/ Guides for app authors: start here, the Contract grammar, recipes, and pitfalls
    rules/ RULES.md, how work on Exact happens
    vendor/taffy/ Taffy, plus Exact’s patches

    Start with rules/RULES.md, one page on how work happens here. Agents
    should also read AGENTS.md. In short:

    • Six blocking checks, 65 seconds in total. Anything slower runs asynchronously,
      per commit.

      cargo build --all-targets --keep-going
      cargo test --tests --no-fail-fast
      cargo clippy --all-targets --keep-going -- -D warnings && cargo fmt --all -- --check
      bun scripts/caps.mjs     # budgets: 1,500 lines per source file, document caps
      bun scripts/boot.mjs     # counts the module graph before first pixel
      cargo check --workspace --all-targets --keep-going   # every crate compiles

      The first three build and test Cargo’s default-members. The last compiles every
      other crate too, so a host or an app outside them cannot stop compiling unseen. It
      needs the pinned Bun (package.json) first on PATH: app crates’ build scripts run it.

    • Verify by running, not by reading. Build the app a change touches and drive it:
      bun scripts/smoke.mjs drives the whole app on that host.

    • Delete; don’t deprecate. Before 1.0 there are no compatibility shims. Generated
      files are built, never committed.

    • Agents remove apparatus freely and add none (checks, scripts, registries, design
      documents) without a person saying so.

    • A spec gets written only when it has an implementer and a date.

    Detailed operational notes cover toolchains, the Contract CLI’s JSON formats, source
    maps, TypeScript and Rust data modules, storage, live replacement, delivery trust, the
    install page, and the stress harnesses. They’re in
    docs/reference.md.

    The workspace is MIT-licensed (Cargo.toml).

    Exact’s Snapback4 adapter uses published 0.4.33 with the matching immutable
    Rust source revision 3653cae823145170df3e720f5e321bd1b5b4d79d.
    See the snapback4 package’s README for wiring and device rebuilds.



    Source link