May 13, 7-8 AM (43)
May 13, 8-9 AM (44)
May 13, 9-10 AM (21)
May 13, 10-11 AM (22)
May 13, 11-12 PM (47)
May 13, 12-1 PM (25)
May 13, 1-2 PM (44)
May 13, 2-3 PM (57)
May 13, 3-4 PM (42)
May 13, 4-5 PM (33)
May 13, 5-6 PM (30)
May 13, 6-7 PM (51)
May 13, 7-8 PM (33)
May 13, 8-9 PM (9)
May 13, 9-10 PM (24)
May 13, 10-11 PM (30)
May 13, 11-12 AM (11)
May 14, 12-1 AM (18)
May 14, 1-2 AM (3)
May 14, 2-3 AM (4)
May 14, 3-4 AM (21)
May 14, 4-5 AM (11)
May 14, 5-6 AM (18)
May 14, 6-7 AM (18)
May 14, 7-8 AM (47)
May 14, 8-9 AM (53)
May 14, 9-10 AM (35)
May 14, 10-11 AM (20)
May 14, 11-12 PM (114)
May 14, 12-1 PM (54)
May 14, 1-2 PM (151)
May 14, 2-3 PM (32)
May 14, 3-4 PM (17)
May 14, 4-5 PM (14)
May 14, 5-6 PM (38)
May 14, 6-7 PM (12)
May 14, 7-8 PM (22)
May 14, 8-9 PM (37)
May 14, 9-10 PM (35)
May 14, 10-11 PM (27)
May 14, 11-12 AM (14)
May 15, 12-1 AM (18)
May 15, 1-2 AM (15)
May 15, 2-3 AM (5)
May 15, 3-4 AM (3)
May 15, 4-5 AM (13)
May 15, 5-6 AM (14)
May 15, 6-7 AM (10)
May 15, 7-8 AM (31)
May 15, 8-9 AM (23)
May 15, 9-10 AM (52)
May 15, 10-11 AM (71)
May 15, 11-12 PM (70)
May 15, 12-1 PM (73)
May 15, 1-2 PM (73)
May 15, 2-3 PM (66)
May 15, 3-4 PM (26)
May 15, 4-5 PM (13)
May 15, 5-6 PM (30)
May 15, 6-7 PM (29)
May 15, 7-8 PM (25)
May 15, 8-9 PM (8)
May 15, 9-10 PM (34)
May 15, 10-11 PM (34)
May 15, 11-12 AM (25)
May 16, 12-1 AM (2)
May 16, 1-2 AM (2)
May 16, 2-3 AM (3)
May 16, 3-4 AM (3)
May 16, 4-5 AM (0)
May 16, 5-6 AM (6)
May 16, 6-7 AM (2)
May 16, 7-8 AM (10)
May 16, 8-9 AM (1)
May 16, 9-10 AM (2)
May 16, 10-11 AM (1)
May 16, 11-12 PM (13)
May 16, 12-1 PM (11)
May 16, 1-2 PM (8)
May 16, 2-3 PM (15)
May 16, 3-4 PM (10)
May 16, 4-5 PM (2)
May 16, 5-6 PM (2)
May 16, 6-7 PM (2)
May 16, 7-8 PM (10)
May 16, 8-9 PM (6)
May 16, 9-10 PM (9)
May 16, 10-11 PM (29)
May 16, 11-12 AM (42)
May 17, 12-1 AM (9)
May 17, 1-2 AM (1)
May 17, 2-3 AM (0)
May 17, 3-4 AM (1)
May 17, 4-5 AM (0)
May 17, 5-6 AM (3)
May 17, 6-7 AM (2)
May 17, 7-8 AM (1)
May 17, 8-9 AM (1)
May 17, 9-10 AM (1)
May 17, 10-11 AM (6)
May 17, 11-12 PM (6)
May 17, 12-1 PM (4)
May 17, 1-2 PM (5)
May 17, 2-3 PM (9)
May 17, 3-4 PM (4)
May 17, 4-5 PM (8)
May 17, 5-6 PM (14)
May 17, 6-7 PM (10)
May 17, 7-8 PM (2)
May 17, 8-9 PM (4)
May 17, 9-10 PM (2)
May 17, 10-11 PM (20)
May 17, 11-12 AM (13)
May 18, 12-1 AM (10)
May 18, 1-2 AM (4)
May 18, 2-3 AM (5)
May 18, 3-4 AM (9)
May 18, 4-5 AM (14)
May 18, 5-6 AM (2)
May 18, 6-7 AM (37)
May 18, 7-8 AM (28)
May 18, 8-9 AM (35)
May 18, 9-10 AM (41)
May 18, 10-11 AM (43)
May 18, 11-12 PM (29)
May 18, 12-1 PM (136)
May 18, 1-2 PM (34)
May 18, 2-3 PM (89)
May 18, 3-4 PM (33)
May 18, 4-5 PM (45)
May 18, 5-6 PM (21)
May 18, 6-7 PM (16)
May 18, 7-8 PM (13)
May 18, 8-9 PM (23)
May 18, 9-10 PM (4)
May 18, 10-11 PM (25)
May 18, 11-12 AM (12)
May 19, 12-1 AM (7)
May 19, 1-2 AM (2)
May 19, 2-3 AM (9)
May 19, 3-4 AM (5)
May 19, 4-5 AM (10)
May 19, 5-6 AM (3)
May 19, 6-7 AM (53)
May 19, 7-8 AM (23)
May 19, 8-9 AM (45)
May 19, 9-10 AM (66)
May 19, 10-11 AM (30)
May 19, 11-12 PM (48)
May 19, 12-1 PM (79)
May 19, 1-2 PM (70)
May 19, 2-3 PM (41)
May 19, 3-4 PM (51)
May 19, 4-5 PM (15)
May 19, 5-6 PM (17)
May 19, 6-7 PM (18)
May 19, 7-8 PM (9)
May 19, 8-9 PM (21)
May 19, 9-10 PM (10)
May 19, 10-11 PM (28)
May 19, 11-12 AM (13)
May 20, 12-1 AM (21)
May 20, 1-2 AM (9)
May 20, 2-3 AM (4)
May 20, 3-4 AM (5)
May 20, 4-5 AM (9)
May 20, 5-6 AM (34)
May 20, 6-7 AM (27)
May 20, 7-8 AM (2)
3,943 commits this week May 13, 2026 - May 20, 2026
fix(chain): observe UpdateParametersTx on-chain + relax value preservation
Two production bugs in the dynamic-head-participants flow surfaced via two
new hydra-cluster end-to-end tests (Join + Leave):

