Skip to content

[WIP] Alpenglow branch engine: execute-on-receipt replay, wear-first durable store, local cert assembly - #246

Open
7layermagik wants to merge 11 commits into
koro/alpenglow-fork-choicefrom
7layer/alpenglow-forkchoice
Open

[WIP] Alpenglow branch engine: execute-on-receipt replay, wear-first durable store, local cert assembly#246
7layermagik wants to merge 11 commits into
koro/alpenglow-fork-choicefrom
7layer/alpenglow-forkchoice

Conversation

@7layermagik

@7layermagik 7layermagik commented Jul 6, 2026

Copy link
Copy Markdown

Alpenglow branch engine: execute-on-receipt replay, wear-first durable store, local certificate assembly

Status: work in progress — opened to document progress, not yet ready to merge.
This branch is the Alpenglow-only branch engine built on top of koro's fork-choice
layer. It is functionally complete and unit-tested, but it cannot yet be soak-tested
end to end
because the shreds-only catchup/repair path that would let it run against
the live cluster without leaning on RPC block fetch is not part of this branch (see
Next step). Review is welcome; treat it as a checkpoint.

This PR turns Mithril into an Alpenglow-only node: blocks execute the moment they are assembled, certificates gate durability rather than execution, and the storage layer is redesigned around batched log-structured folds for low NVMe wear and cheap crash recovery. TowerBFT support is deleted (it remains on dev).

Base: the Alpenglow fork-choice layer (koro)

This branch builds on koro/alpenglow-fork-choice (which supersedes the earlier smcio/alpenglow experimentation that contributed the initial Alpenglow scaffolding plus the native turbine/gossip/repair path). Koro's layer provides:

  • ChainTracker — the certificate-driven decision core: block/skip/conflict decisions per the whitepaper's safety lemmas (unique-strength certs, finalized-ancestry derivation, indirect skips, conflict = halt with write-once evidence).
  • Certificate verification — aggregate BLS over base2/base3 signer bitmaps, validator-set/stake checking, and the Agave votor wire codec validated against agave-votor-messages fixtures.
  • Votor QUIC receiver for live vote/cert traffic, block-id hints into the turbine assembler, and the exact-BlockKey promotion gate with restart evidence.

