Skip to content

plan(#31,#32): fork-impossibility fix — design + failing test#104

Merged
hyperpolymath merged 1 commit into
mainfrom
plan/31-32-fork-fix
May 16, 2026
Merged

plan(#31,#32): fork-impossibility fix — design + failing test#104
hyperpolymath merged 1 commit into
mainfrom
plan/31-32-fork-fix

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

plan(#31,#32): fork-impossibility fix — design + failing test

#31 (per-entity write lock) and #32 (UNIQUE INDEX(entity_id,
previous_hash)) together design the provenance chain so that a forked
history cannot be represented. Issues #31/#32 frame forks as purely
adversarial; that is incomplete. Network-partitioned/replicated honest
writers and simulation branches (ADR-0006) are legitimate divergence.
A UNIQUE INDEX on (entity_id, previous_hash) does not detect a fork —
it rejects the second row at insert time, discarding honest history.
A fork that cannot be written cannot be detected or audited. That is
the integrity defect.

This is a planning skeleton — design doc + a failing-by-design test.
No implementation; the parent session controls merges.

Contents:

  • docs/decisions/0010-provenance-forks-are-first-class.adoc — ADR
    (estate .adoc default). Decision: forks are first-class. Concretely:

  • tests/provenance_fork_test.rs — failing-by-design test. Writes
    genesis + branch A (supported linear path) + branch B (a second
    legitimate child of genesis). Asserts both children persist AND the
    entity records two heads. Compiles against the current public
    surface; the assertions, not the compile, fail. Verified red on
    this branch: child_count==2 passes (log keeps both rows) but
    head_count is 1 not 2 — the single-head table collapses branch B.
    Exactly the V-L2-L1: per-entity serialisation prevents chain forks (write-path lock) #31 defect, in executable form. (With V-L2-L2: UNIQUE INDEX(entity_id, previous_hash) makes forks structurally impossible #32's unique
    index applied, the branch-B insert would additionally fail with a
    constraint violation — also encoded in the ADR test plan.)

Build/test: lib builds clean (only the pre-existing unrelated
gc.rs RetentionConfig warning). New test compiles and FAILS as
intended (1 failed, by design).

Unblocked by #26 / PR #103 — there is now one ProvenanceEntry and one
compute_hash for the fork-aware append/verify to evolve.

Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com


🤖 Generated with Claude Code

#31 (per-entity write lock) and #32 (UNIQUE INDEX(entity_id,
previous_hash)) together design the provenance chain so that a forked
history *cannot be represented*. Issues #31/#32 frame forks as purely
adversarial; that is incomplete. Network-partitioned/replicated honest
writers and simulation branches (ADR-0006) are legitimate divergence.
A UNIQUE INDEX on (entity_id, previous_hash) does not *detect* a fork —
it *rejects the second row at insert time*, discarding honest history.
A fork that cannot be written cannot be detected or audited. That is
the integrity defect.

This is a planning skeleton — design doc + a failing-by-design test.
No implementation; the parent session controls merges.

Contents:

* docs/decisions/0010-provenance-forks-are-first-class.adoc — ADR
  (estate .adoc default). Decision: forks are first-class. Concretely:
  - #32: do NOT add UNIQUE INDEX(entity_id, previous_hash). The `hash`
    PRIMARY KEY (preimage is domain-tagged + covers every field, per
    ADR-0002) already rejects exact-duplicate rows — that is the
    correct duplicate guard. Add a NON-unique
    idx_provenance_predecessor(entity_id, previous_hash) for O(log n)
    fork detection.
  - #31: verisimdb_provenance_chain_head (entity_id PK, one head)
    becomes verisimdb_provenance_chain_heads (PK(entity_id,
    head_hash), a SET of tips). `append_provenance` keeps BEGIN
    IMMEDIATE (still serialises racing *duplicate* appends from one
    node) but removes parent-tip + adds new-tip on linear append; a
    new `append_provenance_fork(... from_hash ...)` adds a head
    without removing one.
  - Detection surface: `fork_points(conn, entity)`; `verify_chain`
    becomes per-branch (each head -> genesis walk hash-consistent),
    so divergence is never conflated with tampering.
  - Data migration: idempotent CREATE-IF-NOT-EXISTS + INSERT..SELECT
    copy of the head table guarded by a sqlite_master check; old
    table left for one release (no destructive step ships). The log
    table is unchanged. Because the unique index is never created,
    an existing sidecar that already contains a legitimate fork
    cannot fail to open — a hazard that WOULD exist had #32 shipped
    first (flagged in the #32 thread).

* tests/provenance_fork_test.rs — failing-by-design test. Writes
  genesis + branch A (supported linear path) + branch B (a second
  legitimate child of genesis). Asserts both children persist AND the
  entity records two heads. Compiles against the current public
  surface; the assertions, not the compile, fail. Verified red on
  this branch: child_count==2 passes (log keeps both rows) but
  head_count is 1 not 2 — the single-head table collapses branch B.
  Exactly the #31 defect, in executable form. (With #32's unique
  index applied, the branch-B insert would additionally fail with a
  constraint violation — also encoded in the ADR test plan.)

Build/test: lib builds clean (only the pre-existing unrelated
gc.rs RetentionConfig warning). New test compiles and FAILS as
intended (1 failed, by design).

Unblocked by #26 / PR #103 — there is now one ProvenanceEntry and one
compute_hash for the fork-aware append/verify to evolve.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@hyperpolymath hyperpolymath marked this pull request as ready for review May 16, 2026 16:32
@hyperpolymath hyperpolymath merged commit e51d345 into main May 16, 2026
18 of 21 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