# Claims ledger: "Undra 1.0: one Rust core under native apps"

Post: `site/blog/undra-1-0/index.html`, published 2026-10-03. Piece: `launch-site`. Written against `main` `005790c` (`.10x/status.md`
checkpoint 34). Same purpose and shape as `site/blog/why-undra-is-the-default-choice/claims.md`: anyone can check every factual sentence
of the post quickly. Every number the post shows that a record can supply is a generated slot, not typed: `<!--measured:NAME-->` (filled by
`site/scripts/build-numbers.mjs` from `site/data/bench.json` and `bench/results/*.jsonl`) or `<!--trust:NAME-->` (filled by
`site/scripts/build-trust.mjs` from `site/data/tests.json` and `contract-tests/scenarios.md`), so the post moves with the records.

**Fact-checked adversarially on 2026-10-03** against `main` `a547226`; the pass is recorded in `.10x/reviews/2026-10-03-launch-posts-fact-check.md`. The last
column is the fact-check: the source was opened and the post compared with it, digit for digit and condition for condition; the three web sizes
were also measured again on `a547226` with `scripts/wasm-size.sh`. **verified** means the source says what the post says; **corrected** says what
was wrong and how the post changed (the earlier wording is in italics); **added** means a claim the draft made without a row. Rows L24, L26, L29,
L36 and L37 are sentences that depend on the release existing, which on 2026-10-03 it does not (no GitHub release, tag, npm or
Maven Central package); the post describes launch-day commands as such.

**Aliases.** `res` = `bench/RESULTS.md`. `adr-NNN` = `.10x/adrs/ADR-NNN-*.md`. `stat` = `.10x/status.md`. `rm` = `site/data/roadmap.json`. `res` line numbers are of `bench/RESULTS.md` at `a547226`.
`gs` = `site/docs/getting-started.html`. **Checked by**: `A` the author opened the path; `R` the author ran it (the run of 2026-10-03:
`cargo build -p undra-cli`, then `undra init myapp` and `npm run dev` in an empty directory, recorded in `.10x/decisions/sde/launch-site.md`);
`J` the author's judgement, worded as such.

