Skip to content

docs: record API-02 block observation audit - #13

Merged
krisoshea-eth merged 6 commits into
mainfrom
krisoshea/lod-12-api-02-docs
Aug 12, 2026
Merged

krisoshea-eth merged 6 commits into
mainfrom
krisoshea/lod-12-api-02-docs

Conversation

@krisoshea-eth

@krisoshea-eth krisoshea-eth commented Aug 10, 2026 •

Copy link
Copy Markdown
Owner

Summary

Records the API-02 block-observation audit and implementation decision in the Builder documentation.

  • Updates the Living Technical Note with the standard block plus getBlockV2 path and why head/head_v2 are insufficient.
  • Records Lodestar's post-import retrieval ordering and the cross-client ordering uncertainty.
  • Documents the bounded retry and FIFO deduplication policy.
  • Records exact post-Gloas bid preservation, including Heze-specific fields.
  • Records the Node 24.13.0 EventSource implementation and asynchronous connection-error boundary.
  • Records operator-visible unexpected stream closure while keeping recovery in REL-01.
  • Records the one-source-BN affinity requirement and routes multi-BN provenance to deferred LOD-37.
  • Records the Beacon API's lack of a standard SSE event ID or resumption contract and routes bounded and advanced reconciliation to REL-01 and LOD-41.
  • Notes that the sibling execution_payload event already uses the selection-identity field names, but emits them too late to trigger reveal.
  • Routes connected and reconnecting SIGTERM evidence to ENV-01.
  • Adds the API-02 decision-log row and Week 9 work-log entry.
  • Refreshes the live #9781 dependency and API-02 test evidence.
  • Links the implementation evidence posted to beacon-APIs #599 from the Week 9 work log.

The standard flow is sufficient for correctness without a Lodestar-specific endpoint. An enriched block event or dedicated bid_included event remains an evidence-backed efficiency and interoperability improvement, and a standard replay or reconciliation contract remains useful for recovery.

Implementation evidence

  • Lodestar draft PR #48
  • 25 focused BlockObserver tests
  • 31 passing Builder package tests on Node 24.13.0
  • ChainSafe/lodestar unstable baseline 713b21812905bb34e547ca85d5f630060179da67

Validation

  • Manually inspected the Markdown diff and links.
  • Ran git diff --check.

Related project issue: krisoshea-eth/lodestar#12

Summary by CodeRabbit

  • Documentation
    • Updated implementation planning and technical documentation with the latest August 2026 status and progress tracking.
    • Clarified block observation, retrieval, validation, deduplication, restart reconciliation, and source-selection requirements.
    • Documented current deferred failover, reconnect, and event-replay limitations.
    • Refreshed the work log with recent implementation progress, testing coverage, findings, and related references.

@linear-code

linear-code Bot commented Aug 10, 2026

Copy link
Copy Markdown

LOD-12

@coderabbitai

coderabbitai Bot commented Aug 11, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The documentation updates establish an August 11, 2026 baseline, record API-02 observation behavior and validation, update Builder review tracking, and define shutdown, source-affinity, reconciliation, replay, and failover requirements.

Changes

API-02 Builder observation

Layer / File(s) Summary
Block observation design
docs/implementation-plan.md, docs/living-technical-note.md, docs/work-log.md
The documents define the block SSE and getBlockV2 selected-bid flow, fork-aware responses, retries, deduplication, callback handling, and focused test coverage.
Implementation and review tracking
docs/implementation-plan.md, docs/living-technical-note.md, docs/work-log.md
Dates, Builder review details, API-02 delivery status, and implementation evidence now use the August 11 baseline.
Operational and reconciliation requirements
docs/implementation-plan.md, docs/work-log.md
The documents add shutdown checks, source affinity, bounded reconnect reconciliation, and deferred replay and failover conditions.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: markolazic01

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the documentation change that records the API-02 block-observation audit.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch krisoshea/lod-12-api-02-docs

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

@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

