Skip to content

fix(relay): make a cold relayed capsule fetch reachable in one command - #334

Draft
MichaelTaylor3d wants to merge 18 commits into
mainfrom
loop/3128-relay-batch
Draft

fix(relay): make a cold relayed capsule fetch reachable in one command#334
MichaelTaylor3d wants to merge 18 commits into
mainfrom
loop/3128-relay-batch

Conversation

@MichaelTaylor3d

@MichaelTaylor3d MichaelTaylor3d commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

DO NOT MERGE — gate round not yet run.

dig-node's whole remaining share of dig_ecosystem#3128, as one batch (CLAUDE.md §1.4 batch-per-repo).

Closes #322
Closes #332
Closes #333


The shape decision for #333, and its argument

A blocking ask must be capped tightly; a non-blocking one need not be. The 65 s descriptor ladder
is not too short — it is the right bound for the operation it was built for, and the operation was
wrong.

The hop held the requestor's stream open for the whole duration of a third-party 135 MB transfer, so
the cost of waiting was a held stream on both ends. That is exactly the cost a wall-clock cap exists to
bound, which is why the cap is correct and why the capability was unreachable behind it.

Neither obvious fix works:

  • Widening the ladder lets an unanswerable holder hold a slot for minutes — the amplification the
    two-phase proxy and proxy_rate_limiter exist to bound.
  • Deriving a deadline from capsule size is circular: the size arrives in the descriptor, which is
    the thing being waited for.

So the fix decouples the two waits rather than lengthening one:

  1. Hop side. relay_capsule returns RelayStatus { Landed | Pending { staged_bytes } | Refused }.
    The pull is spawned in the background and the ask waits only a 3 s grace — comfortably inside the
    requestor's first 5 s rung, so a capsule that lands quickly is still answered in one round trip and
    the case that already worked is unchanged. One that does not is answered ContentMissInconclusive
    (-32017) with error.data.relay_staged_bytes, and the pull keeps running.
  2. Requestor side. On that answer the requestor stops using a wall clock and waits on PROGRESS:
    re-ask every 10 s, continue while the staged count strictly advances, abandon after a 60 s stall
    window with no advance, and abandon at a 30 min ceiling regardless.

Once the ask is non-blocking, the instrument changes. Each further poll costs one small round trip
instead of a held stream, so "how long may I wait" stops being a resource question and becomes a
liveness question. Forward progress needs no size; a wall clock needs a size the requestor cannot have.

Why the ceiling is not optional (NC-12). relay_staged_bytes is a HOP'S CLAIM about itself. A
stall window cannot catch a hostile hop that fabricates a counter rising forever, so the ceiling is
what makes the worst case finite: a lying hop can waste one pull's time from one peer, and no more.
The claim decides only how long to wait — never whether a byte is accepted. The capsule is still
verified against the chain-anchored root exactly as a direct holder's would be, so a hop that
fabricates its way through a wait still cannot produce content that passes.

Backwards compatible by construction. The code is the taxonomy's existing inconclusive-miss code,
not a new number (SYSTEM.md: the taxonomy is owned by dig-rpc-protocol and adopted, never
restated — this repo has paid for restating it twice). A requestor that ignores relay_staged_bytes
sees an ordinary inconclusive miss and retries later, which is precisely what #333's field observers
were doing by hand.

The diff, per ticket

#333module_relay.rs (RelayStatus, the 3 s grace, background spawn), module_reshare.rs
(spawn_relayed_capsule_warm, holds, staged_bytes, await_landing), lib.rs
(relay_pending_err, RELAY_PROGRESS_FIELD, both serve sites), peer.rs +
module_serve.rs (module_relay_pending_frame for the streaming site), module_transport.rs
(DescriptorAnswer, wait_for_relayed_descriptor, the three bounds).

#322module_transport.rs: LadderEnd now distinguishes Refused (the peer ANSWERED, and the
answer was no) from Exhausted (it never answered). A refusal escalates within the same invocation;
an exhausted ladder does not, because a peer that could not answer a plain ask will not answer a relay
ask and escalating would double the invocation's bound for nothing.

#332module_reshare.rs: StagedPull holds the staged bytes and the resume checkpoint as ONE
value with one erase, so no failure path can remove half a partial. Discard erases both;
Preserve touches neither.

