Skip to content

Engine: pure connection/sync state machine core (DEV-1150 part A) - #48

Merged
adamski merged 28 commits into
mainfrom
adam/dev-1150-replicant-redesign-23-implement-the-single-owner-connection
Sep 29, 2026
Merged

adamski merged 28 commits into
mainfrom
adam/dev-1150-replicant-redesign-23-implement-the-single-owner-connection

Conversation

@adamski

@adamski adamski commented Sep 27, 2026 •

Copy link
Copy Markdown
Collaborator

Part of #46 (connection lifecycle redesign) and DEV-1150. Part A of the implementation: the pure core only. It is not wired into the client yet; the driver, transport, SQLite effects and FFI come in a follow-up PR.

What this adds

New module replicant-client/src/engine/. None of it performs I/O, reads a clock, or uses real randomness, so every race can be tested deterministically.

File Contents
types.rs Protocol v2 value types: Change, DocEnvelope, Upload, ServerError
hash.rs Content hash, pinned to the server's two fixture hashes
doc.rs, doc_upload.rs Rules for one document, as pure functions from a snapshot to DB operations: apply_change, apply_server_copy/missing/deleted, build_upload, settle, rebase, and the settle invariant
backoff.rs Full-jitter connect backoff, backoff per document and for catch-up, and a seeded xorshift jitter source
machine.rs Core::step(Input) -> Vec<Effect>: connection lifecycle, catch-up, snapshots, cursor rules for live changes, upload orchestration, timers and lifecycle events
fuzz_tests.rs 2,000 seeded random input sequences

How it addresses #46

The rules are the ones in the design doc (DEV-1149). The main ones:

  • Server shadow per document. Downloads commit the shadow and the cursor in one transaction, so nothing is held in memory and a disconnect loses nothing.
  • Echoes are recognised by upload_id in the outbox, so an echo never double-applies a change.
  • Acks settle only the rows they cover. This fixes the ack that erased a newer edit.
  • One catch-up path. A failed catch-up is retried while connected. After 3 failures the connection drops.
  • Events follow a fixed order in every connection: ConnectionSucceeded, then SyncStarted, then SyncCompleted (at most once), then ConnectionLost. The fuzz tests check this.
  • The heartbeat has a timeout. Socket events carry a generation, so an event from an old socket is ignored.
  • Fatal errors halt until something changes: credentials, a reconnect, or the periodic retry for auth_invalid.

Interface notes for the driver PR

  • The core expects a Plan 2 server (protocol v2). In particular, get_document must return a deleted error with current_seq for deleted documents.
  • The driver must answer every effect that touches the DB with the input named in that effect's doc comment. It must also drop answers that belong to an earlier connection.

Testing

cargo test -p replicant-client --lib engine: 134 tests pass. The workspace --lib suite is green. fmt and clippy (correctness and suspicious) are clean.

Wire contract with the server

tests/wire_contract_tests.rs deserializes every frame recorded by the v2 server (replicant-server#10) into the engine types, and hash.rs now checks the shared content-hash fixture. The request and response structs are test-only mirrors until the driver PR adds real serde types.

…etry-after and fatal/forbidden classification
…fatal upload and fetch replies, reset failure counters on local settle
Deserializes every recorded server v2 frame into the client's engine
types, pins the shared content-hash fixture, and replaces hash.rs's
two hand-built pin tests with a read of that shared fixture.
Remove wire_contract_tests.rs's duplicate of hash.rs's fixture-driven
hash test, and assert the server hash survives the jsonb round trip
and matches the client hash of the echoed upload content.
@adamski
adamski merged commit fac8aab into main Sep 29, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant