Skip to content

docs(rfc-0008,rfc-0001): specify snapshot restore v2 — offset sink, retain floor, recovery driver - #185

Merged
jensholdgaard merged 4 commits into
mainfrom
docs/rfc0008-rfc0001-snapshot-restore-v2
Jun 12, 2026
Merged

docs(rfc-0008,rfc-0001): specify snapshot restore v2 — offset sink, retain floor, recovery driver#185
jensholdgaard merged 4 commits into
mainfrom
docs/rfc0008-rfc0001-snapshot-restore-v2

Conversation

@jensholdgaard

@jensholdgaard jensholdgaard commented Jun 12, 2026

Copy link
Copy Markdown
Owner

What

Spec-first amendment for the WAL checkpoint → snapshot restore workstream (the §6.9 v2 restore that RFC 0001 explicitly deferred until Wal::checkpoint existed). Two RFCs amended together because they are two halves of one design:

RFC 0008 (§6.1, §6.6, §6.7, §5, §8, §9):

  • FrameSink::consume now carries the frame's WalOffset — the recovery driver routes one delivered frame to consumers with different replay horizons: the Parquet path consumes every frame above the checkpoint X; the miner consumes only frames above its snapshot's high-water mark S.
  • Wal::housekeeping(retain_floor: Option<WalOffset>) becomes an explicit API; truncation reclaims only segments wholly below min(X, S). The CHECKPOINT sidecar always records the true Parquet horizon (capping it at the floor would re-feed already-published records → data-side duplicates).
  • New RFC0008.7 retain-floor arm + new scenario RFC0008.10 (startup recovery driver: replay completes before listeners open; per-consumer delivery counts; snapshot written at segment rotation).
  • §9 "Recovery-time snapshot of miner state" → RESOLVED.
  • Drive-by accuracy fix: replay signature is &mut self (the landed §6.6 step-4 heal mutates the newest segment).

RFC 0001 (§6.9, §5, §8, §9):

  • §6.9 v2 amendment: the known-version branch restores the tree and replays only the tail above S — the v1 full-replay carve-out existed solely because the offset-resume API was a stub; the double-apply hazard is resolved by routing, not by refusing to restore. No format change (the high-water mark has been in the payload since v1, as planned).
  • Stale-snapshot fallback: a WAL externally truncated past S restores + replays survivors + emits a structured warning naming the gap — hazard docs(rfc-0001): fill in drafted-bar content for the template miner #5 surfaced (observable via the RFC 0010 drift query), never silent.
  • New acceptance criteria §3.5.3 (restore + tail replay ≡ from-scratch rebuild, compared field-by-field via the snapshot payload of both trees; a sink counter proves no frame ≤ S reached the miner) and §3.5.4 (loud degradation); §8 test-plan entry added; §9 resume entry RESOLVED.

Why this shape

The checkpoint X (Parquet durability) and the snapshot mark S (miner-state coverage) are independent horizons. Truncating up to X while S < X destroys the only remaining source of miner state in (S, X] — the data is safe in Parquet, but the restored tree re-mints template_ids for templates first seen in the gap (hazard #5 template drift). The retain floor closes that hole at the cost of at most the segments appended since the last successful snapshot; in steady state (snapshot per rotation) the floor leads the checkpoint and the rule is vacuous.

Maintainer decisions encoded (2026-06-12)

  1. Retain floor at housekeeping (over snapshot-before-checkpoint coupling, and over a steady-state-argument-only rule).
  2. Full production driver in scopeserve() currently opens the WAL but never replays; the driver (replay before listeners, routing, snapshot-at-rotation) is part of this workstream (RFC0008.10).

Invariants / hazards touched

  • §3.4 WAL-before-ack: unchanged — the WAL remains the truth; the snapshot stays a rebuildable cache. The retain floor only delays truncation, never expands what counts as durable.
  • §3.6 object storage is the truth: unchanged — the CHECKPOINT sidecar still records exactly the Parquet horizon.
  • Hazard docs(rfc-0001): fill in drafted-bar content for the template miner #5 (template schema evolution): this is the hazard the retain floor + stale-snapshot warning exist to address; both paths are loud, not silent.
  • §3.5-adjacent (format compat): no snapshot format change; the v1 version-byte contract is untouched.

Implementation plan (follow-up PRs)

  1. ourios-wal: checkpoint sidecar + replay skip + housekeeping(retain_floor) + offset-carrying sink → flips RFC0008.7 (4 arms).
  2. Miner restore v2 + production startup-recovery driver in serve() → §3.5.3/§3.5.4 + RFC0008.10.

Checks run

mdbook build clean (no new diagrams).

Summary by CodeRabbit

  • Documentation
    • Enhanced specifications for data persistence and recovery mechanisms with improved snapshot handling and recovery path optimization.
    • Added comprehensive test scenarios validating data recovery integrity and stale snapshot fallback behavior.
    • Expanded recovery specifications to include per-consumer recovery horizons and retention floor management for more reliable data restoration.

…etain floor, driver

RFC 0008 §6.1/§6.6/§6.7 amendment: FrameSink::consume carries the
frame's WalOffset (per-consumer replay horizons — Parquet consumes
frames above the checkpoint X, the miner above its snapshot's
high-water mark S); housekeeping becomes an explicit API taking an
optional retain floor so truncation reclaims only segments wholly
below min(X, S) — the CHECKPOINT sidecar always records the true
Parquet horizon, since capping it would re-feed published records.
New RFC0008.7 retain-floor arm + scenario RFC0008.10 (startup
recovery driver: replay before listeners, routing, snapshot at
rotation). The §9 miner-snapshot open question is RESOLVED.