🤖 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/living-technical-note.md`:
- Line 882: Update the observer’s root-consumption policy so transient retrieval
retry exhaustion does not leave the root retained indefinitely: separate
in-flight deduplication from completed outcomes, and remove the root or schedule
a bounded retry when transient retries are exhausted. Continue retaining roots
for deterministic fork skips and malformed responses.
🪄 Autofix

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: e0630f60-6119-45f2-9264-5fdd5ac42d8d

📥 Commits

Reviewing files that changed from the base of the PR and between 4280495 and 4bb0044.

📒 Files selected for processing (4)
  • docs/beacon-apis-599-evidence-comment.md
  • docs/implementation-plan.md
  • docs/living-technical-note.md
  • docs/work-log.md

Comment thread docs/living-technical-note.md Outdated

@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: 2

🤖 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/beacon-apis-599-evidence-comment.md`:
- Line 13: Update the evidence comment to state the authoritative retry limit
and backoff policy, plus that block-root deduplication preserves first-seen FIFO
order. If these rules are defined elsewhere, link to the authoritative API-02
policy instead of leaving the bounded-retry and FIFO guarantees implicit.
- Around line 11-15: Update the evidence conclusion around the replay discussion
to state that reconnect, replay, and restart recovery are deferred to REL-01. Do
not imply that adding a stable replay identifier alone provides recovery;
describe replay as requiring defined SSE retention/replay semantics or an
explicit reconciliation mechanism, while preserving the current bounded
block-plus-getBlockV2 fallback.
🪄 Autofix

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: babad509-2c97-446c-8307-c16305bbb88a

📥 Commits

Reviewing files that changed from the base of the PR and between 4bb0044 and b95e0ea.

📒 Files selected for processing (4)
  • docs/beacon-apis-599-evidence-comment.md
  • docs/implementation-plan.md
  • docs/living-technical-note.md
  • docs/work-log.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/work-log.md
  • docs/implementation-plan.md

Comment on lines +11 to +15
3. Lodestar event frames contain `event` and `data` but no SSE `id`. EventSource can reconnect, but standard `Last-Event-ID` resumption cannot identify or replay a selection notification missed during the disconnect. A Builder therefore needs a separate bounded reconciliation path, and longer gaps or competing roots require a stronger contract than transport reconnection alone.

The implementation retries 404, server, timeout, and transport failures; does not retry other 4xx responses, cancellation, or response-decoding failures; and deduplicates block roots before retrieval. This works without a Lodestar-specific endpoint or a required Beacon API change.

The implementation evidence therefore supports treating either an enriched `block` event carrying selection identity or a dedicated `bid_included` event as an efficiency and interoperability improvement. It would remove the per-block fetch and give external Builders a more explicit cross-client contract. A stable replay identifier or an explicitly specified reconciliation mechanism would also make reconnect behavior interoperable, while the existing `block` plus `getBlockV2` path remains a functional bounded fallback.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Define replay semantics, not only a replay identifier.

Line 11 correctly states that the current frames cannot support Last-Event-ID resumption. A stable identifier alone does not provide replay. The server must emit an SSE id and define event retention and replay, or provide reconciliation. State that this recovery is deferred to REL-01 so the evidence comment does not imply that the current implementation provides it.

As per PR objectives, reconnect, replay, and restart recovery remain deferred to REL-01.

🤖 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/beacon-apis-599-evidence-comment.md` around lines 11 - 15, Update the
evidence conclusion around the replay discussion to state that reconnect,
replay, and restart recovery are deferred to REL-01. Do not imply that adding a
stable replay identifier alone provides recovery; describe replay as requiring
defined SSE retention/replay semantics or an explicit reconciliation mechanism,
while preserving the current bounded block-plus-getBlockV2 fallback.

2. Lodestar emits the event after state transition and fork-choice import. Its retrieval path can serve the block from fork choice, the seen-block input cache, or the database, and the cache is pruned only after the database write. We therefore do not expect a 404 window on Lodestar. The Beacon API does not specify that event delivery must follow retrievability, and another client may choose a different emission point. A BN-agnostic Builder still needs bounded retry for an event-before-block window.
3. Lodestar event frames contain `event` and `data` but no SSE `id`. EventSource can reconnect, but standard `Last-Event-ID` resumption cannot identify or replay a selection notification missed during the disconnect. A Builder therefore needs a separate bounded reconciliation path, and longer gaps or competing roots require a stronger contract than transport reconnection alone.

The implementation retries 404, server, timeout, and transport failures; does not retry other 4xx responses, cancellation, or response-decoding failures; and deduplicates block roots before retrieval. This works without a Lodestar-specific endpoint or a required Beacon API change.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

State the retry and deduplication policy precisely.

Line 13 lists retry categories and says roots are deduplicated. It does not state the retry limit, backoff, or FIFO deduplication behavior. Add those rules or link to the authoritative API-02 policy. Without them, this evidence comment does not substantiate the bounded-retry and FIFO guarantees.

As per PR objectives, this PR defines bounded retry and FIFO deduplication.

🤖 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/beacon-apis-599-evidence-comment.md` at line 13, Update the evidence
comment to state the authoritative retry limit and backoff policy, plus that
block-root deduplication preserves first-seen FIFO order. If these rules are
defined elsewhere, link to the authoritative API-02 policy instead of leaving
the bounded-retry and FIFO guarantees implicit.