1. Validator rejected the post-balancing head output. The wallet's
   coverFee_ calls 'ensureMinCoinTxOut' on every output, which inflates
   the head output's lovelace to cover the higher min-utxo cost caused
   by adding a new participation token. The 'mustPreserveHeadValueAdjustedForPT'
   check used '==' which was too strict; switched to 'geq' to mirror
   the existing 'mustPreserveHeadValue' rationale in Hydra.Contract.Util.

2. The chain observer never recognized UpdateParametersTx after it was
   posted, so neither node emitted 'JoinFinalized' / 'LeaveFinalized'
   (and the HTTP request blocked). Added 'observeUpdateParametersTx' +
   'UpdateParametersObservation' + 'HeadObservation.UpdateParameters'
   arm + 'convertObservation' mapping to 'OnUpdateParametersTx'.

Also registered 'FailedToConstructUpdateParametersTx' in the API JSON
schema and regenerated the head validator + minting policy script hashes
(they change because of the validator-source edit).

Tests:
  hydra-cluster/test/Test/EndToEndSpec.hs
    - can invite a new party to join an open two-party head
    - can let a party leave an open three-party head
fix(chain): post UpdateParametersTx to L1 + cover all dyn-head outputs
Fixes the manual demo path: after alice (or bob) multi-signed the
'AddParty' snapshot, both nodes crashed when actually constructing
the L1 'UpdateParametersTx', and the API server crashed when encoding
the join-related 'ServerOutput' events. The user's POST /participants
returned an empty reply because the API thread died before responding.

Root causes were two stale 'case' expressions whose 'StateChanged' /
'PostChainTx' / 'ServerOutput' constructor sets predated Phase 1+2:

 1. 'Hydra.Chain.Direct.Handlers.prepareTxToPost' had no arm for
    'UpdateParametersTx', so the chain handler thread crashed with
    "Non-exhaustive patterns in case" when the post-tx effect fired.
 2. 'Hydra.API.ServerOutput.prepareServerOutput' had no arm for any
    of 'LeaveRequested' / 'LeaveApproved' / 'LeaveFinalized' /
    'LeaveInvalid' / 'JoinRequested' / 'JoinApproved' / 'JoinFinalized'
    / 'JoinInvalid', so encoding any of them in the WS server crashed
    the API listener thread.