What this PR adds (eleven commits, one per line)

  1. Alpenglow-only rewire — deletes pkg/forkchoice, buffered/confirmation-gated execution, consensus-managed Lightbringer, the consensus mode enum, and the authorized-voters plumbing (~−6k LOC).
  2. Wear-first durable store + WorkingSet — K-slot union-deduped batch folds (one sequential segment, one fsync, one atomic index flip); the fold manifest doubles as commit record, index redo log (WAL-off capable), undo-pointer log, and resume-context carrier, so recovery rebuilds the durable frontier from the store itself (kill −9 no longer forces re-bootstrap). Undo pointers enable rewind to any retained fold boundary and a pinned, budgeted compactor. In RAM, the WorkingSet holds the unrooted suffix with a per-slot undo journal (fork switch = evict + re-execute; model-tested).
  3. Certificate pool — local cert assembly from raw votes at Agave parity (thresholds, unions, vote budgets, fallback-only base3 assembly, disjointness; cross-checked against anza-xyz/alpenglow votor sources and SIMD-0326). Hard trust boundary: unverified votes never touch dedupe/equivocation/tally state. Verification is lazy-batched (one pairing per same-payload tally) but always trigger-fresh: tallies fold the moment any Votor fallback-trigger predicate (SafeToNotar/SafeToSkip, transcribed from Agave) could pass — one fold policy for observer and future voting nodes alike.
  4. Block source — certified rewind for fork switches, mode-independent certified skips, and a cert-driven repair loop that steers turbine toward the most decisive certified-but-unobserved block per slot.
  5. Execute-on-receipt replay — the switch sweep (executed identity vs decisive certs, gated on a decision version that also advances on replay-derived decisiveness), in-RAM unwind with guarded fallbacks, and the dual watermark: folds ≤ min(certificate finality, trailing-verifier watermark). Alpenglow certs attest block data, not execution, so a trailing verifier re-derives per-tx digests from finalized RPC blocks and is the execution-correctness oracle; divergence = evidence + halt; loop and shutdown share one gated fold path.
  6. Node wiring — cluster gate, fold-recovery-before-integrity-check startup ordering, interrupted-rewind completion, --rewind-to-slot, evidence-triggered store rewind in the recovery loop, verifier/fold/compaction config, Alpenglow-only config generators, and the engine doc (docs/alpenglow_branch_engine.md).
  7. Operator-focused terminal stats — the 100-slot summary and per-slot lines rebuilt around the native-shred path (Agave is_full terminology): shred timing, repair counts, fork-switch outcomes, consensus/safety watermarks, promotion-hold signal; RPC-only mode fabricates nothing. Detailed debugging moved to file logs.
  8. Verifying/validator node modes + current defaultsconsensus.mode = "verifying" (default, non-voting) or "validator" (selectable now: enforces identity + vote-account keypairs, turbine source, gossip entrypoint, and the Votor QUIC listener at startup, runs verify-only with a loud warning until the voting engine lands). Cluster defaults to alpenglow, block source to turbine; config init [--validator] and the setup wizard generate per-mode configs.
  9. Fork-choice hardening (review response) — folds run async off the replay loop (single-flight worker; fork switches drain it; shutdown flushes through the same min(finality, verified) gate, with a proof test); stake-index appends are now slot-scoped/branch-safe (fold-time fsync'd flush BEFORE the batch commit, unwind drops wrong-fork entries); fork switches instrumented (in-RAM vs fallback with per-reason counts); the sweep's decision oracles pinned by a scenario matrix, which exposed and fixed a real gap: cert-less ancestry-finalized blocks are now decisive and repairable.
  10. Replay-relative per-slot shred timing (metrics) — per-slot lines report ready (assembly completion minus when replay asked for the slot; negative = pipeline ran ahead, positive = replay waited) and asm (first shred → full), and the 100-slot summary reports their distributions. Computed from timestamps the receiver already stamps — zero added tracking.
  11. General config hygiene — log-retention default is max_age_days = 0 (never delete by age; retention bounded by size × backups, the safer default for a validator you're debugging), and the RPC example comments in config.example.toml are generic placeholders. Catchup-specific config knobs (repair rate, rpc_fallback, snapshot staleness) are not here — they belong with the catchup work.

Next step (required before this can be tested)

Make catchup/repair work end-to-end without depending on RPC block fetch, so the node can be soak-tested against the live Alpenglow cluster in the shreds-only configuration this branch targets. That entire effort — the repair-first-catchup scaffolding and its hardening (turbine prewarm during snapshot build, an on-disk shred spool with prefetch hydration, repair-request pacing/admission control, peer quality scoring, and the catchup drive that fills the resume gap purely from shreds) — is staged on 7layer/catchup-repair (which is based on this branch) and will fold back in once it is validated. It is deliberately kept out of this PR because repair is not yet proven to catch up without the RPC safety net. Until then, unit tests are green but there is no full-cluster soak.

To do before merge

  • Live Alpenglow-cluster soak (blocked on the next step): kill −9 recovery, a real fork switch under the new instrumentation, shutdown mid-fold, verifier fail-closed under RPC cutoff, epoch-boundary equivalence, wear counters for K-tuning, then flip storage.index_wal off and enable compaction.
  • Clean up dead code inherited from the base branch. Several identifiers were already dead at koro/alpenglow-fork-choice and were left untouched here to keep this diff off koro's code: fetchResult.absentOK, the RPC skip-confirmation probe pipeline (confirmSlotAbsent / confirmSlotAbsentViaRPC / shouldProbeAbsentConfirmation / shouldFinalizeSkippedSlot, wired but never invoked), staleBackupResendEvery, lightbringerDialTimeout, rescueStaleWaitingSlot (+ staleWaitingSlotRetry), and the exported ForceRPCFallback (test-only). Remove them once the base lands, or coordinate with koro.

Verification

  • Full unit suite green across all touched packages, -race clean on the concurrency-heavy ones; crash-matrix tests with injected panics at every fold/rewind stage; randomized WorkingSet model test; pool-assembled certs pass the production wire verifier (incl. fallback-only/empty-base bitmaps).
  • Semantics cross-checked against the Agave alpenglow sources (consensus_pool, vote_pool, certificate_builder, common.rs) and SIMD-0326; wire codec against Agave-generated fixtures.
  • pkg/sbpf / pkg/sealevel / pkg/bankhash / pkg/fees / pkg/cu are byte-identical to the base commit (no VM changes).
  • Reviewed commit-by-commit (Codex); all findings addressed, including the follow-up hardening pass.

🤖 Generated with Claude Code

7layermagik and others added 11 commits July 4, 2026 16:14
…s plumbing

This branch targets Alpenglow clusters exclusively. Delete pkg/forkchoice
(vote parsing, confirmed-leaf resolution, skip-path heuristics) and every
caller: buffered/confirmation-gated execution, consensus-managed Lightbringer
handoff, the classic leaf-bankhash divergence check, and the consensus mode
enum (the engine collapses to a single Alpenglow observer constructor). The
EpochAuthorizedVoters state plumbing goes end-to-end (no non-forkchoice
consumer); old state files with the removed JSON field still load. The
fork-tree/branch-overlay replay engine files are deleted with it — the
replacement (WorkingSet suffix + certificate-driven switch) lands in the
following commits.

Net ~-6,000 LOC. TowerBFT clusters remain served by the dev branch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ction; WorkingSet suffix

State model: ONE canonical durable timeline plus a short in-RAM mutable
suffix. The WorkingSet (pkg/accounts) holds executed-but-unrooted slots as a
flat O(1)-read map with a per-slot undo journal; PromotePrefix folds the
rooted prefix out, EvictFrom unwinds a wrong suffix exactly (randomized
model-tested against a naive shadow). Siblings are never materialized as
state.

Durable commits (pkg/accountsdb) are K-slot batch folds: union-deduped
newest-wins deltas stream to ONE sequential segment (appendvec encoding, same
read path as bootstrap files) with ONE fsync and ONE atomic Pebble index
flip. The manifest is simultaneously the commit record, the index redo log
(enables running the index without a WAL), the undo-pointer log, and the
carrier of batch bankhashes + resume context — so RecoverFoldState rebuilds
the durable frontier from the store itself and a kill -9 no longer forces a
snapshot re-bootstrap. Crash matrix (injected panics at every commit stage)
tested; fileIds never reused; orphans GC'd against a bootstrap high-water
sidecar.

The undo-pointer log yields two capabilities: RewindToBatchBoundary restores
durable state to any retained fold boundary (late-detected divergence
recovery without a snapshot restart; crash-safe via parked manifests,
interruptible at any point), and CompactOnce reclaims dead bytes from
out-of-horizon segments and bootstrap appendvecs under a pin rule that keeps
every in-horizon undo target alive (rewind and compaction compose). Reads
retry once on a compaction race instead of panicking.

ResumeContext carries the running transaction count (presence-marked) so
resume and fork-switch unwinds restore it exactly. State-file writes are
power-loss durable (tmp + fsync + rename + dir fsync, the same discipline as
the fold manifests) — the epoch stakes persisted at boundaries have no other
durable home.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e chain queries

Local certificate assembly from raw votor votes (the latency accelerator —
footer certs remain the guaranteed input for unstaked observers), built to
Agave parity and cross-checked against anza-xyz/alpenglow votor sources and
SIMD-0326: thresholds (60/60/60/80/82), vote-to-cert unions
(notarize∪notar-fallback, skip∪skip-fallback), per-validator vote budgets
(notarize 1 block/slot, notar-fallback 3 distinct blocks), base/fallback
bitmap disjointness, fallback-only assembly (base3 with an empty base group),
and base2-when-base-only encoding. Pool-assembled certs pass the same
production verifier as wire certs; wire codec is validated by
agave-votor-messages fixtures.

TRUST BOUNDARY: raw votes are unauthenticated network input. Ingest only
shape-checks, bounds, and parks votes; dedupe, the vote-budget/equivocation
ledger, and disjointness mutate strictly AFTER BLS verification — a bogus
vote can neither suppress a victim rank's real vote nor forge equivocation
evidence. The vote window anchors to replay-observed progress (never to raw
vote slots) with hard per-slot and global memory caps.

Verification is lazy and batched (same-payload tallies verify with ONE
aggregate pairing, bisecting on failure) but ALWAYS trigger-fresh: the pool
folds the involved tallies the moment any Votor fallback-trigger predicate —
SafeToNotar(b): notar(b)>=40% OR (notar(b)>=20% AND notar(b)+skip>=60%);
SafeToSkip: skip+(notarTotal-topNotar)>=40%, transcribed from Agave
votor/src/common.rs — passes on candidate stake but not yet on verified
stake, including the cross-tally mixed condition. One fold policy for
observer and voting nodes alike; a voting engine reads VerifiedVotorStakes
and observes predicate crossings exactly when an eager-verification client
would, while sub-trigger tallies cost zero pairings.

ChainTracker additions: CertifiedBlockAt (unique-strength or finalized —
allocation-free; the switch sweep's oracle), SkipCertifiedAt, WantedBlocks
(one most-decisive unobserved repair target per slot), and a monotonic
decision version that advances on replay-derived decisiveness (parent links,
finalized ancestry, indirect skips), not just cert acceptance. The engine
collapses to the Alpenglow observer and wires the pool (emit -> tracker,
validator sets, epoch lookup, prune floor).

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

Execute-on-receipt keeps the block source's emission policy (first-assembled
candidate emits; certified-skip marking; pre-emission mismatch discard;
conflict halt) and adds the consensus-steered correction paths:

- RewindForAlpenglowSwitch rewinds the emission frontier when replay unwinds
  a certificate-contradicted suffix: purges buffered state at/above the
  switch slot and re-arms turbine toward the certified block id.
- Certified skips apply in ANY mode (RPC catchup / pre-handoff included) — a
  consensus fact survives block-source recreation on a post-switch re-replay,
  so a certificate-skipped slot is never re-fetched or re-run.
- Cert-driven repair: a near-tip loop services ChainTracker.WantedBlocks
  (certified-but-unobserved, the DECISIVE candidate per slot), pinning the
  assembler to certified block ids, pulling repair, discarding buffered
  candidates that carry a contradicted id, and cancelling shred state for
  certificate-skipped slots — rate-limited one nudge per slot per second.
  This also keeps re-hinting a certified sibling after a fork switch until
  its data arrives.

The consensus-managed Lightbringer path is gone (deleted with TowerBFT).

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

Blocks execute the moment they are assembled; certificates never gate
execution — they gate PROMOTION TO DISK and drive fork/skip corrections.

Dual watermark (the safety keystone): Alpenglow certificates attest block
DATA, not execution results, so a trailing verifier is the only execution-
correctness oracle. It re-derives compact per-transaction digests (fee,
status, balances; CU deliberately excluded) from finalized RPC blocks ~32
slots behind tip and compares against replay's run-local capture registry.
Folding is clamped to min(certificate finality, verified watermark) through
ONE shared gated path used by both in-loop promotion and the graceful-
shutdown flush — a Ctrl+C can never fold a slot the loop would refuse.
Promotion re-evaluates every iteration (verified progress alone advances it)
and a verifier divergence halts regardless of finality progress, recording
persisted evidence that blocks re-folding the disputed slot after restart.
Required-verifier + RPC outage stalls folds into the bounded-tail halt:
fail-closed by design.

Fork switch: a sweeper walks the executed-unfolded window whenever the
tracker's decision version advances and reports the first slot whose executed
identity a decisive certificate contradicts (wrong sibling or certified
skip). The common case unwinds IN RAM — WorkingSet suffix eviction, resume
state rebuilt from the retained parent context (the true executed parent,
across skipped slots), block source rewound, capitalization/block-height/
transaction-count restored — and the certified version re-executes without a
process restart. Guards force the rooted-checkpoint fallback instead when the
switch is unsafe in-RAM, each tested: cross-epoch spans, the partitioned-
rewards window, missing parent context, and ANY vote/stake-cache write above
the rooted watermark (the caches cannot be rolled back, and the resume
path's cache reload reads durable, which cannot see retained-suffix writes).
A ResumeContext field tripwire forces every new context field to be wired
through resume + unwind restoration.

Crash/stop-resume completeness: epoch stakes persist to the state file AT
the epoch boundary (previously only on graceful shutdown — a hard crash
after a boundary forced a snapshot re-bootstrap for the rest of the run);
durable promotion HOLDS below the boundary block while partitioned rewards
distribute (the RAM-only bookkeeping is not reconstructible mid-window, so a
resume must re-run the boundary); the graceful-cancel state save re-runs
after the shutdown flush so the state file never trails the store.

Promotion folds rooted slots K at a time through the batch committer with the
chunk-top resume context riding in each fold manifest (a context-less fold is
refused — it would be unrecoverable). rpcclient gains the single-attempt
finalized block fetch the verifier uses.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Startup: the cluster gate (this build boots network.cluster="alpenglow"
only; rooted-durable is forced on), fold recovery BEFORE the bankhash
integrity check (manifest replay restores NoSync rows so a hard kill cannot
falsely condemn a healthy store), state-watermark reconcile against the
store's durable frontier, interrupted-rewind completion (parked manifests
resume uniformly regardless of crash point), --rewind-to-slot, the trailing-
verifier [verifier] config, fold batch sizing, and opt-in background
compaction with validator-safe budgets.

Recovery loop: certificate-switch and finality-mismatch re-replays from the
rooted checkpoint with a bounded budget; a divergence at/below the durable
watermark rewinds the store to the newest retained boundary below it instead
of re-bootstrapping (the reason durable state deliberately lags). The retry
passes the failed run's serialized epoch stakes through verbatim (raw
PersistedEpochStakes JSON, the state-file convention — the consumer
json.Unmarshals them directly).

Config generators (config init / setup wizard / editor / doctor / migrate)
emit Alpenglow-only configs: turbine as the live source (RPC is catch-up/
debug — RPC blocks carry no block ids or footer certs), live [consensus]
keys, and TowerBFT clusters marked as dev-branch builds.

docs/alpenglow_branch_engine.md replaces the deleted TowerBFT fork-choice doc:
the engine model, storage design, Agave-parity certificate semantics, and the
voting-readiness contract (single fork choice; the voting engine plugs in
above via VerifiedVotorStakes, the cert emit callback, and tracker queries).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rework the 100-slot summary and per-slot lines around the signals an
Alpenglow operator actually watches, using Agave terminology: a slot is
'full' when all its data shreds are present and the block is reconstructable
(SlotMeta/is_full) — not finalized.

Summary (turbine): source; slots/sec with 'behind latest shred: replay N,
full N' gaps; skipped + empty blocks (skips annotated with how many had
partial shreds); consensus (finalized slot, certificate switches); safety
(exec-checked slot from the trailing verifier, promotion holds — counted only
when promotion is fully stalled while finality has run a whole fold chunk
ahead, so healthy steady state reads 0); shred first/full timing
(median/max); repair totals; txns median/p90/max with cu/tx; cu
median/p90/max; execution median/p95/max with a >200ms count; efficiency in
ms/Mcu; resources (Linux RSS from /proc/self/statm, heap, per-window gc
count). RPC-only mode omits every shred metric — nothing is fabricated for
non-shred sources. Shred timings are self-anchored: deltas against the
fastest cadence-adjusted arrival (400ms nominal), rebased each window to
absorb drift, since there is no observable wall-clock slot schedule.