@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

🤖 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/work-log.md`:
- Line 119: Update the work-log entry describing beacon-APIs `#599` to say the
implementation evidence was staged for manual posting, not posted. Remove the
issue-comment URL while it remains unpublished, and preserve the surrounding
correctness and efficiency context.
🪄 Autofix

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: c5fbd9ab-ed22-4147-b8bd-191785489e3f

📥 Commits

Reviewing files that changed from the base of the PR and between 3af0923 and bed12b7.

📒 Files selected for processing (3)
  • docs/implementation-plan.md
  • docs/living-technical-note.md
  • docs/work-log.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/implementation-plan.md
  • docs/living-technical-note.md

Comment thread docs/work-log.md
- Began `API-02` from Lodestar `unstable` at `1dde9abaa66ca4d4f3500e8fb06106fa635a066d`, then merged the current `unstable` baseline at `713b21812905bb34e547ca85d5f630060179da67`. Audited the standard `block` SSE payload, Lodestar's post-import emission order, `getBlockV2` fork metadata and retrieval path, and the accepted-bid notification gap in beacon-APIs #599.
- Implemented a Builder `BlockObserver` that subscribes through the source BN REST client, retrieves post-Gloas signed blocks by root with bounded retry, deduplicates concurrent and sequential observations, preserves exact bid values, and hands observations to isolated registered callbacks without adding p2p, reveal, selection, metrics, or recovery behavior.
- Added 25 focused tests covering stream wiring, Gloas and Heze fork-correct output, sequential and concurrent duplicates, event-before-block 404 retry, retry exhaustion, error classification, cancellation, unsupported forks, metadata/body disagreement, bounded eviction, self-build sentinel handling, unexpected stream closure, callback isolation, and shared abort-signal use. The complete Builder package suite now contains 31 passing tests.
- Confirmed that standard `block` plus `getBlockV2` is sufficient for correctness. Posted the implementation evidence to [beacon-APIs #599](https://github.com/ethereum/beacon-APIs/issues/599#issuecomment-5257353985); an enriched event remains an efficiency and interoperability improvement rather than a prerequisite.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Record the #599 comment as staged, not posted.

Line 119 says that the implementation evidence was posted. The PR objective says that the comment is staged for manual posting after the related Lodestar PR and evidence are stable. Change “Posted” to “Staged” and add the issue-comment URL only after publication.

🤖 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/work-log.md` at line 119, Update the work-log entry describing
beacon-APIs `#599` to say the implementation evidence was staged for manual
posting, not posted. Remove the issue-comment URL while it remains unpublished,
and preserve the surrounding correctness and efficiency context.

@krisoshea-eth
krisoshea-eth merged commit 709db03 into main Aug 12, 2026
1 check passed
nflaig pushed a commit to ChainSafe/lodestar that referenced this pull request Sep 10, 2026
## Motivation

The Lodestar Builder needs a source beacon node API path for learning
whether its bid was selected without joining libp2p. This PR implements
the standard `block` SSE topic plus `getBlockV2` as a bounded
compatibility path.

Beacon APIs #630 and merged Lodestar #9832 also forward the signed
winning block directly to the selected external Builder. The main
purpose of that flow is to let the Builder help disseminate the block,
with timely win notification as an additional benefit. Maintainer input
is requested on whether this observer should remain as a compatibility
fallback alongside direct delivery.

## Changes

- Subscribe only to the standard `block` SSE topic.
- Deduplicate roots before asynchronous work and retain a FIFO window of
256 roots.
- Skip locally pre-Gloas slots and retrieve each new post-Gloas block by
root with `getBlockV2`.
- Use the Builder abort signal for both the SSE stream and block
requests.
- Retry 404, server, timeout, and non-input transport failures for up to
six attempts with five 200 ms delays. Other 4xx responses, cancellation,
decoding failures, input fetch errors, and structural failures are
terminal.
- Treat response version metadata as fork authority, require a
Gloas-compatible body, and verify the returned slot against the event.
- Preserve the exact signed bid, including later-fork fields,
exact-width values, and `BUILDER_INDEX_SELF_BUILD`.
- Dispatch observations concurrently through isolated `runOnBlock`
callbacks.

The existing API-client request timeout is unchanged. This PR bounds
attempts and explicit retry delays, not total wall-clock or
slot-relative time. Selection and reveal code will own deadline policy.

The observer starts after the existing genesis, configuration,
readiness, Gloas, and Builder identity gates. It joins the clock and
`BuilderStatusTracker` as a long-lived Builder background service and
shares their abort controller.

This PR does not add p2p, `block_gossip`, canonical-chain filtering,
local-bid matching, reveal behavior, metrics, reconnect, replay, restart
recovery, multi-BN failover, or a new API endpoint.

The observer intentionally evaluates blocks before SELECT-01 registers
its first production consumer so this compatibility path remains active
and evidenced. Event-time `executionOptimistic` comes from the
triggering event, while the response metadata is authoritative for the
fork. Terminally failed roots remain consumed until FIFO eviction;
REL-01 owns controlled reconciliation. Aggregate retrieval concurrency,
observer-specific metrics, and block-root recomputation before financial
decisions are tracked in SEC-01, QA-01, and SELECT-01 respectively.

## API behavior and compatibility

The standard `block` event contains the slot, beacon block root, and
execution optimism. `getBlockV2` supplies the signed fork-correct block
and `Eth-Consensus-Version` metadata. Imported non-head blocks remain
valid observations, so `head` and `head_v2` are not substitutes.

Lodestar emits `block` after state transition and fork-choice import.
Root lookup checks fork choice for presence, then serves the block from
the seen-block input cache or database. This ordering provides no
expected Lodestar event-before-block window, but the Beacon API does not
require equivalent ordering across clients, so bounded 404 retry remains
a cross-client precaution.

The implementation audit is recorded in merged [Builder docs PR
#13](krisoshea-eth/lodestar-eip-7732-builder-docs#13).
The provisional direct-Engine planning reconciliation is recorded in
merged [Builder docs PR
#18](krisoshea-eth/lodestar-eip-7732-builder-docs#18),
the reproducible real-BN and shutdown evidence in merged [Builder docs
PR
#19](krisoshea-eth/lodestar-eip-7732-builder-docs#19),
and the recent upstream PR audit in merged [Builder docs PR
#20](krisoshea-eth/lodestar-eip-7732-builder-docs#20).
Implementation evidence was posted to [beacon-APIs
#599](ethereum/beacon-APIs#599 (comment)).
Marco's open Lodestar PoCs
[#9854](#9854),
[#9875](#9875),
[#9876](#9876), and
[#9896](#9896) explore
optional event improvements separately.

The API-02 diff is limited to five Builder files. The latest review
fixes build on Nico's updated branch at
`09ea035863a5712eb417949f71e31c9d0f97f0fb`; no additional `unstable`
merge was made for these fixes.

Historical validation base: `f22c5ce63e`.

Historical specification baseline: consensus-specs `v1.7.0-alpha.14`.

Project issue:
[krisoshea-eth#12](krisoshea-eth#12).

## Testing

Validated locally on 2026-09-10 at
`d74cf21de82d8ef4a8ad4b65627d2df131b42a16`, with Node 24.13.0 and pnpm
11.0.0:

- 58 targeted tests across the observer, Builder lifecycle, identity,
readiness and payload store.
- Ordinary Builder package type-check.
- Changed-file Biome.
- Builder package build and module import check.
- `git diff --check`.

Dependencies were installed from this branch's lockfile and the Builder
dependency packages were rebuilt in an isolated checkout. The new
startup-log and warning regressions failed before the source changes and
passed afterward.

Coverage includes SSE wiring, cancellation, Gloas and Heze output,
signed-bid preservation, slot consistency, duplicate suppression,
bounded retry, decoding failures, FIFO eviction, self-builds, stream
failures and callback isolation. These are local results, not a fresh CI
or real-BN/EL run. The earlier real-BN and shutdown evidence remains in
Builder docs PR #19.

## AI assistance disclosure

- [x] External Contributors: I have read the contributor guidelines and
disclosed my usage of AI below.

> AI assistance was used for codebase research, implementation, testing,
and review. I reviewed and revised the submitted code and PR text, made
manual edits and technical decisions, and take responsibility for the
final result.
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