Skip to content

fix(freeze): mint a new log epoch when freeze or thaw changes served history - #785

Merged
scbrown merged 2 commits into
mainfrom
malcolm/xdvg1-feed-f2
Oct 24, 2026
Merged

scbrown merged 2 commits into
mainfrom
malcolm/xdvg1-feed-f2

Conversation

@scbrown

@scbrown scbrown commented Oct 24, 2026 •

Copy link
Copy Markdown
Collaborator

What

A deep freeze or a thaw now mints a new log epoch when it changes the history the change feed serves. A freeze or thaw that moves no rows keeps the epoch.

Freeze moves a graph's rows into a pack. The graph still reads the same, so a replica that follows the feed needs no change record. But the feed no longer serves those rows, so a replay from an older cursor silently returns fewer records. Thaw does the opposite: the rows return behind every cursor. The new epoch appears in source.log_epoch on the next /changes page. A follower that sees it change knows its cursor names a history that no longer exists, and must re-bootstrap instead of splicing.

There are deliberately no tombstone or retract records for freeze. They would make a replica drop facts that the source still serves.

Base

The PR is rebased onto main 3b1cf198 (#789 landed) and is not stacked. Its commits are 7ba99040 (mint a new log epoch when a freeze or thaw changes served history) and f6185c6f (keep the epoch when a freeze or thaw moves no rows). git range-diff against the published c6a97d62/5690893f shows both identical (=). #789's merge 3b1cf198 equals the reviewed f2c51c0b patch. All knowledge witnesses match their files.

Local runs at f6185c6f:

  • cargo test --features shacl,owl: 3210 passed, 0 failed;
  • the server binary: 237 passed, 0 failed;
  • clippy -D warnings -A missing-docs exits 0 for full and shacl,owl;
  • fmt and the file-size ratchet pass.

#805 (the as-of share) is rebased onto this branch and follows it.

Acceptance

Criterion Evidence
Epoch changes on freeze and on thaw, and the next page carries it freeze_and_thaw_each_mint_a_new_log_epoch. Its control checks that the frozen graph's own feed records go from >0 to 0.
No change on a freeze or thaw that moves no rows, or on a refused freeze a_freeze_or_thaw_that_moves_no_rows_keeps_the_log_epoch
Query results identical across a freeze existing freeze_keeps_the_graph_readable_via_all_three_opt_ins

Sabotage: making the freeze guard unconditional, making the thaw guard unconditional, and removing the freeze mint each turn the matching test red. Restoring the file brings back identical bytes.

Local runs: cargo test --features shacl,owl gives 3192 passed / 0 failed. cargo clippy --all-targets -- -D warnings -A missing-docs is clean with --features full and with --features shacl,owl. cargo fmt --check is clean.

Not in this PR

A follower that refuses the old cursor and re-bootstraps until it equals the source. A source that holds a frozen graph cannot be rebuilt from the feed today, and shares exclude frozen packs. That work is tracked separately, with the as-of share.

Rollback

Revert these two commits. The epoch then stops changing on freeze and thaw, and followers lose the signal. No data or schema changes.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BzgSY2LpdGhBhb7D1nHbiF

scbrown and others added 2 commits October 24, 2026 10:22
…history

Deep freeze relocates a graph's rows into a pack. The graph reads the same,
so a following replica needs no record, but the change feed no longer serves
those rows: a replay from an older cursor silently returns fewer records
(local-sync-mode.md F2). Thaw re-imports the rows behind every cursor.

Both now mint a new log epoch inside their savepoint, so a follower sees the
epoch change and re-bootstraps instead of trusting its cursor. Freeze emits
no tombstone records: that would make a replica drop facts the source still
serves.

The test checks that the window graph's feed records go from present to
absent and that the epoch changes on freeze and again on thaw. Removing
either mint turns it red.

Refs aegis-xdvg.1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0185RxTpLsPCQFB9ni72Nzac
A freeze that removed no feed rows, or a thaw that restored none,
changed no served history. Minting a new epoch then would make every
follower re-bootstrap for nothing. A refused freeze already rolled back
before the mint, and a test now pins that too.

The freeze test also checks that the next change page names the new
epoch, which is how a follower learns its cursor belongs to the old
history.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BzgSY2LpdGhBhb7D1nHbiF
@scbrown
scbrown force-pushed the malcolm/xdvg1-feed-f2 branch from 17b1b20 to f6185c6 Compare October 24, 2026 14:22
@scbrown
scbrown marked this pull request as ready for review October 24, 2026 14:38
@scbrown

scbrown commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator Author

APPROVE source review at f6185c6, base 3b1cf19.

Reviewed both changed files and the epoch-setting, history-import and savepoint boundaries. Freeze and thaw mint only when they move feed rows; each update participates in the existing savepoint. No blocking finding.

Independent exact-product-source tests: all 15 freeze tests passed, including three private controls for failure after the epoch update during freeze, failure after the epoch update during thaw with archive remount, and epoch/store identity persistence across reopen and subsequent feed pages. The watched HOME paths and UID resources remained unchanged. Existing compiler warnings retained. No live freeze/thaw or replica enrollment performed.

@scbrown
scbrown merged commit e3ea365 into main Oct 24, 2026
24 checks passed
@scbrown
scbrown deleted the malcolm/xdvg1-feed-f2 branch October 24, 2026 14:54
@scbrown scbrown mentioned this pull request Oct 25, 2026
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