Per-slot lines gain shreds(first/full/repair) for shred-sourced blocks and
drop wait/vote-split/replay-total from the terminal. Skipped slots align
visually — and when the leader DID send shreds before the slot was skipped,
the line reports them (shreds(first +18ms, full --, repair 3, partial 12)):
'leader started transmitting then stopped' is a different operator story
from 'leader never showed up'. Partial arrivals are snapshotted whenever a
never-full slot's assembler state is dropped (reset, prune, cap eviction,
non-canonical discard) and retained for the completed-slot window. The old
detail (wait, vote/non-vote, COW, memory breakdown, getBlock fetch stats)
moves to file-only logs; the tip-stale WARNING stays on the terminal.

Plumbing: the turbine assembler stamps each slot's first-shred arrival and
full time and counts repair-delivered shreds (the receiver attributes a shred
to repair when it matches an outstanding request by responder+nonce),
attaching them to the assembled block; monotonic ShredEdges (latest shred
slot, highest full slot) and per-slot ShredObservation exposed through the
block source; the receiver's LastDataSlot is now a monotonic max so
out-of-order packets cannot move the reported edge backward.

Tests: percentile/format helpers, self-anchored delta math (fabrication-free
for missing full timestamps), and the exact per-slot/skipped line shapes —
including the partial-shreds skip case.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-mode config profiles

