# Tilia LLM Docs This index is optimized for AI agents and coding assistants. ## Table of Contents 1. [ReScript guide](./llms-rescript.md) 2. [TypeScript guide](./llms-typescript.md) ## How AI Should Use These Docs 1. Read this file first. 2. Load the language-specific guide. 3. Follow the carve-first rules strictly. 4. Prefer simple, testable composition over inline reactive complexity. ## Non-Negotiable Rules - Use `carve` as the default way to build feature modules. - `carve` must bind parts into one reactive object, not contain business logic. - Keep `carve(({ derived }) => ({ ... }))` glue-only. - Move logic into standalone functions, then inject `self` via `derived`. - Prefer `name: derived(name)` where `name` takes `self` as first parameter. - In TypeScript examples, prefer arrow functions to keep dependency injection paths clean. ## API Anchors - `carve: (deriver<'a> => 'a) => 'a` - `deriver.derived: ('p => 'a) => 'a` - `computed: (unit => 'a) => 'a` - `source: ('a, ('a, 'a => unit) => 'ignored) => 'a` - `store: (('a => unit) => 'a) => 'a` - `tilia: 'a => 'a` - `changing: (unit => dict<'a>, ~guard: (unit => bool)=?) => { changes: unit => { upsert: array<'a>, remove: array }, mute: (unit => unit) => unit }` ## Coverage Requirement for AI - Treat `llms-rescript.md` and `llms-typescript.md` as full API references. - Cover: `tilia`, `carve`, `observe`, `watch`, `batch`, `signal`, `derived`, `lift`, `readonly`, `computed`, `source`, `store`, `changing`, `make`. - For `source`, always explain: - setup runs on first read - setup re-runs when tracked dependencies change - `set` emits the next value imperatively - `previous` is the last emitted value - recommended example: `source(empty(), derived(loader(service)))` where loader sets `stale(previous)` before async fetch - when used with `carve` + `derived(loader)`, `previous` should be used to keep stale UI data visible during reload (no blink) - the `derived(loader)` pattern enables conditional loader strategies based on sibling feature fields - For `changing`, always explain: - takes an accessor `() => dict` so the tracker can follow source swaps - tracks key-level writes on a tilia-proxied dict - returns `{ changes, mute }`: `changes` is a capture function for `watch` that drains accumulated changes as `{ upsert: T[], remove: string[] }`; `mute` runs a callback with tracking suppressed - `upsert` contains objects captured at write time, `remove` contains keys of deleted entries - last write wins for same-key overwrites within a batch - each call creates an independent accumulator - optional `guard` gates accumulation: when falsy, changes accumulate silently; when truthy, all accumulated changes drain - `mute` is for inbound sync writes: writes inside `mute` are still reactive but not tracked, preventing feedback loops - the accessor pattern enables use with `source`-backed data: when `source` replaces the dict, the tracker re-registers on the new object - `source` handles inbound (loading into reactive data), `changing` + `watch` handles outbound (pushing writes to external)