feat(eventcore-fs): replica-id fingerprint + conflict check #400

Merged
jwilger merged 2 commits from feat/392-replica-identity into main 2026-06-13 06:46:58 -07:00
Owner

Implements issue #392 / ADR-0044: hardens replica identity for merge mode against the copy trap.

Problem

Each working copy has a machine-local replica_id. The .gitignore keeps it out of git clone, but a cp -r of a working tree (including .eventcore/) can still duplicate a writer's identity — making divergent forks invisible (silent corruption, the worst failure class).

Change

  • Fingerprint binding. replica_id is bound to a working-copy fingerprint (OS machine id + absolute repo path + .git inode), recorded in a gitignored .eventcore/replica_fingerprint. On open, a fingerprint mismatch (a cp -r to a new path, a move, a restored backup) regenerates a fresh id — so the copy gets a distinct id on its next write (a detectable fork rather than silent sharing).
  • Reconcile-time collision check. detect_forks (and status/reconcile) fail loud with FsEventStoreError::ReplicaIdentityConflict when two concurrent transactions carry the same replica_id — they cannot have come from one linear writer — instead of silently merging a corrupt history.
  • Explicit override. FsConfig::with_replica_id sets the id verbatim for containers/CI/provisioned replicas; the collision check still applies as a backstop.
  • Accessor. FileEventStore::replica_id() exposes this clone's current write identity.

Acceptance (ADR-0044)

  • A cp -r of a working tree, then a write in the copy, yields a distinct replica_id (proven by cp_r_of_a_working_tree_yields_a_distinct_replica_id, which actually cp -rs the store dir and writes).
  • Two transactions sharing a replica_id with inconsistent parent sets surface ReplicaIdentityConflict from detect_forks (proven by concurrent_transactions_sharing_a_replica_id_surface_a_conflict).

Tests

  • New replica_identity_test.rs (2 tests). Full crate suite 43 passed; workspace clippy/fmt clean (stable 1.96).

Closes #392

Implements issue #392 / ADR-0044: hardens replica identity for merge mode against the copy trap. ## Problem Each working copy has a machine-local `replica_id`. The `.gitignore` keeps it out of `git clone`, but a `cp -r` of a working tree (including `.eventcore/`) can still duplicate a writer's identity — making divergent forks invisible (silent corruption, the worst failure class). ## Change - **Fingerprint binding.** `replica_id` is bound to a working-copy fingerprint (OS machine id + absolute repo path + `.git` inode), recorded in a gitignored `.eventcore/replica_fingerprint`. On open, a fingerprint mismatch (a `cp -r` to a new path, a move, a restored backup) regenerates a fresh id — so the copy gets a *distinct* id on its next write (a detectable fork rather than silent sharing). - **Reconcile-time collision check.** `detect_forks` (and `status`/`reconcile`) fail loud with `FsEventStoreError::ReplicaIdentityConflict` when two concurrent transactions carry the same `replica_id` — they cannot have come from one linear writer — instead of silently merging a corrupt history. - **Explicit override.** `FsConfig::with_replica_id` sets the id verbatim for containers/CI/provisioned replicas; the collision check still applies as a backstop. - **Accessor.** `FileEventStore::replica_id()` exposes this clone's current write identity. ## Acceptance (ADR-0044) - A `cp -r` of a working tree, then a write in the copy, yields a distinct `replica_id` (proven by `cp_r_of_a_working_tree_yields_a_distinct_replica_id`, which actually `cp -r`s the store dir and writes). - Two transactions sharing a `replica_id` with inconsistent parent sets surface `ReplicaIdentityConflict` from `detect_forks` (proven by `concurrent_transactions_sharing_a_replica_id_surface_a_conflict`). ## Tests - New `replica_identity_test.rs` (2 tests). Full crate suite 43 passed; workspace clippy/fmt clean (stable 1.96). Closes #392
Harden replica identity for merge mode (ADR-0044) against the copy trap.