Both spots had been emitting '-Wincomplete-patterns' warnings — caught
now and the wider sweep of test scaffolding / visualizer that was also
incomplete:

 - 'Hydra.Chain.Direct.State.updateParameters' is the new wrapper that
   resolves the head output from spendable UTxO and the head minting
   policy from the seed input, then delegates to the off-chain builder
   'Hydra.Tx.UpdateParameters.updateParametersTx'. Mirrors 'decrement'.
 - 'PostTxError.FailedToConstructUpdateParametersTx' is the
   corresponding 'Hydra.Chain' error variant.
 - 'Hydra.Chain.Direct.Handlers' imports + dispatches the new wrapper.
 - 'Hydra.API.ServerOutput.prepareServerOutput' now handles all 8 new
   constructors (pass-throughs to 'encodedResponse').
 - 'visualize-logs/src/VisualizeLogs.hs' labels the new 'StateChanged'
   events.
 - Test scaffolding ('Test.Hydra.API.ClientInput', 'Test.Hydra.Chain',
   'Hydra.Chain.Direct.HandlersSpec') gets exhaustive 'shrink' / case
   coverage for the new variants.

With this fix the manual demo flow works end-to-end:
'nix run .#demo', drive alice+bob into 'HeadIsOpen', then in another
terminal: 'bash demo/invite-carol.sh'. Both nodes log 'JoinApproved'
+ post 'UpdateParametersTx', observe 'OnUpdateParametersTx', and
report 'JoinFinalized' with [alice, bob, carol].
feat(#1813): new-party state sync via SideLoadSnapshot
After the on-chain join finalizes, the joining party's local snapshot
is still the empty 'InitialSnapshot' while the existing parties have
already confirmed the 'AddParty' snapshot. The next 'ReqSn' would be
rejected as 'ReqSnNumberInvalid'. Closes the loop by:

  - Relaxing 'onOpenClientSideLoadSnapshot's multi-signature check:
    when the requested snapshot's 'parameterUpdate' is 'AddParty p',
    the signers were the pre-update parties, so we verify the
    multi-sig against 'parties \\ {p}'. This lets the joining party
    (or any node that lost state) side-load the original two-of-two
    signature even though their local 'parties' now has three
    members.
  - Fixing 'UpdateParameters' validator/builder mismatch around
    'utxoHash':
      - Tx builder: 'utxoHash = hashUTxO snapshot.utxo' (back to
        the same shape Increment / Decrement use).
      - Validator: 'checkSnapshotSignature' now signs over
        'nextUtxoHash' (matches what the off-chain
        'getSignableRepresentation' emits), and
        'mustOnlyChangeParties' no longer enforces
        'prevUtxoHash == nextUtxoHash'. UpdateParameters may
        advance the on-chain utxoHash the same way Increment/
        Decrement do — without this, a Leave/Add following an
        Increment failed with 'H57' (utxoHash mismatch) or 'H12'
        (signature mismatch).

E2E (real cardano-node):

  - 'canJoinHead' (carol joins the open head): fetches alice's
    confirmed snapshot, side-loads it on carol via the WebSocket
    'SideLoadSnapshot' input, deposits funds, submits a 'NewTx',
    then asserts the 'SnapshotConfirmed' event on all three nodes
    carries three multi-signatures and the new tx in 'confirmed'.
  - 'canLeaveHead' (carol leaves the open head): deposits funds
    first so the head has L2 UTxO, lets carol leave via 'DELETE
    /participants/me', then alice submits a 'NewTx' and we assert
    a 'SnapshotConfirmed' with /two/ multi-signatures on the
    remaining two-party head.

Other:

  - Updated 'demo/dynamic-head-participants.md' with a step-5 that
    shows carol side-loading alice's confirmed snapshot via
    websocat, removing the previous "out of scope" note.
  - Plutus golden hashes regenerated (vHead / mHead) because the
    validator changed.

Tests passing:
  cabal test hydra-tx          → 52/52
  cabal test hydra-plutus      →  4/4
  cabal test hydra-node        → 538/538
  hydra-cluster e2e (this PR)  →  2/2 ('dynamic head participants')
fix(headlogic): allow joining-party catch-up for snapshot side-load and connectivity
Two issues observed when running the dynamic-head-participants demo
manually (issue #1813):

1. Side-load rejection at the join step. The AddParty snapshot the
   inviter hands the joining party is signed at version N-1 (just
   before the UpdateParametersTx that admitted them), but the joining
   party's coordinatedHeadState.version is already N (after observing
   that L1 transaction). The existing 'requireVerifiedL1Snapshot' check
   rejected this as SideLoadSvNumberInvalid. Bypass the version /
   commit / decommit checks when the snapshot is an AddParty for us at
   currentVersion - 1; the multi-signature is still verified.

2. 'Network Disconnected' in the joining party's TUI. Connectivity
   events (NetworkConnected, PeerConnected, ...) describe the L2 mesh
   and are orthogonal to L2 state. 'updateCatchingUpHead' was waiting
   on every NetworkInput, so a late-joining party whose etcd fires
   NetworkConnected during chain replay never sees that event - it
   gets dropped after the TTL expires and 'networkInfoP' stays at
   the initial 'NetworkInfo False mempty'. Process ConnectivityEvents
   during catch-up; keep waiting only on ReceivedMessage.
feat(network): dynamic accepted-parties set via STM accessor
The 'withAuthentication' middleware no longer closes over a fixed
'[Party]' list; instead it takes an 'STM m [Party]' action that is
read once per inbound message. This lets the live party set shrink
(Phase 1) or grow (Phase 2) at runtime as 'ParametersChanged' events
arrive — see issue #1813.

- 'withAuthentication' (Hydra.Network.Authenticate) signature changes
  from '[Party]' to 'STM m [Party]'. The signature-verification path
  reads a fresh snapshot every call; the per-message cost is one
  uncontended STM read.
- 'withNetwork' (Hydra.Node.Network) threads the accessor through.
- 'Hydra.Node.Run' currently supplies @pure otherParties@ — a static
  accessor sourced from 'Environment.otherParties'. The TVar that
  observes 'ParametersChanged' and write-throughs is the next milestone.
- New 'AuthenticateSpec' test exercises the dynamic case: after the
  accepted set shrinks, messages from the removed party are dropped.
- All 5 existing 'AuthenticateSpec' fixtures updated to lift the static
  list into 'pure'.

Hydra-node suite: 516/516 passing.
feat(api): DELETE /participants/me handler
Adds the HTTP entry point for the dynamic-head-participants flow
(issue #1813, Phase 1):

- 'DELETE /participants/me' fires a 'Leave' 'ClientInput' and waits for the
  result. Returns 200 on 'LeaveFinalized', 400 on 'LeaveInvalid' (body is
  the 'LeaveInvalidReason' JSON) or 'CommandFailed', 503 on
  'RejectedInputBecauseUnsynced', 202 on timeout (with
  'LeaveSubmitted' tag).
- 'api.yaml' picks up the new route + bindings.
- 4 new HTTPServerSpec test cases cover the timeout, finalized, invalid,
  and unsynced paths.

Hydra-node suite: 520/520 passing.
ResolveLeiosBlock: narrow to chain-dep state
The only non-default instance (Dijkstra-era ShelleyBlock) consumes only
the Praos chain-dep state from its HeaderState argument, the tip is
never used. Taking the full HeaderState forced the HFC dispatch in
Cardano.Block to fabricate a HeaderState with 'headerStateTip = Origin'
purely to satisfy the record. The original leios-prototype branch worked
around the same smell with a partial toConwayHeaderState projection and
an 'error "Must be in Conway"' arm.

Narrowing the class to only taking the chain-dep state collapses the HFC
instance to a single combined case on (chainDepState, block), using the
existing ChainDepStateDijkstra pattern synonym. Also, the Dijkstra-era
Shelley instance drops the redundant 'headerStateChainDep' projection on
its argument.
Leios demo: ImmDBServer schedule-driven Leios + schedule generator
Adapt the ImmDBServer tool to drive a Leios demo from a pre-recorded
schedule: replay EBs / cert-blocks at slot timestamps and serve them
over the LeiosNotify / LeiosFetch mini-protocols introduced in the
diffusion chunk. The actual schedule generator lives at
'ouroboros-consensus/app/leios-schedule-gen.hs' (committed with the
diffusion chunk for cabal layout reasons).

- 'immdb-server' grows --leios-db / --leios-schedule / --initial-slot
  / --initial-time / --address.
- 'Cardano.Tools.ImmDBServer.Diffusion' adds the LeiosSchedule type
  and a scheduler thread that ticks through schedule entries.
- 'Cardano.Tools.ImmDBServer.MiniProtocols' grows LeiosNotify /
  LeiosFetch responders, send/recv tracers, and a slot-delay aware
  chainSyncServer.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
Leios diffusion: LeiosNotify + LeiosFetch mini-protocols + Reception
Wire LeiosNotify and LeiosFetch mini-protocols into the node-to-node
diffusion layer, plus the consensus-side adaptation to the network
fork's BearerBytes + arrival-time 'Reception' API.

Mini-protocols (live in consensus, not network)
- LeiosDemoOnlyTestFetch — request/response for EB bodies and the
  accompanying tx closures.
- LeiosDemoOnlyTestNotify — server pushes 'MsgLeiosBlockOffer' /
  'MsgLeiosBlockTxsOffer' as EBs arrive, plus 'MsgLeiosVotes' which
  this chunk handles as a no-op stub (the voting chunk replaces it).
- New 'NodeKernel' fields: 'getLeiosDB', 'getLeiosPeersVars',
  'getLeiosOutstanding', 'getLeiosReady'. The 'leiosFetchLogic'
  thread (0.5s loop) reads peers' offerings, runs the fetch-decision
  iteration and dispatches fetches.

Network wiring
- 'Ouroboros.Consensus.Network.NodeToNode' grows codecs + handlers
  for both protocols and threads the LeiosDbConnection through to the
  inbound side.
- 'Ouroboros.Consensus.Node' opens the in-memory LeiosDb and passes
  the handle + connection to the kernel / NTN / ChainDB layers.

BearerBytes + Reception migration
- All codec entry points drop the 'dataSize :: bytes -> Word'
  argument (now a 'BearerBytes' constraint).
- 'recv' returns 'Maybe (Reception a)' carrying per-chunk arrival
  times; the threadnet harness wraps reads via 'MkReception
  IntMap.empty', the PeerSimulator strips via 'fmap received'.
- 'TraceSendRecv' grows arrival-time on the receive side
  ('TraceRecvMsg :: Maybe Time -> AnyMessage ps -> ...').

Threadnet harness
- 'MinimalChainDbArgs.mcdbLeiosDbVar' backs the in-memory LeiosDb
  with a TVar so the test infrastructure can inspect it.

cabal
- Exposes 'LeiosDemoOnlyTestFetch' / 'LeiosDemoOnlyTestNotify' and
  adds network-mux, pretty-simple, typed-protocols:cborg deps.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
Leios resolving + chain inclusion
Plumb a 'ResolveLeiosBlock' typeclass through the block-application
pipeline so a Dijkstra-era block carrying 'SJust LeiosCert' has its
(empty) tx list spliced in from the EB closure in the LeiosDb before
validation. Plumbing only — no chain-selection rule change.

- 'Storage.LedgerDB.Forker' defines 'ResolveLeiosBlock' as a typeclass
  with a no-op default, and the 'applyBlock' hook calls
  'resolveLeiosBlock leiosDb hdrSt b' before
  'tickThen{Apply,Reapply}'. The forker reads the 'ExtLedgerState' to
  extract 'headerState' for the splice (used to look up the previous
  EB announcement on the chain-dep state).
- 'Storage.LedgerDB.V2' threads 'LeiosDbConnection' alongside the
  existing args so the new 'applyBlock' signature reaches the V2
  pipeline.
- Explicit no-op instances for every block type that doesn't carry
  Leios: 'ByronBlock', 'DualBlock', 'ShelleyBlock proto era',
  'HardForkBlock xs' (OVERLAPPABLE), 'TestBlockWith ptype', the
  storage-test 'TestBlock', the mock 'SimpleBlock''.
- Real-work instance: 'ShelleyBlock (Praos c) DijkstraEra' splices the
  EB closure from the previously-announced point (looked up via
  'praosStateLeiosAnnouncement' + 'praosStateLastSlot' on the Praos
  chain-dep state).
- 'Ouroboros.Consensus.Cardano.Block' routes per-era dispatch to the
  appropriate instance.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
Leios voting: LeiosVoteState + leiosVoting thread + threadnet test
Add the voting capability on top of the diffusion layer: an EB
becomes eligible for certification once enough peers have voted
'yes' on it (via the 'MsgLeiosVotes' message on the LeiosNotify
protocol from the previous chunk).

Voting state
- 'LeiosVoteState' (new module) — aggregated vote state across all
  peers, with 'addVote' / 'subscribeVotes' write/read API.
- 'NodeKernel.getLeiosVoteState' field plus the
  'NodeKernelArgs.leiosVotingKey' / 'NodeKernel.leiosVoting' background
  thread that subscribes to local 'AcquiredEbTxs' notifications and
  emits one vote per acquired EB.

LeiosNotify wiring
- The 'MsgLeiosVotes' server case now calls 'addVote' on each
  incoming vote (replacing the chunk-2 no-op stub).
- The notify server multiplexes EB offers with a 'processVote' loop
  that drains 'subscribeVotes leiosVoteState' and publishes
  'MsgLeiosVotes' to peers.

Tests
- 'Test.LeiosVoteState' (foundation-level unit tests).
- 'Test.ThreadNet.Leios' — end-to-end threadnet driver that asserts
  cert blocks happen, no vacuous runs, gap > 'minCertificationGap',
  all nodes converge, and the post-resolve replay matches the live
  ledger.

cabal
- Exposes 'LeiosVoteState'; wires 'Test.LeiosVoteState' /
  'Test.ThreadNet.Leios' into the test suites.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
Leios mempool access + forging
Foundations for the Leios prototype's production side, plus the
Dijkstra-era forging path that produces EBs / cert blocks.

Foundations
- LeiosDemo{Types,Logic,Exception} — placeholder types (EbAnnouncement,
  LeiosPoint, TxHash, LeiosVote), EB-forging logic, exception type.
- LeiosDemoDb / LeiosDemoDb.{Common,InMemory,SQLite} — pluggable
  storage for forged EBs + cert lookup.
- ouroboros-consensus.cabal exposes the foundation modules; the
  consensus-test suite gains Test.LeiosDemoDb and Test.LeiosDemoTypes;
  new leios-db-bench benchmark.
- cabal.project pins cardano-ledger at leios-prototype-remake-on-chap
  for 'Maybe LeiosCert' on the Dijkstra block body.

Mempool access (cumulative tx bytes)
- 'shelleyCumulativeTxBytes :: Word64' on ShelleyLedgerState +
  TickedShelleyLedgerState, threaded through CBOR + the tick step.
- HFC mempool threads it per-era so the kernel can read the running
  total of bytes that have entered the ledger.

Forging — Praos header EB announcement
- 'hbLeiosEbAnnouncement :: !(StrictMaybe EbAnnouncement)' on the
  Praos block header (chunked CBOR-after the existing field set).
- 'PraosState.praosStateLeiosAnnouncement' / 'praosStateLastSlot'
  on the Praos chain-dep state, written non-stickily on each tick.
- 'ProtocolHeaderSupportsKES.protocolStateLeiosInfo' projects the
  pair out; TPraos returns Nothing. No certify flag — the
  certificate, when forged, lives on the block body (see ledger PR).

Forging — Dijkstra-era 'decideLeios'
- 'ForgeBlockArgs' extended with fbEbTxs / fbLeiosDb /
  fbCurrentTickedLedgerState / fbChainDepState / fbLeiosTracer so
  the forger has read access to the EB-candidate tx pool, the
  LeiosDb (to look up certificates and store newly-forged EBs),
  the ticked ledger state and the chain-dep state.
- Dijkstra-only forging path: 'decideLeios' returns either a
  Leios certificate (when a previously-announced EB has gap >
  'minCertificationGap' and is locally available with a cert)
  or a new EB announcement (forged from fbEbTxs).
- Cert-block forging emits an empty tx sequence on the wire; the
  full transaction list is re-attached at apply time by
  resolveLeiosBlock (see resolving chunk).
- ForgeBlockArgs adaptation propagates through Byron, ByronDual,
  Mock and Shelley forging.

Plumbing
- ChainDB.Impl{,.Args} + LedgerDB{,.Args} grow a LeiosDbConnection
  so downstream chunks (diffusion, resolving) can reach it via
  ChainDbArgs / LedgerDbArgs.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
docs(website): refine bridges page integration guidance and finality framing
Apply lead-dev review feedback: promote Rust client library and CLI as the
primary integration path (with WASM scoped to browser-facing components),
rework the finality framing so it's clear the bridge sets its own
confirmation threshold under both current and upcoming Mithril certification,
tighten the "compromised provider" clause to name the concrete consequence,
and clarify the user-facing "aggregator" term with a 1inch example.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>