Decisions the coordinator asked me to state

StagingDisposition is KEPT, not removed. With dig-download 0.19.2's demotion wipe gone, the
disposition is not belt-and-braces — it is now the ONLY thing that erases a partial attributable to a
proven-false descriptor. The lower layer deliberately PRESERVES on demotion; something still has to
decide that a verification failure is different, and that decision has exactly one home.

dig-download is bumped to 0.19.2 in the lock (lock-only; the "0.19" caret already admits it).
Verified from the index: vers=0.19.2, yanked=false, checksum 02a51e9858830dcc…, and the
non-dev dependency set is byte-identical to 0.19.1, so the single [[package]] block swap is the whole
correct change. cargo metadata --locked passes.

No other dig-*/chia-* bump is takeable here (§2.4b), and this is measured, not assumed. Every
latest release reaches chia 0.36 — dig-download 0.20.3, dig-nat 0.20.0 and dig-dht 0.12.1 all
require dig-tls ^0.4 (chia-bls ^0.36.1), and dig-peer 0.12.0 requires chia-protocol ^0.36 — while
this crate is on chia-protocol 0.26 / chia-bls 0.26 / chia-wallet-sdk 0.30. Taking any one of them
would put two chia lines inside one crate, which is the internally-split state §2.4b exists to prevent
and which shipped twice on 2026-08-22. The uplift is the chia-0.36 cascade (dig_ecosystem#3152), not
this PR.

Two false manifest comments corrected (dig-node-core/Cargo.toml, dig-wallet/Cargo.toml). They
said the chia-peer unpin awaited a release built against dig-chainsource-interface 0.2. Measured on
the index: no such release was ever neededchia-peer 0.2.0 skipped dci 0.2 and went straight to
dci ^0.3 + chia-protocol ^0.36.1, where chia-query 0.15.0 also sits, so the two
ChainSourceProvider traits already unify again. They unify at CHIA 0.36, so the real blocker is the
cascade above. A stale blocker is worse than none, because it stops people looking.

Blast radius (§2.0)

gitnexus indexed per-worktree (dn-3128, 114 MB) and queried; paired with ripgrep, because gitnexus
under-counted here — it found 1 caller of relay_capsule where grep found 3.

symbol risk callers
relay_capsule LOW 3 — peer.rs::stream_module_range, lib.rs::get_module_info, lib.rs::fetch_module_range_frame. All three updated.
discard_stagingStagedPull::erase LOW 2 direct (StagingDisposition::apply, warm_with_config)
descriptor_over LOW 1 (get_module_info)
ask_within_deadlines MEDIUM 7 direct, 6 of them its own tests

No public API of dig-node-core changes shape; every edited symbol is pub(crate) or private, and the
wire change is one additive error.data field.

Verification

Revert-proofs, each run alone, naming the assertion that fired — see the PR comments for the table.

@MichaelTaylor3d

Copy link
Copy Markdown
Contributor Author

Progress — resume-ready

Branch loop/3128-relay-batch · HEAD 2a27fd3 (pushed) · DRAFT, do not merge — gate round not yet run.

Done

The finding that mattered, and what it changed

The first revert-proof pass ran a revert called no-wait — removing the relay wait from
get_module_info — and nothing failed. The four #333 tests were driving
wait_for_relayed_descriptor directly, so they proved the wait was correct while saying nothing
about whether the production path still reached it. That is the same class of defect as #328
shipping broken twice: the right property, asserted against a fixture that cannot exercise the real
path.

The fix was structural rather than a bigger test. The three steps — ask, escalate, wait — are one
decision about a holder that did not simply hand over the descriptor, so they now live in ONE
function, descriptor_via_rounds, which get_module_info calls and which every test drives. A
helper extracted purely for testability is a helper a call site can quietly stop using.

In progress

Re-running the five revert-proofs against the folded shape, individually rather than as a batch —
the no-ceiling revert makes the wait unbounded, so it hangs rather than failing, and a batch run
cannot attribute that to a single test.

Next action

  1. cargo test -p dig-node-core --lib seams::dig_peer::module_transport — confirm 21 green.
  2. Five reverts via scratchpad/revert.py, one at a time, each naming the assertion that fired.
  3. Version bump, CHANGELOG, SPEC/docs sweep, §2.4b dep pass, then the gate.

@MichaelTaylor3d

Copy link
Copy Markdown
Contributor Author

Revert-proof evidence

Each revert applied ALONE to a committed tree, run alone, restored afterward. Committing first is not
optional here — git checkout <path> is destructive, and a background proof script's trailing
checkout clobbered an uncommitted edit of mine twice during this work.

revert tests that failed assertion that fired
no-wait — a RelayPending answer returns NoAnswer instead of waiting 4 — all of the #333 group a hop that is moving bytes must be waited for: Err(NoAnswer), and three left: NoAnswer
no-ceiling — drop elapsed() >= RELAY_MAX_WAIT 1a_hop_that_fabricates_endless_progress_is_bounded_by_the_ceiling the wait MUST end at the ceiling; an unbounded wait is the NC-12 defect itself: Elapsed(())
no-stall — drop the stall branch 1a_frozen_relay_is_abandoned_after_the_stall_window the Stalled equality at :1145
no-escalation — one round per invocation 1a_cold_first_invocation_spends_a_plain_round_then_escalates the phase sequence, [false] vs [false, true]
escalate-always — escalate whenever no descriptor came back 2an_unresponsive_holder_is_not_escalated AND the_production_get_module_info_climbs_the_whole_ladder the ladder-total equality: 130 s vs 65 s, through the real method, in 60 s of wall clock
#332 Discard no longer clears the checkpoint 1a_verification_failure_still_discards_what_it_staged a discarded partial must leave no resume checkpoint behind
#332 Preserve also clears (the over-broad direction) 1a_severed_warm_resumes_from_its_partial_instead_of_refetching_the_capsule the resumed byte count at :1567

The one that mattered: no-wait fired NOTHING on the first pass

The four #333 tests were driving wait_for_relayed_descriptor directly. They proved the wait was
correct and said nothing about whether get_module_info still reached it — the right property against
a fixture that cannot exercise the real path, which is the shape that let #328 ship broken twice.

Why the fixture missed: the wait had been extracted purely for testability, so the tests and the
call site were two independent things and only one of them was pinned.

What changed: the three steps — ask, escalate, wait — are ONE decision about a holder that did not
simply hand over the descriptor, so they now live in one function, descriptor_via_rounds, which
get_module_info calls and which every test drives. Re-run: no-wait fires on all four, naming
Err(NoAnswer) where a descriptor was expected.

The general lesson, since it caught two separate things here: a helper extracted for testability is
a helper the call site can quietly stop using.
Prefer folding the decision into one function over
adding a test that asserts the seam is still wired.

Two other things the proofs caught, which are worth recording

  1. A killed proof batch silently un-shipped a security bound. The first no-ceiling run hung (an
    unbounded wait spins virtual time forever); the batch was killed at its timeout before its restoring
    git checkout ran, and the emergency "commit everything now" after a cap swept the revert into
    2a27fd3. The ceiling was missing from main-bound code for four commits. Restored in 4da7ecc,
    and found only because the proof discipline sent me back to look at the pattern.
  2. A test that HANGS on regression is a landmine, not a proof. The ceiling revert burned 659
    CPU-seconds and reported nothing attributable. The test is now bounded by
    tokio::time::timeout(RELAY_MAX_WAIT * 2, …), so the same regression now fails in 0.01 s with a
    sentence naming the defect.

@MichaelTaylor3d

Copy link
Copy Markdown
Contributor Author

Implementation complete — handing back to the review gate

Branch loop/3128-relay-batch · HEAD 5305989 (rebased onto 8f11afa, pushed) ·
Still DRAFT. The §2.4a gate round has not been dispatched; this lane does not run it.

Local evidence

gate result
cargo fmt --all -- --check 0
cargo clippy --workspace --all-targets -- -D warnings 0
cargo test --workspace 0 — 968 in dig-node-core lib, ~2,300 across the workspace, 0 failed
revert-proofs 7, each run alone, each naming the assertion that fired (comment above)
dig-download 0.19.2 in the lock cargo metadata --locked passes

CI: every required check green except Test + coverage, still running at the time of writing.
check-merge-preconditions.sh reports BLOCKED [EXIT 1] on that one check alone —
unresolvedReviewThreads=0, and the other four required contexts are present and SUCCESS by name.

The acceptance bar this PR does NOT clear, stated plainly

§2.6's bar is a person watching a .dig arrive over a hop, on real hardware. This lane cannot
produce that evidence
#322, #332 and #333 were all measured on a three-machine A → B → C run, and
I have unit-level proof only.

What that means concretely: the timing constants — a 3 s hop grace, a 10 s poll, a 60 s stall window,
a 30 min ceiling — are argued from the field measurements on the tickets, not confirmed by a repeat of
that run. The hardware acceptance re-run is the remaining work on #333, and it is worth doing
against this branch rather than against v0.145.0, whose lock still resolves dig-download 0.19.1.

One correction to the brief I was given

The two manifest comments were stale, but not in the way reported. The claim was that the chia-peer
unpin awaited a dig-chainsource-interface 0.2 release that had since shipped. Measured on the
index: no such release was ever needed. chia-peer 0.2.0 skipped dci 0.2 entirely and went to
dci ^0.3 + chia-protocol ^0.36.1, where chia-query 0.15.0 also sits — so the two
ChainSourceProvider traits already unify, at chia 0.36. The real blocker is therefore the
chia-0.36 cascade (dig_ecosystem#3152), and unpinning on the strength of the old comment would have
pulled this crate onto a second chia line. Both comments now say that.

@MichaelTaylor3d MichaelTaylor3d left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

CHANGES-REQUIRED — correctness gate

Head reviewed: 6e2b6730a772f6fc92b39cd16c8273a666baed0b (resolved from the remote, not from the dispatch).

Recorded as a --comment review: a verdict event from the PR author's identity is a 422 for both APPROVE and REQUEST_CHANGES. The four open inline threads are what bar the merge, via required_conversation_resolution.

What I verified myself, by mutation, in my own worktree

Baseline at head: 968 passed, 0 failed.

Probe Result
no-wait (delete the RelayPending arm) CONFIRMED — all four #333 tests fail. The compiler also reported wait_for_relayed_descriptor as dead code, proving the mutation reached production.
#332 Discard (drop state_store.clear) CONFIRMEDa_verification_failure_still_discards_what_it_staged fails.
#332 Preserve (make Preserve erase) CONFIRMEDa_severed_warm_resumes_from_its_partial_instead_of_refetching_the_capsule fails.
ceiling removed CONFIRMEDa_hop_that_fabricates_endless_progress_is_bounded_by_the_ceiling fails in 0.01 s with Elapsed(()). The tokio::time::timeout bound works; it fails fast, it does not hang.
get_module_info bypasses descriptor_via_rounds DEFECT — 968/968 still pass. See thread 1.

The revert-proofs the PR claims are real. The gap is one level up: they protect the helpers, not the production call path.

The ceiling incident — verified, with one correction

RELAY_MAX_WAIT is present and correct at head (const module_transport.rs:564, the check at :624 before any further sleep, three tests). I traced the history independently: the check was removed in 27c97cd and restored in 203661c. Nothing else from that batch is missing — the only other deletions in 27c97cd are the intended descriptor_with_escalationdescriptor_via_rounds refactor, and all three RelayWaitEnd endings survive in both code and tests.

One correction to the write-up: 2a27fd3 is not an ancestor of this branch. The branch was rewritten; its on-branch twin is 27c97cd. The narrative is otherwise accurate, but a reader chasing 2a27fd3 will not find it in the history.

Verdict on the four constants

  • 3 s grace — sound. Comfortably inside the 5 s first rung, so the small-capsule case stays byte-for-byte one round trip.
  • 10 s poll — sound, with a doc nit: it is described as "one small round trip", but each poll is round(true) = a re-dial plus the full 65 s ladder. Cheap for a hop that answers Pending promptly; a hop that goes silent costs a 65 s ladder before being classed Abandoned. Non-gating.
  • 60 s stall — sound. Correctly sized above the poll interval, and the monotonic-max best makes downward lying useless (it self-stalls at 60 s).
  • 30 min ceiling — right instrument, wrong scope. See thread 2: it bounds one peer, while dig-download's documented worst case is 3 × holders × the transport's per-ask timeout, and this PR raises that term from 65 s to 30 min.

Also confirmed

  • NC-12 core claim holds. staged_bytes is read only for best / last_advance and one tracing::debug!. It never gates acceptance, and the capsule still clears the chain anchor + merkle verification in warm_claiming.
  • §2.4b spot-checked and the lane's claim is correct. dig-nat 0.20.0dig-tls ^0.4; dig-peer 0.12.0chia-protocol ^0.36 + dig-tls ^0.4; dig-dht 0.12.1dig-nat ^0.20, so it is blocked transitively too. dig-peer-selector 0.9.1 is already inside the declared caret. Nothing else is takeable without the chia-0.36 cascade (#3152). The two corrected Cargo.toml blocker comments are an improvement and match the index.
  • capsule_warm_locator_tests — the set-based assertion keeps both halves: an empty set still catches a pull that asked nobody, and a second id still catches reaching past the pool. Correct call, correctly reasoned.
  • #332 / dig-download 0.19.2 composition — the StagingDisposition justification holds. for_failure is the only producer of Discard, erase() is the only path that clears the checkpoint, and I found no second eraser.

Not blocking

.github/** is untouched, so there is no workflow collision with #336 — but Cargo.toml, Cargo.lock and SPEC.md all overlap. Thread 5, self-resolved.

Four threads open. I will re-verify and resolve each against the new head.

Comment thread crates/dig-node-core/src/seams/dig_peer/module_transport.rs Outdated
Comment thread crates/dig-node-core/src/seams/dig_peer/module_transport.rs Outdated
Comment thread crates/dig-node-core/src/seams/dig_peer/module_relay.rs
Comment thread crates/dig-node-core/src/seams/dig_peer/module_reshare.rs
Comment thread Cargo.toml
MichaelTaylor3d and others added 15 commits August 23, 2026 21:53
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…ifest blockers

The ceiling check was removed by a revert-proof batch that was killed before it
could restore the file, and swept into 2a27fd3. It is the NC-12 bound: a hop's
staged byte count is its own claim, so a stall window alone cannot catch a hop
that fabricates rising progress forever.

The two Cargo.toml comments named a blocker that never existed: chia-peer 0.2.0
skipped dig-chainsource-interface 0.2 and went straight to dci ^0.3 with
chia-protocol ^0.36.1, where chia-query 0.15.0 also sits. The real blocker is
the chia-0.36 cascade (dig_ecosystem#3152).

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…ion proof

dig-download 0.19.2 adds an across-round descriptor re-ask (dig-download#37), so a
never-answering holder is now asked MAX_DESCRIPTOR_ATTEMPTS times. The test pinned
the exact ask sequence, which was never its subject -- it proves the warm locator
unions the connected pool. Both halves of that property survive the change to a
set: an empty set still fails, and a second peer still fails.

Co-Authored-By: Claude <noreply@anthropic.com>
…NTS.md

gitnexus analyze rewrites its index name into both files, which are TRACKED, so a
per-worktree index silently lands in the diff.

Co-Authored-By: Claude <noreply@anthropic.com>
…ay waiting per PULL

The ladder, the second-pass escalation and the progress-bounded wait now live in a
private child module. The parent supplies only a policy-free one-shot ask, so a
call site cannot reach the middle of the decision: the gate's bypass -- calling the
inner round directly, which deleted both features with 968 tests green -- no longer
compiles.

RELAY_MAX_WAIT bounds one PEER. The pull asks many, so ten hops each fabricating a
byte of progress per poll stayed inside their ceilings while holding one pull for
hours. RelayWaitBudget charges relay waiting against the CAPSULE, so one honest hop
may still spend the whole budget and liars share it.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…its name

Co-Authored-By: Claude <noreply@anthropic.com>
MichaelTaylor3d and others added 3 commits August 23, 2026 21:56
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
@MichaelTaylor3d

Copy link
Copy Markdown
Contributor Author

All four gate findings addressed — re-gate requested

Rebased onto 5fdcf909 (#336). Re-asserted BY VALUE after the rebase, not by "the file changed":
root 0.146.0, dig-node-core 0.55.0, lock dig-download 0.19.2, and both corrected manifest
comments still naming #3152 (4 hits + 1 hit).


Finding 1 — you were right, and my fix had closed the wrong joint

Unifying the three steps made them inseparable from each other while leaving the inner ladder
reachable from the parent. So the call site could still take the first step and skip the rest, which
is what you demonstrated with 968 tests green.

The fix is structural, not another assertion. The ladder, the escalation and the relay wait now
live in a private child module descriptor_ask. ask_within_deadlines is private to it. The parent
supplies only ask_descriptor_once — a dial, a question, a hang-up, carrying no policy at all.
There is no longer anything a call site could use to get a descriptor almost the right way.

Proved the way you broke it. I reproduced your bypass exactly — replaced get_module_info's body
with a direct one-shot ask — and it now fails two production-path tests:

test assertion that fired
the_production_get_module_info_climbs_the_whole_ladder `get_module_info` must be BOUNDED by the ladder; an unbounded ask is the defect: Elapsed(())
a_cold_first_invocation_escalates_through_the_production_transport (new) a refused plain round must be followed by an ESCALATED round inside the same call — 1 round observed, 2 required

The second is new and is the one that pins the call site rather than the helper: a CountingLocator
counts locator consultations, and each descriptor round consults it exactly once, so the round count is
observable through the real method with no peer, no socket and no injected seam.

One thing worth flagging: on the first attempt your bypass made the ladder test hang rather than
fail — with no ladder there is no bound at all, so it waited on the pending locator forever. It is now
wrapped in tokio::time::timeout(ladder * 2, …), so the same regression fails in milliseconds with a
sentence naming the defect. A test that hangs on regression is a landmine, not a proof; that is the
second one on this PR (the ceiling test had the same shape).

Finding 2 — per-pull budget added; the per-hop ceiling untouched

RelayWaitBudget charges relay waiting against the capsule, keyed (store_id, root) — which is
exactly the unit one ModuleDownloader::download call is about, so the key is the pull.
descriptor_for_holder now receives what is left rather than a fresh allowance, and reports its
relay_waited so only actual relay time is charged. One honest hop may still spend the entire budget,
so the large-capsule case you correctly warned about is preserved; ten liars share it.

Three tests, all revert-proved. The load-bearing one is a_hop_gets_only_what_the_pull_has_left,
which catches the shape a half-applied budget takes — a parameter accepted, threaded, then ignored in
favour of the constant. Reverting exactly that line yields:

the wait must end at the budget it was GIVEN, not at the per-hop maximum
  -- elapsed 1800s against a budget of 30s

Also written into SPEC.md §21.1, since a reimplementation reading only the ceiling clause would
rebuild the multiplication.

Finding 3 — WarmRegistry starvation, documented and tested

Recorded at both ends — the spawn site in module_relay.rs and CapsuleWarmer::warm_relayed — that
the cap is global and shared with local warms, and that since the pull became spawned it survives
the requestor abandoning it. New test
a_relayed_warm_and_a_local_warm_compete_for_the_same_slots fills every slot with relay-shaped claims
and asserts the local warm is refused, with a truthful control that it succeeds once a slot frees.
That direction is the one that matters; the reverse would be satisfied by any shared cap.

Finding 4 — orphaned doc

The block is back on warm_relayed, and holds has its own.


Also in this push

SPEC.md §11.1b — "verified complete" now names the asset's upload state, not name presence,
with the reason stated: the row exists at upload start, so all names can be present while bytes are
still in flight, and a name-counting reimplementation rebuilds the race #336 just fixed.

On 2a27fd3 — you are right, it is not an ancestor; the rebase rewrote it and I quoted the
pre-rebase hash. The substance stands: the ceiling was absent from four commits and is present and
correct at head.


Local evidence on the rebased tree (5fdcf909 base)

gate result
cargo fmt --all -- --check 0
cargo clippy --workspace --all-targets -- -D warnings 0
cargo test --workspace 0 — 973 in dig-node-core lib, 0 failed
revert-proofs 9 total, each run alone, each naming the assertion that fired

Still DRAFT. Re-gate requested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant