Status: true differential IVM — point rows, N-table equi-JOIN chains, multi-row join bags, inequality/LIKE bag filters — + scrub UI + causal resume + wire fail-closed
Thesis: In LukeLang you never fetch, never invalidate, never subscribe, never diff. You declare dependencies once, and change finds its own way from row to pixel.
One graph
DB row → server cell → [wire] → client cell → pixel └────────────── one dependency graph, compiler-known ──────────────┘
Mainstream stacks glue three separate worlds (DB / server / client) with queries, cache invalidation, refetching, subscriptions, and diffing. LukeLang's reactive engine already answers "when X changes, what needs to update?" inside the client. The Live Graph answers it for the whole stack.
This release
| Piece | Role |
|---|---|
WATCH … FROM db WHERE … |
Declares a DB-backed reactive cell |
Trigger IVM cache (luke_ivm_*) |
Maintained TEXT snapshot |
| Differential triggers | Single-table id = N: NEW.col / OLD — no full group_concat rewrite |
| Keyed join differential | JOIN … ON a.x = b.y WHERE a.x = N → WHEN NEW on both tables + partner probe |
| N-table join chains | Equality closure on ON keys → WHEN NEW.<bound> = lit + NEW-pinned SELECT (live_graph_join3) |
| Multi-row JOIN cards | luke_ivm_jbag_* keyed by hop rowids; publish group_concat (live_graph_join_filter, live_graph_join_multi) |
| Bag aggregates | Any NEW./OLD.-qualifiable filter (=, >, LIKE, …) — bag I/U/D, no base re-scan (live_graph_agg, live_graph_agg_range) |
Causal log (luke_ivm_log_*) |
Append on each pushed change |
Last-Event-ID |
Parsed on the request; PUSH WATCH replays log rows with seq > id |
| Wire hardening | SSE send failure aborts the stream; LUKE_SSE_ORIGIN; scoped PUSH requires user |
| Scrub UI | Client-buffered history + Back / Forward / Live (live_graph_scrub.luke) |
data_version gate |
Idle beats skip even the cache read |
| Wall app | examples/deploy/wall — one deployable DB→pixel binary + Caddy |
Acceptance: external UPDATE → pixel with region=1; reconnect resumes from the log; watch_queries stays bounded; join / join-multi / filter / bag / range proofs stay green in make test-build.
War cry surface
raw "# server"watch user from db where "id = 1"push watch user on req for 50 beats every 50 msraw "# client"signal user = ""bind("name", user)watch user from "http://127.0.0.1:8798/watch"
Differential shapes (not recompute):
watch card from db as "SELECT u.name || ' · ' || p.title FROM users u JOIN profiles p ON u.id = p.user_id WHERE u.id = 1"watch card from db as "SELECT u.name || ' · ' || p.title FROM users u JOIN profiles p ON u.id = p.user_id WHERE u.status = 'active'"watch adults from db as "SELECT name FROM people WHERE age > 30"watch feed from db as "SELECT body FROM notes WHERE status = 'open'"
Prior tiers
- Spike — hand-rolled CDC poll →
region=1 - Server
WATCH/PUSH WATCHsurface PRAGMA data_versiongate- Trigger-maintained cache (
group_concat) - NEW/OLD differential triggers + event-log resume
- Multi-join cache recompute + client scrub UI beachhead
- Keyed equi-join differential + wire fail-closed + wall deploy proof
- Partner EXISTS / 3+ chains / equality bag
- Now: multi-row join bags + inequality bag filters (true Execution A+)
What falls out next
- Free multicore parallelism — independent reactions; compiler schedules without races
- Server+client scrub by log seq — wire DevTools to
luke_ivm_log_*(client buffer shipped) - Broader SQL shapes — LEFT JOIN, non-equi ON, expression-only SELECT lists
Related
STRATEGY.md— identity and phased planREACTIVE.md— client reactive engineDEPLOY.md— TLS / Caddy /LUKE_SSE_ORIGIN- Wall app:
examples/deploy/wall/ - Spike A push:
SUBSCRIBE+ SSE — the wire primitive clientWATCHsits on