RFC 0001 §6.9 v2 amendment: the known-version branch now restores
the tree and replays only the WAL tail above S (the v1 full-replay
carve-out existed solely because Wal::checkpoint was a stub); the
v1 double-apply hazard is resolved by offset routing rather than by
refusing to restore. Stale-snapshot fallback: a WAL externally
truncated past S restores + replays survivors + emits a structured
warning naming the gap (hazard #5 surfaced via the RFC 0010 drift
query, never silent). New criteria §3.5.3 (restore-equivalence,
field-by-field via the snapshot payload of both trees) and §3.5.4
(loud degradation); §9 resume entry RESOLVED. No snapshot format
change — the high-water mark has been in the payload since v1.

Design decisions (maintainer, 2026-06-12): retain floor at
housekeeping over snapshot-before-checkpoint coupling; full
production driver in scope (serve() replays before listening).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jensholdgaard
jensholdgaard requested a review from Copilot June 12, 2026 11:25
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@jensholdgaard, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 33 minutes and 56 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 47b4cf68-20b4-4e30-acd7-a418509468a2

📥 Commits

Reviewing files that changed from the base of the PR and between 5e232a4 and 86553e0.

📒 Files selected for processing (2)
  • docs/rfcs/0001-template-miner.md
  • docs/rfcs/0008-wal.md
📝 Walkthrough

Walkthrough

Two RFCs are amended to align snapshot recovery: RFC 0008 introduces WAL retain-floor truncation and per-consumer replay horizons via new Wal::housekeeping(retain_floor), updated Wal::replay(&mut self), and offset-carrying FrameSink::consume signatures. RFC 0001 switches known-version recovery to v2 restore-plus-tail-replay, applying only WAL frames above the snapshot high-water mark, with stale-snapshot fallback and structured warnings.

Changes

Snapshot Recovery Semantics Alignment

Layer / File(s) Summary
RFC 0008 Public API contract for housekeeping, replay, and frame routing
docs/rfcs/0008-wal.md
New Wal::housekeeping(retain_floor: Option<WalOffset>) method introduced. Wal::replay signature changes from &self to &mut self. FrameSink::consume updated to accept (offset, kind, payload) to enable per-consumer replay routing.
RFC 0008 retain-floor and per-consumer horizon design
docs/rfcs/0008-wal.md
Retain-floor truncation bounds housekeeping unlinks to min(checkpoint, retain_floor), preserving frames the latest snapshot requires. Design sections rewrite checkpoint, housekeeping, and replay semantics to pass WalOffset to sinks, enabling Parquet (all frames ≥ checkpoint) and miner (only frames ≥ snapshot offset) to enforce different replay cutoffs and prevent double-apply.
RFC 0008 acceptance criteria and test plans for retain floor and RFC0008.10 recovery
docs/rfcs/0008-wal.md
New RFC0008.10 acceptance scenario specifies startup recovery with per-consumer horizons, completion before listeners open, and per-horizon delivery counts. Test plans add retain-floor sub-cases verifying segment preservation and reclamation, and integration test for snapshot-cadence rotation behavior.
RFC 0001 acceptance criteria for restore-plus-tail-replay equivalence
docs/rfcs/0001-template-miner.md
§3.5.3 enforces tree equivalence between restore-plus-tail-replay and full rebuild with "no double-apply" constraint. §3.5.4 specifies stale-snapshot loud failure: if WAL is truncated past snapshot high-water mark, recovery falls back to replaying survivors and emits structured warning describing the gap.
RFC 0001 recovery amendment and known-version algorithm rewrite
docs/rfcs/0001-template-miner.md
§6.9 recovery amendment switches to v2 restore path: known-version restores snapshot and replays only WAL tail above high-water mark S, using per-consumer offset routing and WAL retain-floor. Rewritten algorithm includes stale-snapshot fallback with structured warnings; snapshot format unchanged from v1.
RFC 0001 testing strategy and resolved questions
docs/rfcs/0001-template-miner.md
Testing strategy extended with restore-equivalence test verifying tree equality vs. full rebuild, asserting no frame at/below S re-applied, and validating stale-snapshot warning output. Open question "Resume-from-high-water-mark replay" marked resolved: v2 restore-plus-tail-replay enabled, aligning acceptance criteria with §6.9 amendment and RFC 0008 per-consumer horizons.