Two node types, selected by consensus.mode (one fork choice for both):

- "verifying" (default) — non-voting: observe, execute, verify. No keypairs.
- "validator" — selectable now, ahead of the voting engine: startup enforces
  the full voting-deployment shape (identity + vote-account keypairs, turbine
  block source, gossip entrypoint, Votor QUIC listener) and refuses to start
  listing anything missing, so validator deployments are provisioned once and
  stay valid when voting activates. Until then the node runs the verifying
  pipeline and warns loudly that NO votes are cast. The authorized-withdrawer
  keypair is deliberately NOT a runtime requirement (keep it offline).

Defaults brought current with the Alpenglow-only build:

- network.cluster defaults to "alpenglow" (mainnet-beta/testnet/devnet still
  recognized and pointed at the dev branch until those clusters upgrade).
- block.source defaults to "turbine" with the bind address defaulting to
  0.0.0.0:8001 — a bare 'mithril run' boots the live shred path. The
  lightbringer auto-switch keys on explicit-vs-defaulted source rather than
  the old literal "rpc" default.
- Stale flag help/comments fixed (--cluster, --block-source, min_node_version).

Config surfaces per node type:

- 'config init' (verifying) and 'config init --validator' generate tailored
  starter configs; the setup wizard asks the node type first, always collects
  the gossip entrypoint (turbine needs it as much as the sidecar did), asks
  validator keypaths, and skips the Lightbringer question for validators.
