Skip to content

dir-sim: the history as a log of verified append-only batches - #1482

Merged
AdaWorldAPI merged 3 commits into
mainfrom
ccr-0455e606-wmtsor
Oct 11, 2026
Merged

AdaWorldAPI merged 3 commits into
mainfrom
ccr-0455e606-wmtsor

Conversation

@AdaWorldAPI

@AdaWorldAPI AdaWorldAPI commented Oct 11, 2026 •

Copy link
Copy Markdown
Owner

What

Decision 3(b): OGAR's logical version stays independent of Lance's physical commit version. Ordered logical versions persist in atomic, verifiable batches, following the sparse-append / resident-fold pattern. A torn save is detected, and incomplete recovery is refused. This is D-DSP-2a.

The new log module is pure, with no I/O. It is what the D-DSP-2b storage crate will write and read.

  • LogState::batch: holds only what an image has beyond the log: values, roots and versions since the last save, plus tags and verdicts that changed. A batch carries any number of logical versions, counted on the history's own clock and never on the storage version.
  • Continuation check: LogState keeps a digest of the logged values, roots and versions, and batch refuses (NotAContinuation) an image whose logged prefix differs, not only a shorter one.
  • committed(): advances the state only after the store write has succeeded. Each batch is committed once, in order.
  • Batch header: every batch starts with its number, first version, version count, record count and an FNV-1a checksum.
  • replay: refuses the whole log at the first batch with a numbering gap, a missing header, records out of position, a count or checksum mismatch, or versions that do not follow. Nothing is repaired.
  • Replay cost: a batch is applied in place and undone on failure by truncating to the previous lengths, so restoring never copies the accumulated image.
  • replay_prefix: a separate, explicit call that reads up to the damage and names it.
  • Encoding: the encoder destructures every struct exhaustively, so a new field is a compile error rather than a field silently dropped from the log. Decoding rebuilds through validating constructors (RemoteMailbox::new, Dn128::new) and refuses trailing bytes.

Tests

  • Codec: every Change, Violation and Recipient variant and a full ObservedNode round-trip, and every strict prefix of each encoding is refused.
  • Two batches: a two-batch log restores the same reads.
  • Many versions per batch: one batch holds five versions.
  • Torn saves: four kinds are refused (truncated tail, missing header, checksum mismatch, numbering gap).
  • Explicit prefix: replay_prefix reads up to the damage and names it.
  • Commits: commits only move forward.
  • Rewritten history: a changed value, version or root is not a continuation.
  • Rollback: a batch that fails after it applied leaves the earlier batches' image.

Disable runs were red as required (7 guards).

Rebased onto #1481: cargo +1.99.0 fmt --check, clippy --all-targets -D warnings and cargo test (231 passed, 0 failed) on lance-graph-dir-sim. The supersession index is current.

Not in this PR

D-DSP-2b, the Lance I/O crate that writes these batches, is queued on the board.

🤖 Generated with Claude Code

https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg


Generated by Claude Code

log (pure, no I/O) is what D-DSP-2's storage crate writes and reads.
LogState::batch holds only what an image has beyond the log: values,
roots and versions since the last save, tags and verdicts that changed.
A batch carries any number of logical versions (the history's own clock,
never the storage version), and committed() advances the state only
after the store write succeeded; a batch is committed once, in order.

Every batch starts with a header: number, first version, version count,
record count, FNV-1a checksum. replay refuses the whole log at the first
batch with a numbering gap, a missing header, records out of position,
a count or checksum mismatch, or versions that do not follow. Nothing is
repaired; replay_prefix is the separate call that reads up to the damage
and names it.

The encoding destructures every struct exhaustively, so a new field is a
compile error rather than a field dropped from the log. Decoding rebuilds
through validating constructors (RemoteMailbox::new, Dn128::new) and
refuses trailing bytes.

Tests: every Change, Violation and Recipient variant and a full
ObservedNode round-trip, every strict prefix refused; two-batch log
restores the same reads; one batch holds five versions; four kinds of
torn save refused; the explicit prefix; forward-only commits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
@AdaWorldAPI
AdaWorldAPI marked this pull request as ready for review October 11, 2026 10:32
@coderabbitai

coderabbitai Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

Next included review available in 59 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available. Your 63 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: 7a159d85-7e51-476c-9435-7fcb22f1fa3e

📥 Commits

Reviewing files that changed from the base of the PR and between c7c4a53 and 4a2b86a.


📒 Files selected for processing (5)
  • .claude/board/STATUS_BOARD.md
  • .claude/plans/dir-sim-persist-v1.md
  • crates/lance-graph-dir-sim/src/lib.rs
  • crates/lance-graph-dir-sim/src/log.rs
  • crates/lance-graph-dir-sim/tests/persist.rs

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.


Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-11T10:35:02.625674Z 251ec01 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 251ec01460

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/lance-graph-dir-sim/src/log.rs
Comment thread crates/lance-graph-dir-sim/src/log.rs Outdated
LogState::batch only compared counts, so an image with the logged
number of values and versions but different content was accepted: the
sparse loops skipped the changed prefix and could return None, reporting
a save while replay kept the old history. LogState now keeps a digest of
the logged values, roots (in version order) and versions, and batch
refuses an image whose prefix digests differently (NotAContinuation).
LogState's Default holds the digest of the empty history, so the first
save is checked like the rest.

replay_prefix cloned the accumulated image before every batch, making a
restore quadratic in the number of saves. A batch is now applied in
place and undone on failure by truncating values, roots and versions to
their previous lengths; its tags and verdicts collect apart and merge
only when the batch is whole.

Tests: a_divergent_history_is_not_a_continuation (changed value, version
and root each refused; red before: the changed value saved as nothing
new); a_batch_failing_after_it_applied_is_undone (a header claiming one
version more fails only after the batch's value and tag were applied;
the image and state are those of the batches before it).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
@AdaWorldAPI
AdaWorldAPI merged commit dff342c into main Oct 11, 2026
13 checks passed
AdaWorldAPI pushed a commit that referenced this pull request Oct 11, 2026
D-DSP-2b. A new workspace-excluded crate, lance-graph-dir-sim-lance, so
lance-graph-dir-sim itself stays free of lance, arrow and datafusion.
It stores the records lance_graph_dir_sim::log builds and reads them
back; it decides nothing about their content.

- LanceLog::append stores one batch as one Lance commit (Create for the
  first, then Append) and only then advances the LogState. The writer
  holds the dataset version it last saw: if the dataset is elsewhere,
  nothing is written (StaleWriter), because Lance would rebase an append
  onto a newer version and interleave two writers' batches. A commit that
  does not land as the next version is reported as Race.
- LanceLog::restore reads every record in one scan and hands them to
  replay; a missing dataset is an empty log.
- The logical versions stay in the records; the Lance version is only
  the physical commit counter.

Tests: two saves restore to the same reads and the same log state; a
missing dataset is an empty log; a stale writer writes nothing, on create
and on append; a batch stored with a record missing is refused at
restore; a batch out of order is refused before anything is written.

CI: .github/workflows/dir-sim-persist.yml builds and tests the crate
(protoc and the protobuf includes installed, which lance's build needs).
Board: D-DSP-2a shipped (#1482), D-DSP-4 shipped (#1481), D-DSP-2 in PR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
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.

2 participants