eleven / ocho Public

The app schema: one JSON description renders To-do & Reminders with native components (#478)

* web+ios+mac: the app schema — one JSON description renders To-do & Reminders native

A declarative, human/LLM-readable JSON schema (docs/app-schema.md) now
formally describes how a mini-app looks and behaves — state shape,
event folds, grace-period partition, actions (emit/schedule/unschedule/
parse), a searchable sectioned list view, and a detail "show" view.
Two interpreters consume the same schema:

- web/static/apps/schema-engine.js (pure, DOM-free; tested in Node)
- ios/LChatCore SchemaEngine.swift + SchemaAppView.swift (SwiftUI)

Tapping To-do or Reminders on iOS now opens a native screen (schema
Interface + companion Chat via the native feed, toggled in the header);
the Mac's companion split and takeover panes render the same native
interface in place of the app webview. Other apps keep their webviews.

The contract is pinned three ways in npm test: the schema fold replays
the shared event vectors, fuzzed parity proves it identical to the
hand-written todo.js/remind.js reducers, and the native schema/vector
copies must be byte-identical to the canonical files. SchemaEngineTests
runs the same vectors and semantics on the Swift side.

Native transport reuses the existing wire: REST POST /api/messages for
events, /api/schedule for reminder fires (scheduled-first, sid recorded,
offline fallback ticker), and it seeds from the encrypted "apps"
snapshot before folding newer events, so long lists survive the message
window. The server keeps seeing only ciphertext.

No changelog entry yet — native-facing; it ships with the next
iOS/Mac release per the release-timing rule.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* web+mac: grace rings drain clockwise (todo, remind, schema renderer)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* mac+ios: HIG design pass on the schema renderer — grouped lists, native search, empty states, destructive confirms

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* mac: quiet focused search field + inline empty state (drop emptySymbol from the schema)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* web+mac+ios: Reminders-style completion flourish — check fills, a beat, then tuck (no countdown ring)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* merge fixes: dedupe threadState + decryptData/encryptData twins added on both sides

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* apps: optimistic emit + composer spinner, native NSSearchField, drag-to-reorder todos (todo.move)

- Native emits fold before the POST (ordered send queue; a failed send hints
  inline and reconciles by reloading) — adding a todo is instant; the composer
  shows a small spinner for actions with a real pre-fold round-trip
  (Reminders' schedule step).
- The Mac search field is the platform's NSSearchField — no more hand-drawn
  capsule flashing the white field-editor background on focus.
- New move fold op + section reorder surface in BOTH engines, todo.move event
  + move action in the schema, moveItemAfter in apps.js for the hand-written
  todo.js twin, native onMove drag with visual→array anchor mapping, vectors
  + mirrored JS/Swift tests, docs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ios+mac: group threads render shared schema apps native

SchemaAppStore/cache take an explicit appId (a group hosts several shared
apps on one thread); the Mac group pane and the iOS picked-app surface swap
in SchemaAppView when the picked app has a schema, webview embed otherwise.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* web: todo & remind render through the schema engine (schema-render.js)

One schema now drives BOTH platforms end to end: the app files wire
schemaEvents/schemaRender into defineApp and keep only glue (commands,
preview/unread, chat wording, the reminder parser). The DOM renderer consumes
the same listModel/rowModel SwiftUI does — search and drag-to-reorder arrive
on the web for free; the fold-up tuck beat and per-thread transient UI state
carry over. Docs + README updated; fuzz parity repurposed as the wiring net.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ios+mac: composer keeps focus after adding (burst entry, Return chains)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell c415888a223521887d802b65d3e059333b615c58 parent 70c503b
31 files changed, +4940 −699
  • CLAUDE.md +1 −1
  • changelog/2026-07-09-ring-clockwise.md +1 −0
  • docs/app-schema.md +355 −0
  • ios/LChat/Views/NativeChatScreen.swift +16 −2
  • ios/LChat/Views/SchemaAppScreen.swift +55 −0
  • ios/LChat/Views/ThreadListView.swift +9 −4
  • ios/LChatCore/Package.swift +4 −1
  • ios/LChatCore/Sources/LChatCore/AppSchemaRegistry.swift +28 −0
  • ios/LChatCore/Sources/LChatCore/Crypto.swift +12 −14
  • ios/LChatCore/Sources/LChatCore/LChatAPI.swift +0 −1
  • ios/LChatCore/Sources/LChatCore/SchemaAppStore.swift +329 −0
  • ios/LChatCore/Sources/LChatCore/SchemaAppView.swift +573 −0
  • ios/LChatCore/Sources/LChatCore/SchemaEngine.swift +738 −0
  • ios/LChatCore/Sources/LChatCore/Schemas/remind.json +124 −0
  • ios/LChatCore/Sources/LChatCore/Schemas/todo.json +123 −0
  • ios/LChatCore/Tests/LChatCoreTests/Fixtures/schema-vectors-remind.json +156 −0
  • ios/LChatCore/Tests/LChatCoreTests/Fixtures/schema-vectors-todo.json +208 −0
  • ios/LChatCore/Tests/LChatCoreTests/SchemaEngineTests.swift +334 −0
  • ios/LChatMac/NativeChatView.swift +40 −15
  • package.json +1 −1
  • test/apps.test.mjs +1 −1
  • test/event-vectors/todo.json +69 −1
  • test/schema-engine.test.mjs +389 −0
  • web/static/apps.js +18 −0
  • web/static/apps/README.md +11 −0
  • web/static/apps/remind.js +31 −326
  • web/static/apps/schema-engine.js +522 −0
  • web/static/apps/schema-render.js +521 −0
  • web/static/apps/schemas/remind.json +124 −0
  • web/static/apps/schemas/todo.json +123 −0
  • web/static/apps/todo.js +24 −332
CLAUDE.md

Loading diff…

changelog/2026-07-09-ring-clockwise.md

Loading diff…

docs/app-schema.md

Loading diff…

ios/LChat/Views/NativeChatScreen.swift

Loading diff…

ios/LChat/Views/SchemaAppScreen.swift

Loading diff…

ios/LChat/Views/ThreadListView.swift

Loading diff…

ios/LChatCore/Package.swift

Loading diff…

ios/LChatCore/Sources/LChatCore/AppSchemaRegistry.swift

Loading diff…

ios/LChatCore/Sources/LChatCore/Crypto.swift

Loading diff…

ios/LChatCore/Sources/LChatCore/LChatAPI.swift

Loading diff…

ios/LChatCore/Sources/LChatCore/SchemaAppStore.swift

Loading diff…

ios/LChatCore/Sources/LChatCore/SchemaAppView.swift

Loading diff…

ios/LChatCore/Sources/LChatCore/SchemaEngine.swift

Loading diff…

ios/LChatCore/Sources/LChatCore/Schemas/remind.json

Loading diff…

ios/LChatCore/Sources/LChatCore/Schemas/todo.json

Loading diff…

ios/LChatCore/Tests/LChatCoreTests/Fixtures/schema-vectors-remind.json

Loading diff…

ios/LChatCore/Tests/LChatCoreTests/Fixtures/schema-vectors-todo.json

Loading diff…

ios/LChatCore/Tests/LChatCoreTests/SchemaEngineTests.swift

Loading diff…

ios/LChatMac/NativeChatView.swift

Loading diff…

package.json

Loading diff…

test/apps.test.mjs

Loading diff…

test/event-vectors/todo.json

Loading diff…

test/schema-engine.test.mjs

Loading diff…

web/static/apps.js

Loading diff…

web/static/apps/README.md

Loading diff…

web/static/apps/remind.js

Loading diff…

web/static/apps/schema-engine.js

Loading diff…

web/static/apps/schema-render.js

Loading diff…

web/static/apps/schemas/remind.json

Loading diff…

web/static/apps/schemas/todo.json

Loading diff…

web/static/apps/todo.js

Loading diff…