- Bind replica_id to a working-copy fingerprint (machine id + absolute repo
  path + .git inode), recorded in a gitignored .eventcore/replica_fingerprint.
  On open, a fingerprint mismatch (a cp -r to a new path, a move, a restored
  backup) regenerates a fresh id, so a naive cp -r gets a distinct id on its
  next write — a detectable fork rather than silent identity sharing.
- Add a reconcile-time collision check: detect_forks (and status/reconcile)
  fail loud with FsEventStoreError::ReplicaIdentityConflict when two concurrent
  transactions carry the same replica_id, instead of silently merging a
  corrupt-but-linear-looking history.
- Allow an explicit replica_id override via FsConfig::with_replica_id, for
  containers/CI/provisioned replicas; the collision check still applies.
- Expose FileEventStore::replica_id() — this clone's current write identity.

Closes #392
Merge remote-tracking branch 'origin/main' into feat/392-replica-identity
All checks were successful
CI / Detect Changes (pull_request) Successful in 5s
CI / Request auto_review semantic review (pull_request) Successful in 2s
CI / Format (pull_request) Successful in 16s
auto_review auto_review: no findings
CI / Clippy (pull_request) Successful in 1m47s
CI / Security Audit (pull_request) Successful in 25s
CI / Test (pull_request) Successful in 2m43s
CI / Mutation (pull_request) Has been skipped
CI / CI Gate (pull_request) Successful in 2s
f46201ddca
# Conflicts:
#	eventcore-fs/src/config.rs
auto-review left a comment

This PR enhances the replica identity mechanism by binding it to a working-copy fingerprint and introducing a collision check during reconciliation. The changes appear well-structured and include comprehensive tests, making it safe to merge.

Walkthrough

  • eventcore-fs/src/config.rs: Added a new replica_id field to FsConfig with a method to set it explicitly.
  • eventcore-fs/src/error.rs: Introduced a new error type ReplicaIdentityConflict to handle identity conflicts.
  • eventcore-fs/src/lib.rs: Refactored the load_or_create_replica_id function to the new replica module.
  • eventcore-fs/src/merge.rs: Added check_replica_collisions to detect identity conflicts during reconciliation.
  • eventcore-fs/src/replica.rs: New module to handle replica identity logic, including fingerprint computation.
  • eventcore-fs/tests/replica_identity_test.rs: New tests to verify the new replica identity mechanisms, including fingerprint binding and collision detection.

LLM usage and cost

This PR enhances the replica identity mechanism by binding it to a working-copy fingerprint and introducing a collision check during reconciliation. The changes appear well-structured and include comprehensive tests, making it safe to merge. ## Walkthrough - **eventcore-fs/src/config.rs**: Added a new `replica_id` field to `FsConfig` with a method to set it explicitly. - **eventcore-fs/src/error.rs**: Introduced a new error type `ReplicaIdentityConflict` to handle identity conflicts. - **eventcore-fs/src/lib.rs**: Refactored the `load_or_create_replica_id` function to the new `replica` module. - **eventcore-fs/src/merge.rs**: Added `check_replica_collisions` to detect identity conflicts during reconciliation. - **eventcore-fs/src/replica.rs**: New module to handle replica identity logic, including fingerprint computation. - **eventcore-fs/tests/replica_identity_test.rs**: New tests to verify the new replica identity mechanisms, including fingerprint binding and collision detection. ## LLM usage and cost - Reasoning (gpt-4o) in=6244 out=626 cost=$0.040610 - Cheap (gpt-4o-mini) in=747 out=52 cost=$0.000143 Estimated total USD: $0.040753 via https://api.openai.com and https://api.openai.com
jwilger deleted branch feat/392-replica-identity 2026-06-13 06:46:58 -07:00
jwilger referenced this pull request from a commit 2026-06-13 06:57:41 -07:00
Sign in to join this conversation.
No description provided.