- doctor and the dashboard validate consensus.mode and list exactly which
  validator requirements are missing; the config editor's sidecar-off
  fallback reverts to turbine, not rpc.
- config.example.toml gains an at-a-glance turbine deployment checklist
  mapping every --flag to its config key; README rewritten for this build:
  Alpenglow-only intro, both node types with their init profiles,
  turbine-first flow, honest current-limitations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, switch instrumentation, decisive-oracle completeness

Review-response hardening of the fork-choice path, four pieces:

ASYNC PROMOTION. CommitBatch (segment write + fsync + index flip, hundreds
of ms per K-slot chunk) was the replay loop's one remaining synchronous
stall. Promotion is split build/run/apply around a single-flight worker: the
loop builds an immutable fold job, a worker runs the durable half, the loop
applies completions — all WorkingSet/map mutation stays on the loop thread.
Fork switches DRAIN the promoter before evicting (a drained fold that moves
the frontier past the switch slot defers to node-level recovery — new
'durable-overlap' fallback reason); the shutdown flush drains then folds
synchronously through the SAME gate; every exit applies a drained completion;
a discarded-but-committed fold is the already-supported crash-after-commit
case. Proof test: force-flush folds through min(finality, verified) and not
one slot further.

BRANCH-SCOPED STAKE INDEX. stake_pubkeys.idx appends were per-block —
durable writes for blocks a certificate switch could still unwind, leaking
wrong-fork pubkeys into the index that enumerates the epoch-stakes scan,
plus per-block file syscalls on the hot path. Entries are now slot-keyed:
flushed (fsync'd) at fold time BEFORE the batch commit (crash = harmless
superset, never a scan-corrupting subset), merged into stake scans from RAM
meanwhile, dropped with the state on unwind, cleared on replay restart. An
order-asserting committer test proves flush-precedes-commit.