| ID | Claim | Source | Checked by | Fact-check (2026-10-03) |
|---|---|---|---|---|
| L01 | Undra is a Rust framework for domain logic, reactive state, the data layer and persistence; the UI stays SwiftUI, Compose, React and React Native | `CLAUDE.md` line 3; `README.md` lines 1-30 | A | **verified** `CLAUDE.md:3`, `README.md:1-30` |
| L02 | The macros describe the marked types as a schema; `undra bindgen` turns it into each platform's API | `CLAUDE.md` R1 (line 12); `site/docs/cli.html` `#undra-bindgen` | A | **verified** `CLAUDE.md:12` (R1); `site/docs/cli.html#undra-bindgen` |
| L03 | an `@Observable` class for SwiftUI, `StateFlow`s for Compose, hooks for React and React Native | `README.md` lines 22, 125, 136, 142-144 | A | **verified** `README.md:22`, `125`, `136`, `142-144` |
| L04 | A read is a property access and never calls into Rust; a write crosses once per transaction and one binary change-set patches every mirror | `CLAUDE.md` R5 (line 16); `site/blog/reads-never-cross-the-boundary/` | A | **verified** `CLAUDE.md:16` (R5) |
| L05 | The home page's diagram can be tapped | `site/assets/hero.js` (`tap`), this piece | R | **verified** `site/assets/hero.js:6-10`, `198-210` (each screen is a `role="button"`; a tap makes the next write leave from it) |
| L06 | In a release build on the reference machine, an Apple M5 Pro and not a phone, a synchronous call into the core costs 49.8 ns on the core side (slot `row-call-sync`). *Was: no build or device* | `site/data/bench.json` row `call-sync` (line 8); `res` line 54 (`boundary/call_sync/add` 49.8 ns) | A | **corrected** (conditions): `res:54`, `res:761` (median 49.8 ns, interval 49.6 to 50.0; `res:23` the `bench` profile, release, fat LTO). The figure predates ADR-044's per-core function table, whose cost is "about a nanosecond" (`adr-044:102`) and not settled (`why-undra-is-the-default-choice/claims.md` B12); it is the number the README and the landing page print |
| L07 | A change-set of 100 signals costs 2.3 µs, core side (write, build, deliver), in the same release build on the same machine (slot `row-changeset-100`) | `site/data/bench.json` row `changeset-100` (line 48); `res` line 39 (2.30 µs) | A | **corrected** (conditions): `res:39` "core side: write, build, deliver", 2.30 µs; the draft did not say it is the core's half |
| L08 | The reference machine | `res` line 20 (Apple M5 Pro, 18 cores) | A | **verified** `res:20` (Apple M5 Pro, 18 cores) |
| L09 | For teams on two or more platforms that must agree on logic, state and data rules | the founder's brief for this piece; `.10x/specs/2026-10-01-v1x-default-choice-design.md` §0 | J | judgement, as worded |
| L10 | Written three times, those rules drift apart | judgement, the argument of `site/blog/why-undra-is-the-default-choice/` | J | judgement, as worded |
| L11 | It assumes a team that can carry Rust; the default-choice post weighs it | `site/blog/why-undra-is-the-default-choice/index.html` `#what-it-costs` | A | **verified** `why-undra-is-the-default-choice/index.html:167` (`id="what-it-costs"`); the post names Kotlin Multiplatform, UniFFI and "three times" (3 occurrences) |
| L12 | The core list: records, enums, typed errors, stores, signals, computeds, keyed and derived lists, objects and host callbacks, newtypes, decimals, declared generics | `rm` section "In 1.0", group "Core and bindings"; adr-039, adr-040, adr-041, adr-042, adr-058 | A | **verified** the eight items of the group (`site/data/roadmap.json`, section "In 1.0"); the alias said "Shipped", the section is titled "In 1.0" |
| L13 | The data layer list: queries and mutations with staleness, retry and optimistic updates that roll back; an offline queue; persistence that migrates stored data across schema changes and refuses what it cannot convert; paged and lazy lists; polling. *Was: "persistence that survives a schema change"* | `rm` section "In 1.0", group "Data"; adr-037, adr-043, adr-049; `site/docs/updates.html` | A | **corrected**: `site/docs/updates.html` ("What migrates for free": by name; anything not structural, such as a narrowing, a rename or a new field with no default, "needs a hook", and data that cannot be converted "is refused with a typed outcome"); "survives a schema change" was wider than that |
| L14 | Ten platform services with adapters and deterministic fakes; WebSocket, SSE and SQLite; the app's own OkHttp, URLSession or fetch | `README.md` line 205; adr-047, adr-048, adr-060 | A | **verified** `README.md:205` (ten ports), roadmap group "Ports" |
| L15 | `undra dev` keeps state across a rebuild, except in a web page started with `npm run dev`, which reloads and starts fresh; devtools with a change-set timeline; `undra schema diff` marks changes breaking or additive. *Was: no exception* | adr-053, adr-059, adr-054, adr-062; the run (`Restarted: … state kept`) | A, R | **corrected**: `.10x/decisions/sde/launch-site.md` "Product findings" (the Vite plugin full-reloads a page that runs on the remote core; the run: "the Vite plugin rebuilt the wasm and full-reloaded the page, which started fresh"; `UNDRA_SKIP_BUILD=1` keeps the page on the served core) and `runtimes/ts/@undra/runtime/src/vite.ts:18-20`; `site/docs/getting-started.html` says the same. `undra dev` itself: "Restarted ... state kept (1 store, 1 KiB, restored in 140 µs)" in the same run. The roadmap's "keeps the state and query handles" (group "Dev loop and tooling") has the same gap |
| L16 | SwiftUI, Compose, React and React Native; built from Xcode, Gradle, Vite or Bazel | `README.md` lines 185-192; adr-038, adr-061 | A | **verified** `README.md:185-192`; adr-038, adr-061 |
| L17 | iOS 15 and 16 get `ObservableObject` stores, proven by compilation and a probe on iOS 26.5, not on an iOS 15 or 16 runtime, simulator or device. *Was: "not on an iOS 15 or 16 device"* | `README.md` line 227; adr-045 | A | **corrected**: `README.md:225-227` says "not yet on an iOS 15 or 16 runtime", which covers the simulator as well as a device; the draft said "device" only |
| L18 | 35 contract scenarios, 101 of 101 cells; two scenarios run on TypeScript only (slots `scenarios`, `cells-of`) | `contract-tests/scenarios.md` (S01-S35, S21 and S22 TypeScript only: 33 × 3 + 2 = 101); `site/data/tests.json` line 12 | A | **verified** `contract-tests/scenarios.md:5`, `20` (S01 to S20 and S23 to S35 on every platform, S21 and S22 on TypeScript: 33 × 3 + 2 = 101); `build-trust.mjs` fails the build when `cellsPassing` differs from the cells the file defines. "Two run on TypeScript only" added to the post |
| L19 | 7,414 tests at the 2 October count, with more added since (slot `tests-total`) | `site/data/tests.json` lines 5-11 (3,689 + 1,864 + 881 + 870 + 110) | A | **corrected**: `site/data/tests.json` is "as of 2026-10-02, the reload-handles branch" (3,689 + 1,864 + 881 + 870 + 110 = 7,414), before ts-runtime-16k and the feedback wave landed: the runtime's own suite alone ran 2,030 tests at `rev:ts-runtime-16k:71`, `swift test` 900 at `rev:sse-chunks:77`. 7,414 is a floor, not the count of `main`; the post says so. `tests.json` and the landing card, README and the roadmap that repeat it are the integrator's to refresh |
| L20 | An adversarial review before every feature merged | `stat` checkpoints 5-34 (one review per feature row; two small fix pieces had none, `site/blog/why-undra-is-the-default-choice/claims.md` MI03) | A | **verified** `stat` checkpoints 27-34 (the pieces since checkpoint 26 each name a review or are CI-only: `ci-green`, `pr-gate`, `merge-queue`, `wt-pr-fix`); `cold-restore-regression` is reviewed (`.10x/handoff.md:59`); "feature" as in the default-choice ledger MI03 |
| L21 | The hello-world web core, the WebAssembly module, is 118.4 KB gzipped in the committed record, against a budget of 120 KB (slot `web-size`). *Was: "is 118.4 KB gzipped against 120 KB"* | `bench/results/web-size.jsonl` line 1 (118,409 B, budget 120,000) | A | **corrected**: `bench/results/web-size.jsonl` line 1 says 118,409 B, and the record is stale: it was taken before `cold-restore-regression` (#1, `773054f`) took serde's serializer out of every core (`.10x/decisions/sde/cold-restore-regression.md:72`, 116,224 to 111,355). `scripts/wasm-size.sh` on `a547226` (2026-10-03; rustc 1.99.0, wasm-opt 133, offline) measures **112,772 B (112.8 KB)**. The post says "in the committed record"; `scripts/wasm-size.sh --record` would correct the slot, `README.md:54`, the landing page and `site/data/bench.json` |
| L22 | The JavaScript runtime up front 15.8 KB against 16 KB (slot `web-runtime-js`) | `bench/results/web-size.jsonl` line 2 (15,774 B, budget 16,000) | A | **verified** `bench/results/web-size.jsonl` line 2 (15,774; budget 16,000); re-measured on `a547226`: 15,774 |
| L23 | The hello-world Android library for arm64-v8a is 898.2 KB, stripped and uncompressed, against 1.2 MB per ABI (slot `android-size`). *Was: "898.2 KB per ABI"* | `bench/results/native-size.jsonl` line 1 (898,176 B, budget 1,200,000) | A | **corrected**: `bench/results/native-size.jsonl` line 1 (898,176 B, arm64-v8a, "the shipped libhello_core.so (stripped, 16 KB aligned)"); x86_64 is 960,776 B (line 2), also under 1.2 MB; the two web figures are gzipped, this one is not |
| L24 | The three commands (the installer first), and that cargo works too (launch-day commands: depends on the release) | `docs/RELEASING.md` lines 8-9 | A, R (init and npm; the installer needs the release) | **verified** the commands and their order, `docs/RELEASING.md:8-9`; **not available yet**: no GitHub release, no tag (`gh release list` on 2026-10-03). Only `cargo install --git` works today. **Changed 2026-10-03 (`no-homebrew`)**: the draft's first command was a `brew install` from a tap; the launch has no Homebrew tap (ADR-063, amendment), so the installer is first and cargo second, and the sentence after the block now names the cargo command. Listed for the integrator |
| L25 | `undra init` writes a core with a to-do store, its bindings and an app per platform, downloading nothing | the run: `Created …/myapp (79 files, bindings for schema hash …)`; `gs` `#in-five-minutes` | R | **verified** the run (`Created …/myapp (79 files, bindings for schema hash …)`); the run used `--undra-path` (78 files) because the tag `undra init` pins does not exist (`sde:launch-site`, "Verified by running"); `site/docs/getting-started.html#in-five-minutes` |
| L26 | `npm run dev` builds the core to WebAssembly and serves the app at `http://localhost:5173` | the run: `==> Building the core for web (release)`, `Local: http://localhost:5173/` | R | **verified** the run (`==> Building the core for web (release)`, `web wasm 263.8 KB (gzip 112.8 KB)`, `Local: http://localhost:5173/`), with `npm install --offline` and the checkout's runtime. A fresh `npm install` resolves `@undra/runtime` from npm, where it is not published (`launch-dist` will resolve it from GitHub at the release tag): depends on the release |
| L27 | Edit `core/src/lib.rs` and save: the page is on the new core | the run: `undra: …/core/src/lib.rs changed, rebuilding the core`; `runtimes/ts/@undra/runtime/src/vite.ts` lines 18-20 (rebuild, full reload) | R | **verified** the run (`core/src/lib.rs changed, rebuilding the core`, then the page reloaded onto the new core); `vite.ts:18-20` |
| L28 | `undra dev` serves one core to every app at once | the run (banner: web, iOS, Android, JVM); `site/docs/cli.html` `#undra-dev` | R | **verified** the run (banner: web, iOS, Android, JVM); `site/docs/cli.html#undra-dev` |
| L29 | The iOS and Android apps get their runtimes from the same GitHub release, with no account: the Swift package at the repository's root and the Kotlin modules from JitPack | `adr-063`; `crates/undra-cli/src/dist.rs` (the addresses `undra init` writes: the repository URL, `MAVEN` = `com.github.shreypdev.undra`, `v<version>`, `https://jitpack.io`); `Package.swift` and `jitpack.yml` at the root; `packaging/rehearse-launch.sh` | R | **changed by `launch-dist`** (2026-10-03): the earlier sentence ("not yet published ... build against a checkout") stopped being true with ADR-063. Run: `packaging/rehearse-launch.sh` against a local stand-in for GitHub built the generated web, iOS (simulator, the Swift package resolved from the bare clone) and Android apps without a checkout (`.10x/decisions/sde/launch-dist.md`). Depends on the release existing: not yet run against github.com or JitPack's hosted build (`docs/RELEASING.md` steps 4 and 8) |
| L30 | Not a UI toolkit; shared UI not planned | `rm` lines 148-151 (`notPlanned`) | A | **verified** `site/data/roadmap.json:148-151` (the cited lines 149-150 were off by one) |
| L31 | A single-platform app does not need it | judgement | J | judgement, as worded |
| L32 | Every device number comes from the iPhone simulator, the Android emulator or headless Chromium | `res` line 849 ("Device numbers") and line 1025 ("Pending hardware"); `stat` checkpoint 34 ("no physical device has run any of it") | A | **verified** `res:849`, `res:1025` ("Pending hardware"); `stat:593` ("no physical device has run any of it"). The SSE figures of the week-of-outside-use post are a Mac's, say so there, and are not called device numbers |
| L33 | One team's work with no production users yet | `README.md` line 246 | A | **verified** `README.md:246` |
| L34 | The comparisons say where each tool is ahead | `site/blog/undra-vs-kotlin-multiplatform/`, `site/blog/undra-vs-uniffi-and-crux/` ("When Kotlin Multiplatform is the better choice", "When UniFFI is the better choice", "When Crux is the better choice") | A | **verified** the three headings exist: `undra-vs-kotlin-multiplatform/index.html:78`, `168`; `undra-vs-uniffi-and-crux/index.html:78`, `156`, `160` |
| L35 | The first `npm run dev` build compiles the Undra crates and takes a minute or two | `site/docs/getting-started.html#in-five-minutes` | A | **added**, verified (the docs' own sentence). "Five minutes" itself was not timed by the run; it is the docs' heading, with Rust and Node already installed |
| L36 | The post says Undra 1.0; the workspace is 0.1.0 and `undra --version` prints `undra 0.1.0` | `Cargo.toml:25`; `docs/RELEASING.md:86-91` (`scripts/bump-version.sh 1.0.0`, tag `v1.0.0`; `80-85` before the checklist lost its tap steps); `sde:launch-site` ("Verified by running") | A | **added**: true after the release pull request bumps the version and tags; a sentence that depends on the release existing |
| L37 | Applications built from `undra init` pin the Undra crates at a git tag, `@undra/runtime` from npm, the Swift package and Maven Central | `site/docs/getting-started.html` section 3; `crates/undra-cli/src/commands/init.rs:483` | A | **added**, verified; not true until the release (and `launch-dist`) exist |

Not claimed, on purpose: any speed comparison with another tool; any date for the Swift package or Maven Central.