Possibly Related PRs

  • jensholdgaard/ourios#65: Rewrites RFC 0008 §5 acceptance criteria and recovery/corruption test expectations for WAL recovery and truncation semantics that this PR now formalizes in the retain-floor + per-consumer horizon contract.
  • jensholdgaard/ourios#123: Implements Wal::replay(&mut self) with crash-recovery scanning and healing logic, directly corresponding to the RFC 0008 signature change and per-consumer frame routing this PR defines.

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Two RFCs find harmony in the moonlight,
One tends the snapshots, one guards the WAL's height,
Restore-and-replay, no frames double-fold,
Per-consumer horizons keep recovery bold!
The stale snapshots speak, structured warnings ring true—
A coordinated dance, old and new.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically describes the main changes: RFC amendments specifying snapshot restore v2 with offset sink, retain floor, and recovery driver implementation.
Description check ✅ Passed The description is comprehensive and well-structured, covering the What, Why, design decisions, invariants, and implementation plan. However, the Checklist section from the template is missing entirely.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/rfc0008-rfc0001-snapshot-restore-v2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (2)
docs/rfcs/0008-wal.md (1)

835-839: 💤 Low value

Minor: Inconsistent terminology—use X instead of "checkpoint" in the per-consumer horizon explanation.

Line 835–839 explains per-consumer routing using "checkpoint" and "(checkpoint, S]", while the rest of §6.6 and §6.7 use variable names X (Parquet checkpoint offset) and S (miner snapshot high-water mark). Aligning the terminology—e.g., "frames in (X, S] are already folded into the snapshot"—would improve clarity and reduce the cognitive load of mapping variable names.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/rfcs/0008-wal.md` around lines 835 - 839, Replace the informal term
"checkpoint" with the established variable name `X` in the per-consumer horizon
explanation so it matches §6.6/§6.7; specifically, update the sentence that
reads "frames in `(checkpoint, S]` are already folded into the snapshot" to use
`(X, S]` and ensure surrounding text consistently uses `X` as the Parquet
checkpoint offset alongside `S` (miner snapshot high-water mark).
docs/rfcs/0001-template-miner.md (1)

2477-2488: 💤 Low value

Enumerate the snapshot payload fields compared in the restore-equivalence test.

The test description says the recovered state is "compared field-by-field via the §6.9 snapshot payload" but does not enumerate which fields. The snapshot format (lines 2272–2276) lists: tree leaves, template_id, template_version, (severity_number, scope_name) key, slot_types, structured-template-id map, and high-water mark. Adding a brief parenthetical here — e.g., "(leaves, template versions, slot types, structured-template map)" — would make the test intent clearer without requiring the reader to cross-reference §6.9.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/rfcs/0001-template-miner.md` around lines 2477 - 2488, Update the
"Restore-equivalence test" description to explicitly list the §6.9 snapshot
payload fields that are compared; add a brief parenthetical after "compared
field-by-field via the §6.9 snapshot payload" naming the fields (e.g., leaves,
template_id/template_version, (severity_number, scope_name) keys, slot_types,
structured-template-id map, and high‑water mark) so the test intent is clear
without cross-referencing §6.9.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/rfcs/0001-template-miner.md`:
- Around line 485-497: The paragraph claiming "the data side is complete
(everything truncated was ≤ `X`, hence in Parquet)" in the stale-snapshot
fallback needs an explicit ordering clarification between the miner snapshot
high-water mark `S` and the Parquet checkpoint `X`: either (a) state the design
invariant "S ≤ X" is enforced by the downstream consumer layer (so the claim
holds), or (b) change the sentence to a conditional that only asserts
completeness when "X ≤ S" and explain the fallback behavior/risk if "X > S"
(i.e., frames in (S, X] may be missing). Update the amendment text around the
stale-snapshot fallback and the sentence referencing `S` and `X` accordingly.

---

Nitpick comments:
In `@docs/rfcs/0001-template-miner.md`:
- Around line 2477-2488: Update the "Restore-equivalence test" description to
explicitly list the §6.9 snapshot payload fields that are compared; add a brief
parenthetical after "compared field-by-field via the §6.9 snapshot payload"
naming the fields (e.g., leaves, template_id/template_version, (severity_number,
scope_name) keys, slot_types, structured-template-id map, and high‑water mark)
so the test intent is clear without cross-referencing §6.9.

In `@docs/rfcs/0008-wal.md`:
- Around line 835-839: Replace the informal term "checkpoint" with the
established variable name `X` in the per-consumer horizon explanation so it
matches §6.6/§6.7; specifically, update the sentence that reads "frames in
`(checkpoint, S]` are already folded into the snapshot" to use `(X, S]` and
ensure surrounding text consistently uses `X` as the Parquet checkpoint offset
alongside `S` (miner snapshot high-water mark).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a9b3908e-38a4-4cf2-a0a5-d848120d8a84

📥 Commits

Reviewing files that changed from the base of the PR and between fa646a3 and 5e232a4.

📒 Files selected for processing (2)
  • docs/rfcs/0001-template-miner.md
  • docs/rfcs/0008-wal.md

Comment thread docs/rfcs/0001-template-miner.md

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR amends RFC 0008 (WAL) and RFC 0001 (template miner) to specify snapshot restore v2 with per-consumer replay horizons, a housekeeping(retain_floor) API to prevent WAL truncation from outrunning miner snapshots, and a production startup recovery driver contract.

Changes:

  • RFC 0008: specify offset-carrying FrameSink::consume, explicit Wal::housekeeping(retain_floor), and a new startup recovery driver scenario (RFC0008.10).
  • RFC 0001: switch the known-version recovery branch to restore + tail replay, add stale-snapshot loud-degradation behavior, and add acceptance criteria (§3.5.3/§3.5.4).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.

File Description
docs/rfcs/0008-wal.md Updates WAL API/spec to support snapshot-restore v2 (offset sink, retain floor, driver scenario).
docs/rfcs/0001-template-miner.md Updates miner persistence/recovery spec to enable restore+tail replay v2 and adds new acceptance criteria.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread docs/rfcs/0008-wal.md Outdated
Comment thread docs/rfcs/0008-wal.md Outdated
Comment thread docs/rfcs/0008-wal.md Outdated
Comment thread docs/rfcs/0008-wal.md Outdated
Comment thread docs/rfcs/0001-template-miner.md Outdated
Comment thread docs/rfcs/0001-template-miner.md Outdated
Comment thread docs/rfcs/0001-template-miner.md Outdated
…er-consumer in the driver

Review round 1 — Copilot caught a real inconsistency: the in-replay
checkpoint skip made the retain floor useless (a lagging snapshot's
(S, X] frames were retained on disk but never deliverable, so the
miner could not close its state gap). Suppression moves out of
Wal::replay into the recovery driver: replay delivers every
well-formed surviving frame with its offset; the driver suppresses
per consumer (Parquet > X via the new last_checkpoint accessor,
miner > S), handling both orderings of S and X. RFC0008.7 arm 2
reworded accordingly (sidecar survives; the driver's Parquet-side
suppression uses it); RFC0008.10 gains a lagging-snapshot catch-up
arm. Also: the stale-fallback example is corrected to manual WAL
segment deletion (deleting snapshots triggers full-replay fallback,
not a truncated WAL), and §3.5.4 introduces X explicitly, scoping
'data side complete' to truncation that never exceeded the
checkpoint.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comment thread docs/rfcs/0008-wal.md Outdated
Comment thread docs/rfcs/0008-wal.md Outdated
…ording in §6.7

Copilot round 2: a present-but-invalid CHECKPOINT (bad magic, unknown
version, non-zero flags, wrong size) is a structured corruption error
that aborts recovery — silently treating it as None would drop the
Parquet suppression horizon and duplicate every already-published
record; removal by the operator is an explicit acceptance of
at-least-once re-publish. The §6.7 durability paragraph now speaks in
driver-suppression terms instead of the retired skip-inside-replay
model.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comment thread docs/rfcs/0008-wal.md
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jensholdgaard
jensholdgaard merged commit f75d001 into main Jun 12, 2026
11 checks passed
@jensholdgaard
jensholdgaard deleted the docs/rfc0008-rfc0001-snapshot-restore-v2 branch June 12, 2026 12:24
jensholdgaard added a commit that referenced this pull request Jun 12, 2026
…ver (#187)

* feat(miner): snapshot restore v2 — recover() returns the state, restore_tenant rebuilds the tree

RFC 0001 §6.9 v2 (amended 2026-06-12, PR #185): the known-version
branch now restores instead of discarding. recover() returns
(Option<SnapshotState>, RecoveryOutcome) — Restored replaces
KnownVersionDiscarded; the v1 discard-contract test is retired per
the RFC-gated amendment and replaced by the restore contract.

MinerCluster::restore_tenant rebuilds a tenant tree from a
SnapshotState: leaves re-descend by their creation-time masked path
(a path-position wildcard resolves to its singleton mask tag —
widening/type-expansion are impossible at path positions because
candidates share their first walk_depth masked tokens by
construction); structured-template map and template_count rebuilt;
the cluster-wide template_id allocator bumps past every restored id.
Semantically inconsistent snapshots (empty template, slot-count
mismatch, non-mask path slot) are RestoreError::Inconsistent — the
driver treats them as corrupt (discard, full replay) per §6.9.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(ingester): startup recovery driver + per-tenant snapshot store (RFC 0008 §6.6 / RFC0008.10)

recovery::recover restores each tenant's snapshot into the miner
(uuid-parse failures and restore_tenant rejections discard the
artefact as corrupt — §6.9), then replays the WAL through a sink
that decodes, fans out, and feeds the miner only frames above that
tenant's high-water mark; the Parquet horizon (last_checkpoint) is
read and reported — its consumer joins when the Parquet write path
does. Stale-gap detection per §3.5.4: S below the checkpoint with
S's segment absent from the replayed set (internally unreachable
under the §6.7 retain floor; a hit means external mutation and is
surfaced, never silent — hazard #5). A frame that fails decode or
fan-out stops replay loudly: it was valid when acked, so this is
corruption-adjacent.

snapshot_store: <wal_root>/snapshots/<tenant>.snap artefacts, atomic
tmp -> fsync -> rename -> parent fsync; load_all returns raw bytes
(version dispatch stays in recover). Journal::sync now returns the
durable offset so the pipeline can track last_durable for the
shutdown-cadence snapshot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(server): recovery before listeners; snapshot cadence at post-recovery + shutdown

serve() now runs the RFC0008.10 sequence: open WAL, restore + replay
through the recovery driver, warn per stale-gap tenant (stderr — the
documented stopgap until structured logging lands), write fresh
snapshots at the replayed high-water, and only then construct the
pipeline and bind both listeners. ReceiverHandle::shutdown writes
snapshots again after both listeners stop; a write failure degrades
to a warning — the snapshot is a rebuildable cache (§6.9), never
durable state. Per-rotation cadence stays blocked on RFC0008.6.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test: land RFC0001 §3.5.3/§3.5.4 and the RFC0008.10 driver scenario

Restore-equivalence (restored + tail-replayed miner equals a
from-scratch control, with the suppression counter proving no frame
at or below S reached it), corrupt-version full-replay fallback,
the stale-gap arm (external segment deletion past S with a
checkpoint above it -> loud report, survivors still fold), cold
start, and the served-binary end-to-end (pre-populated WAL +
snapshot -> serve -> live export -> shutdown artefacts equal a
control).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(ingester,miner,server): restore requires a concrete horizon; seed last_durable from recovery

Review round 1:
- a known-version snapshot whose wal_high_water is absent or
  unparseable is discarded (full replay) instead of restored —
  restoring without a horizon cannot suppress, which is exactly the
  v1 double-apply hazard (§6.9 maps it to the discard class)
- IngestPipeline::with_last_durable seeds the recovered high-water,
  so a zero-traffic shutdown no longer overwrites the post-recovery
  snapshots with a horizonless artefact that would force full replay
- restore_tenant rejects duplicate template_ids (across leaves +
  structured) and duplicate structured (severity, scope) keys as
  Inconsistent
- load_all skips non-file *.snap entries instead of aborting recovery
- colocated unit tests for recovery.rs (parse_high_water, the
  extracted stale_gap classification helper, sink rejection)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(server): shutdown doc reflects the handle retaining the pipeline

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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