SWITCH INSTRUMENTATION. tryInLoopUnwind reports WHY it declined (nil-tail /
cross-epoch / rewards-window / vote-stake-dirty / missing-context /
context-rebuild / durable-overlap); the 100-slot summary splits switches
into in-RAM vs fallback with per-reason counts in the file log — the signal
for whether the in-RAM engine suffices or a branch-aware state engine is
actually needed. Guard-matrix tests assert the exact reasons.

DECISIVE-ORACLE COMPLETENESS. The switch sweep's oracles (CertifiedBlockAt /
SkipCertifiedAt) are now pinned by a scenario matrix — decisive notarize,
fallback-only ambiguity, explicit skip, indirect skip, ancestry
finalization, two-decisive conflict handoff — which exposed a real gap: a
block finalized only by ancestry that never received its own certificate
was invisible to the oracles (blockSlots indexes certified blocks only). A
per-slot finalized index makes it decisive and repairable, and the ancestry
walk mints a hash-bound stub for an exact-but-untracked parent (the
finalized child's header binding is protocol-final). The adversarial corner
— replay executed an equivocation twin, the finalized descendant is the
only evidence — now takes the cheap in-RAM switch with cert-driven repair
fetching the true block, instead of a blind spot ending in the fold-gate
halt and a full rooted re-replay.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces the cadence-anchored per-slot shred deltas with timing measured
against replay itself — the clock an operator actually cares about:

  ready < 0  the block finished assembling |ready|s BEFORE replay asked for
             it (pipeline ahead); > 0 means replay waited that long
  asm        first shred seen -> fully assembled

Computed from the two timestamps the receiver already stamps plus the
NextBlock wait timer replay already had (renamed waitStart -> neededAt),
so there is zero added tracking. The 100-slot summary reports ready
median/worst and asm median/max; the skipped-slot line drops its
anchor-based "first" delta and keeps the seen/repaired counts. The
cadence-anchor machinery (computeShredDeltas, shredAnchorCandidate,
nominalSlotNanos) is removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
General (non-catchup) config hygiene: the log-retention default is now
max_age_days = 0 (never delete by age) across the example config and the
config/setup generators — retention stays bounded by max_size_mb x
max_backups, which is the safer default for debugging a validator. The
config.example.toml RPC example comments become generic placeholders
rather than naming a specific provider.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@7layermagik
7layermagik force-pushed the 7layer/alpenglow-forkchoice branch from 258eb68 to 448e6ca Compare July 6, 2026 05:43
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