Skip to content

Local fallback + Rejoin merge (fixture-locked) #290

Description

@aarontrowbridge

Important

Scope. Slice 7 of Managed Fleet (#283 — read it first). Local fallback + Rejoin — the repair hatch that closes the loop. Explicit entry from unreachable (or on demand): the client serves itself locally, banner visible ("Local fallback — sessions merge back on rejoin"). On stable reconnect, the client ships its local store shard to the server's rejoin route, which merges it as the single writer under the fixture-locked policy and returns a merge report. Highest merge-risk slice — deliberately last, riding everything before it.

Acceptance Criteria

  1. Fallback is explicit and visible; sessions created in fallback are usable locally and marked for rejoin.
  2. Rejoin merge, fixture-locked: no canonical row downgraded; a canonical-newer fixture proves shard-stale rows are NOT applied; equal timestamps keep canonical (tie-break pinned by fixture); foreign-key check and quick check clean after; the client's shard preserved regardless of outcome.
  3. Conflicting event-stream positions (both sides hold a position with different content) are skipped-and-reported — every skipped event listed in the merge report, never silently renumbered or merged.
  4. The handshake measures clock skew; a shard from a skewed machine beyond the configured threshold is refused with preservation.
  5. Schema drift: columns map by name; drift is unmappable — refusing with preservation — when any shard column carrying non-default values has no canonical counterpart.
  6. After a verified merge, fallback exits, the store quarantines, and the panel reattaches to canonical history whole.

Testing Decisions

Fixture-driven merge tests: the golden shards from the 2026-08-07 recovery archive (real fork, real drift) plus synthetic fixtures — canonical-newer rows, equal-timestamp ties, conflicting event positions, skewed clocks, unmappable drift. Every AC maps to at least one fixture assertion; the merge report's content is asserted, not just exit status. Client flow tests against a fake rejoin route; the route itself tested under standalone serve (CLI parity, AC 8 on the parent).

Key Decisions & Constraints

  • The merge runs only on the canonical server (single-writer rule); the client ships bytes, the server decides.
  • A merge that cannot be verified does not run; refusal always preserves the shard and says why.
  • The merge report is a user-visible artifact (what merged, what skipped, what was refused) — rejoin is never a silent sync.
  • Parent constraints apply; slice-local: rejoin requires identity-verified attach + token (slices 1–3), and fallback state is per-machine, never synced.

Source

Part of #283 · Design: docs/adr/0005-managed-fleet.md (PR #282) · Blocked by #286 (rejoin rides the verified tunnel+attach path).

Metadata

Metadata

Assignees

No one assigned

    Labels

    afkAutonomous: implement + merge unattended

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions