Consistency
Lime Sync is an asynchronous client cache over a transactional authority. A client may read an older committed snapshot while another client’s acknowledged write is still propagating. That is the contract Zero documents for its clients, and Lime Sync does not reinterpret it: the validation suite makes no claim of global linearizability or strict serializability for cache reads.
What Lime Sync does own is a database’s core job. The server decides what a query returns and whether a mutation commits. If those are wrong, they are wrong regardless of consistency model. So the test suite is organized around four properties, in priority order:
- Named queries return the right data and the right shape. ZQL queries — filters, ordering, limits, related subqueries, permission transforms — produce the same rows the reference implementation produces.
- Custom mutators commit or roll back atomically, exactly once. A mutation’s row effects and its last-mutation-ID move together: never rows without the ack, never the ack without the rows, and a replayed mutation never applies twice.
- No data loss across restart, eviction, reconnect, retention pruning, and injected storage faults.
- Ordering holds. Client cookies never regress, an acknowledgement never arrives before its effects, and a patch is never applied torn.
How observations are made
Zero clients keep a local optimistic store that applies mutations instantly, before the server confirms anything. A read on the mutating client immediately after a mutation observes that local overlay, not the engine. Every correctness check in the suite therefore observes server-confirmed state: a read after the server acknowledged the mutation, a non-writing client’s view, or the authority database directly. A test run with zero non-writing clients is treated as invalid rather than passing.
The suite keeps three kinds of evidence separate, because mixing them makes stale-but-valid cache reads look like transactional anomalies:
- Authority history — mutation invocations, server outcomes, last-mutation-IDs, and direct authoritative reads. This history supports atomicity, durability, and idempotency claims.
- Client observation history — complete materialized-query observations, cookies, and convergence barriers. This history supports monotonic-session, read-your-writes, atomic-visibility, authorization, and eventual-convergence claims. Wall-clock completion order across clients is not a serialization order.
- Differential traces — generated operations run against the TypeScript reference core, stock zero-cache, and the Rust engine, with normalized outputs compared. These prove semantic equivalence for the exercised corpus.
Convergence is proven with a barrier, not a delay: the workload stops, faults heal, a unique sentinel commits, every client must report a complete observation containing the sentinel, and a fresh client plus a direct authority read must agree with all of them. Two clients agreeing with each other is not enough.
How the checks run
- Differential lanes compare Lime Sync against stock zero-cache and against a TypeScript reference core: fixed query shapes, seeded randomized sweeps with shrinking, and a generated operation-trace differential inside the Rust engine’s test suite whose seeded vocabulary includes the query layer — named ZQL queries, membership changes, multi-table writes, and in-place permission-transform replacement — with failing traces shrunk to minimal reproducers.
- Consistency checkers consume recorded operation histories and validate atomic visibility, exactly-once mutation application, and permission-transition behavior. Every checker ships with a positive fixture and a mutant history it must reject, so the checker itself is proven able to fail. The recorded atomic-visibility history is additionally analyzed by the pinned Elle checker (list-append, serializable) on every qualifying run: a torn read surfaces as a dependency-cycle anomaly. The check is scoped honestly — it proves dependency safety among the tracked appends of that workload, and no strict or realtime model is claimed for asynchronous cache reads.
- Fault lanes kill and restart the server process over the same SQLite file, inject engine faults at named push/pull transaction boundaries, inject storage faults, evict and resume Cloudflare Durable Objects, drop or replay responses, and compose fault classes — a client transport outage held open across an engine-fault arm and a server restart, and a full prune followed by a restart that must keep the served cookie monotonic. Every scheduled fault carries receipts: armed, then fired or an explicitly recorded cancellation, with heals recorded for outages. A generated fault schedule that fires nothing fails as invalid instead of passing vacuously.
- Mutation testing keeps twelve runnable known-bug patches: nine in the Rust engine and three in the TypeScript host. They cover dropped filter branches, skipped LMID writes, over-pruned retention, swallowed rollbacks, non-durable watermarks, reader/writer admission, committed-change publication, and torn namespace backups. The matrix records which compatible lane catches each patch and verifies every verdict against the lane’s actual failure output. A mutant nothing catches is a named gap to close.
What is deliberately not claimed
- Global linearizability or strict serializability across clients. An asynchronous cache cannot honestly claim either, and Lime Sync does not.
- CAP-theorem positioning. Lime Sync inherits Zero’s model: reads are local and fast, writes are authoritative and acknowledged.
- Green-badge completeness. The suite’s own documentation records the gaps it has not closed, including checks that do not yet run against every target, fault modes not yet composed, and scale numbers that are historical single runs rather than gated assertions. A claim stronger than the evidence would be a regression, not a feature. (The machinery itself has pinned real bugs: the metamorphic guard caught an upstream zero-cache defect, and the mutation matrix proves each included engine and host lane goes red on known bugs.)
The repository’s internal testing documentation tracks the current gap list
with the same candor; /docs/testing describes how evidence is scoped per
runtime.