fix(rpc): reconcile the two machine names dig-node emits at -32000 - #518
Conversation
RED: the shell publishes DISPATCH_FAILED and the embedded read path publishes SERVER_ERROR at the same number, on the same port, so a client branching on data.code cannot see which layer answered. The assertion compares the two EMITTED frames against each other rather than either against the catalogue -- a catalogue that agrees with itself proves nothing about the wire -- and pins which name won so the equality cannot later be satisfied by a third string nobody publishes. `dig_node_core::rpc_err` becomes public so the frame this crate actually emits is reachable from an integration test, rather than a restatement of it. Refs #496 Co-Authored-By: Claude <noreply@anthropic.com>
-32000 has TWO producers on one port: the shell (2 sites) and the embedded read
path (11 sites, all through errors::error_object, which resolves the name from
dig-rpc-protocol by NUMBER). They published DISPATCH_FAILED and SERVER_ERROR
respectively, so a client branching on data.code saw two names for one condition
and could not tell which layer answered -- which is what data.origin is for.
SERVER_ERROR is authoritative: it is what 11 of the 13 producers already emit,
and no consumer anywhere in the ecosystem branches on DISPATCH_FAILED. The Rust
variant keeps its condition-describing spelling; that is internal identity.
The drift guard's DispatchFailed exclusion is DELETED rather than replaced by a
list -- the loop already sweeps ErrorCode::all(), so removing the carve-out is
the whole fix -- and its floor rises 7 -> 8.
Three -32000 producers emitted a different ENVELOPE, not merely a different name
(SPEC §5.1's MUST, whose only exception is -32001): peer.rs's admission refusal
carried only {reason}, dispatch.rs's upstream failure carried no data at all,
and dig-runtime's wallet-only guard was a raw string literal invisible to every
type-scanning guard. All three now mint through the canonical builder.
content_serve::SERVE_UNREADABLE is deleted: its code field's only sink answers
502 from the message and never reads the number, so it was a dead const rather
than the taxonomy gap lib.rs's comment claimed.
Closes #496
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
|
IN PROGRESS — not the verdict. Independent correctness gate, fresh context, reading head Confirmed so far:
Still verifying: the |
…ract # Conflicts: # Cargo.lock # Cargo.toml
…ract # Conflicts: # Cargo.lock # Cargo.toml
….41) Co-Authored-By: Claude <noreply@anthropic.com>
….81) Co-Authored-By: Claude <noreply@anthropic.com>
….81) Co-Authored-By: Claude <noreply@anthropic.com>
loop-reviewer verdict @ 386999aCHANGES-REQUIRED -- one gating coherence finding. Everything else in the PR body checks out on this head. Head read: GATING -- G1. The SERVED OpenRPC description for
|
| Sentence | Cited at | Verdict |
|---|---|---|
"-32000 is SERVER_ERROR on both sides" |
SPEC.md:972 |
TRUE. meta.rs:843 sources dig_rpc_protocol::ErrorCode::ServerError.machine_code(); the crate returns "SERVER_ERROR" (dig-rpc-protocol-0.10.2/src/error.rs:267). Core resolves the same value by number via errors::taxonomy_code (errors.rs:36-42). |
"Which layer answered is carried by data.origin" |
SPEC.md:973-974, README.md:359, SPEC.md:3278 |
TRUE, and independently checked -- this is the load-bearing claim and no test asserts it. Shell: meta.rs:870-881 puts DispatchFailed in the "shell" arm. Core: RpcError::of uses ErrorCode::default_origin(), whose catch-all _ => ErrorOrigin::Node (error.rs:341-355) yields "node" for ServerError. The two producers genuinely differ. |
| "a guard asserts that equality across the whole shared set, with no exclusions" | SPEC.md:99 |
TRUE. openrpc_drift_guard.rs:300 iterates ErrorCode::all() with the DispatchFailed continue deleted; all() (meta.rs:974-999) lists all 21 variants incl. DispatchFailed. |
"A panicked/failed dispatch task yields SERVER_ERROR (-32000)" |
SPEC.md:994 |
TRUE. server.rs:1340-1343 mints via rpc_error(.., ErrorCode::DispatchFailed, ..); rpc.rs:67-77 writes data.code = code.name(). |
"dig-node-control-interface declares the same number as DISPATCH_FAILED ... which dig-node does not mint" |
SPEC.md:975-976 |
TRUE. This is the sole surviving DISPATCH_FAILED string in the repo (see section 5). |
"Carries NO numeric code: this outcome's only sink ... answers 502 BAD_GATEWAY from the message alone" |
content_serve.rs:138-141 |
TRUE (see section 4). |
"A literal frame is invisible to every guard that scans TYPES ... now carries the same data.code + data.origin" |
dig-runtime/src/lib.rs:104-113 |
TRUE, derived through dig_node_core::rpc_err. |
| "PUBLIC so a consumer that also mints frames at the same numbers ... can be tested against the frame this crate actually emits" | dig-node-core/src/lib.rs:4576-4579 |
TRUE (see section 2). |
| "The node failed to dispatch the request." | meta.rs:917 |
BORN-STALE -- G1 above. |
No other born-false claim found.
2. Declared vs served
peer.rs:1273-1275-- mints viaerrors::error_object(-32000, "request refused"), then addsdata.reasonrather than replacingdata. Same shape asrelay_pending_object(errors.rs:74-81). Reachable:admission_refusedis the peer-surface admission path.dispatch.rs:1044-1046--errors::error_frame(&id, -32000, ..)on the upstream-proxyErr(e)arm. Reachable.dig-runtime/src/lib.rs:112-113--node_unavailable_jsonrpc(); sole consumerdispatch_node_rpcatlib.rs:130, whose sole production caller isdig_rpcatlib.rs:194. So the built constant is what reaches the FFI wire. Confirmed.rpc_erris genuinely consumed, not merely exposed:dig-runtime/src/lib.rs:113(production) andopenrpc_drift_guard.rs:389(the new test). Not a pub-for-nothing.
3. Guards -- non-vacuity
shell_error_names_match_the_shared_catalogue(openrpc_drift_guard.rs:298-325). IteratesErrorCode::all()(21 variants,meta.rs:974), joins todig_rpc_protocol::ErrorCode::ALLby number, comparesmachine_code(). With the exclusion deleted,-32000now enters the loop; revertingmeta.rs:843to the literal"DISPATCH_FAILED"makes the comparison at:314mismatch"SERVER_ERROR"and go red. Fails on the defect it claims to catch.- Floor
7 -> 8(:321). Correct and meaningful: removing the one carve-out raises the checked count by exactly one, so the tripwire tracks the enumeration rather than lagging it. It is a tripwire, not the assertion -- the assertion is the name equality above. the_shell_and_the_read_path_publish_one_name_for_32000(:383-411). Compares the two emitted frames from the real production minters (dig_node_service::rpc::rpc_error, used atserver.rs:1342/1402;dig_node_core::rpc_err, used at 11 sites incl.lib.rs:3043,3149,3307,3510,3595), not two catalogues. The assertion at:400is the equality;:406-409pins the winner toServerError.machine_code()so a future edit cannot satisfy the equality by reconciling both onto a third string nobody publishes. Non-vacuous.- Mutation claim consistent. Reverting only
meta.rs:843breaks (a)shell_error_names_match_the_shared_catalogueat:314and (b)the_shell_and_the_read_path_publish_one_name_for_32000at:400and:406-- two distinct test functions out of the 11. The PR's "9 passed; 2 failed" is exactly what the code predicts. - One residual:
dig-runtime's ownnode_rpc_without_engine_returns_jsonrpc_error(lib.rs:659-673) asserts onlyerror.is_object()and a message substring -- it would pass under the old string literal too. Non-gating (pre-existing, unchanged by this diff, and the envelope is guaranteed byrpc_err), but it is not evidence for the WU4 change there.
4. content_serve::SERVE_UNREADABLE deletion -- CONFIRMED
rg 'PlaintextOutcome::Unreadable' over all .rs on this head returns exactly two sites: construction at content_serve.rs:989-992 and the sink at server.rs:1886, which destructures { message, .. } and answers StatusCode::BAD_GATEWAY from the message (server.rs:1885-1888). The code field was never read. No wire ever carried it -- a dead const, as claimed. content_serve.rs:97 and :575/615/825/920 are comments only.
5. Blast radius -- every -32000 producer on this head
| Site | Mints via | Name emitted | data.origin |
|---|---|---|---|
server.rs:1342 (dispatch task failed) |
rpc_error(ErrorCode::DispatchFailed) |
SERVER_ERROR |
shell |
server.rs:1402 (wallet-proxy fallthrough) |
rpc_error(ErrorCode::DispatchFailed) |
SERVER_ERROR |
shell |
dig-node-core/src/lib.rs -- 11 sites (3043, 3149, 3307, 3510, 3595, ...) |
rpc_err -> error_frame -> error_object |
SERVER_ERROR |
node |
peer.rs:1273 (admission refusal) |
errors::error_object |
SERVER_ERROR (+ data.reason) |
node |
dispatch.rs:1045 (upstream proxy) |
errors::error_frame |
SERVER_ERROR |
node |
dig-runtime/src/lib.rs:113 (wallet-only guard) |
dig_node_core::rpc_err |
SERVER_ERROR |
node |
Residual DISPATCH_FAILED strings: exactly ONE in the whole worktree -- SPEC.md:976, the deliberate cross-repo note about dig-node-control-interface. (rg, pattern control-tested: SERVER_ERROR returns many hits across SPEC.md, README.md, USER_JOURNEY.md and source, so the pattern is live.) The Rust variant ErrorCode::DispatchFailed survives as internal identity only, which the diff states explicitly at meta.rs:672-679.
6. Versions -- on disk, and the lock agrees
| Artifact | On disk | origin/main |
Lock |
|---|---|---|---|
workspace Cargo.toml |
0.252.81 |
0.252.80 |
dig-node-service 0.252.81 OK |
dig-node-core |
0.68.0 (from 0.67.0) |
-- | 0.68.0 OK |
dig-runtime |
0.4.0 (from 0.3.0) |
-- | 0.4.0 OK |
All three increment and the lock matches. Note (non-gating): the PR body's Versions section is stale -- it says workspace 0.252.41 against a main of 0.252.40. The real numbers are 0.252.81 / 0.252.80, and the diff's three-dot base (0.252.42) is the merge base, not current main. The bump still clears the version gate; only the PR prose is wrong.
7. Test counts (never exit status)
| Command | Result |
|---|---|
cargo test -p dig-node-service --test openrpc_drift_guard |
11 passed; 0 failed; 0 filtered out |
cargo test -p dig-node-core --lib |
1105 passed; 0 failed; 0 filtered out |
cargo test -p dig-node-service --lib |
793 passed; 0 failed; 0 filtered out |
All three matched a non-zero test count. The core figure is 1105, not the 1101 in the PR body (main moved since); not a discrepancy that matters, but the PR's number is stale.
8. Does this close #239? -- NO. Confirmed refuted.
#239 asks to wire canonical dig-rpc-types/dig-rpc into dig-node and delete the hand-rolled table. On this head:
rgfordig-rpc-types/dig_rpc_types/ adig-rpcdependency over the workspace and every crate manifest returns nothing. Onlydig-rpc-protocol = "0.10"(dig-node-service/Cargo.toml:168,312) and= "0.10.2"(dig-node-core/Cargo.toml:192) are present, and both were already there onorigin/main.- The hand-rolled table is intact:
pub enum ErrorCodestill lives atmeta.rs:656with its owncode(),machine_code(),origin(),description()andall().
The PR body claims only Closes #496, so this is consistent -- but #239 must not be closed on this merge.
Non-gating notes (recorded, no action required)
SPEC.md:3278/README.md:359keep the single-valuedorigincolumn asshellfor-32000while the row's own description correctly names both minters. The column cannot express two origins;-32004's row solves it the same way. Cosmetic.dispatch.rs:1045keeping-32000rather than-32010 UPSTREAM_ERRORis called out in the PR body as deliberate and out of scope. Agreed -- changing the number is a separate wire change.
Summary: the reconciliation is correct, the guards are genuinely load-bearing, the deleted const was genuinely dead, and every claim in the PR body except its version/test figures is true on this head. One sentence -- the one that is actually served over rpc.discover -- was not swept. Fix meta.rs:917 and this is a PASS.
Addendum -- head moved mid-review; the verdict still holdsThe PR head advanced from Re-resolved against
Verdict stands: CHANGES-REQUIRED at |
The rpc.discover catalogue's DispatchFailed description said "The node failed to dispatch the request" -- true of the shell's 2 mint sites, false of the embedded read path's 11. Reviewer-caught (dig-node#496): the reconciled name (SERVER_ERROR) now correctly names both producers, but the message describing it still described only one. Widened following the ResourceUnavailable precedent at the same call site, which covers its own dual-minter case the same way. Co-Authored-By: Claude <noreply@anthropic.com>
…-file fmt (#240) Three fixes folded into one commit since they were all found by the same gate round on the same rebase: - genesis_bringup.rs and the bring-up integration test it names live only on the unmerged loop/240-genesis-bringup branch -- this crate has no such file, so SPEC.md's "Proven by tests/genesis_bringup.rs" was false the moment it landed. The underlying claim (genesis is real/non-zero, no code path produces an all-zero id) is still true and is verified here by genesis_challenge_from's own unit tests in peer.rs. Restated the still-open proof obligation (full bring-up + pool convergence) as open, pointing at issue #240 item 1 rather than a file this repo does not have. - Reclaimed the version at 0.252.94: 0.252.81 collided with two other open PRs (#518, #524) and 0.252.82-93 were also already claimed by other open branches at the time of this rebase. - cargo fmt on the five .rs files this branch's own commits touch (never --all): a module-declaration ordering in dig_peer/mod.rs and a single-line-fits signature in tests/server.rs. Co-Authored-By: Claude <noreply@anthropic.com>
….95) Co-Authored-By: Claude <noreply@anthropic.com>
….95) Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…able store records (#524) * chore: open lane for #520 hint-scan doc correction Co-Authored-By: Claude <noreply@anthropic.com> * docs(mirror): the missing pointer has no hint-scan fallback Five doc sites and the dig-node-core SPEC claimed a verifier falls back to `dig-mirror-coin`'s hint scan when a provider record carries no mirror-coin pointer. It cannot: `discover` and `list` both require `owner_puzzle_hash` because the hint is morphed from the owner, and a `ProviderRecord` carries no owner and no route to one. `mirror_bond.rs` was the load-bearing case -- the claim was the stated justification for `claimed_coin_id` being an `Option` -- so it is rewritten to give the real reason: absence is ordinary, and a verifier answers `Unverified`, withholding credit rather than demoting. SPEC.md 6.2 now states that a store-granularity record is unbondable by construction, so a measurement of pointer-less records is not a measurement of uncollateralised holders. Matches the wording landing in dig-dht PR #29 so the two crates agree. Docs only: no behaviour, signature or wire change. Co-Authored-By: Claude <noreply@anthropic.com> * chore(version): bump to 0.252.12 Co-Authored-By: Claude <noreply@anthropic.com> * docs(mirror): the pointer replaces a search, and store ids are deduped per store - dht.rs: change "scan of the mirror puzzle hash" to "searching for it" - pointers.rs: same wording fix for consistency - SPEC.md: correct deduplication — stores are deduplicated, so S stores produce S pointer-less records; change "a large share" to "some" provider records Co-Authored-By: Claude <noreply@anthropic.com> * chore(release): re-bump to 0.252.81 -- main advanced to 0.252.80 during merge 0.252.75 (this PR's prior version) is now BELOW origin/main's tip, which would fail ensure-version-increment.yml. Re-bump above the current tip. Co-Authored-By: Claude <noreply@anthropic.com> * chore(release): re-bump to 0.252.93 -- avoid collision with #518/#530 at 0.252.81 #518 and #530 are also sitting at 0.252.81; #518 claimed it first. Take 0.252.93 to clear origin/main (0.252.80) and leave room for sibling lanes. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…#241 (#530) * chore(release): claim 0.252.33 for the #242/#241/#396/#240 batch Anchor commit for the four-ticket batch. Version claimed early so a sibling lane does not take 0.252.33; the substantive commits follow on this branch. Co-Authored-By: Claude <noreply@anthropic.com> * chore(legacy): sweep the dig-companion-era prose and disposition the dead_code suppressions Closes the bounded #242 checklist (items 1-4). - tests/server.rs: rename the 92 bare-word "companion" uses to "node". Test-local identifiers, module docs and prose only; no path, env var, wire value or asserted literal changed. Survivors: zero. - SPEC.md 11.2a: record the KEEP verdict for the surviving dig-companion references (live byte-identical cache path, frozen historical release name, the guard test that a literal "grep must be clean" sweep would delete), with re-located file:line, plus the measured ZERO #[deprecated] attributes. - allow(dead_code): capsule_resolver's module-level allow removed - its stated precondition (PR-3 constructs it) has landed, tier0_live.rs now consumes it. Reasons added to the two suppressions that carried none (tests/content_serve.rs, tests/support/mod.rs). The remaining sites already name what consumes them and when. Co-Authored-By: Claude <noreply@anthropic.com> * docs(spec): retire four stale pre-launch-placeholder-genesis clauses (#240) The DIG L2 mainnet genesis has been real and non-zero since 2026-07-17 (dig-constants DIG_MAINNET_GENESIS_CHALLENGE, the Chia mainnet header hash at height 9,021,277); dig-node declares dig-constants 0.13.0. genesis_challenge_from falls back to that real value for EVERY invalid DIG_NETWORK_GENESIS input, so no code path can hand gossip an all-zero network_id and dig-gossip rejects only an all-zero one. Bring-up therefore proceeds. Four clauses asserted the opposite and were manufacturing false defect reports: dig-node-core/SPEC.md 7.2 bring-up, the DIG_NETWORK_GENESIS override paragraph, the env-table default column, and top-level SPEC.md 19.7. Stated precisely: the value is canonical-and-revisable-at-launch, not a placeholder, and a re-anchor is a deliberate protocol event with every derived AGG_SIG domain recomputed. Acceptance means bring-up is not gated; it does not claim the pool connects to anyone. Also narrows the capsule_resolver module-level allow(dead_code) to the single accessor that is genuinely unconsumed, with the reason on it. Co-Authored-By: Claude <noreply@anthropic.com> * docs(spec): state the NFT-metadata and theme-derivation surfaces as intentionally unserved (#241) Gap 3 (18.12a) was FALSE, not merely vague: it said the NFT metadata JSON "surfaces when fetched", and nothing in dig-wallet can fetch it. The only writers of the nft_metadata_json cache are two #[cfg(test)] sites (db.rs:5499, 5905), so get_nft_data's metadata_json is None for every NFT in production, unconditionally, and redownload_nft clears a cache nothing ever fills - a no-op that reports success. Restated as intentionally unserved, with the security boundary that justifies it (third-party-controlled URIs into a loopback daemon holding spend keys) and the guard set serving it would require. Gap 2 (18.16): the theme store persists the fixed marker "auto" (DERIVED_THEME_PLACEHOLDER, verified) and its served meaning is a boolean wearing a string. Stated as intentionally unserved rather than a tracked follow-on, with an explicit MUST NOT render / MUST NOT infer for clients. 18.15 (exercise_options) deliberately left alone - a sibling lane owns it. Co-Authored-By: Claude <noreply@anthropic.com> * docs(spec): cite the default-genesis bring-up proof and state its hermetic limits (#240) Names crates/dig-node-core/tests/genesis_bringup.rs (Lane B) as the evidence that bring-up is not gated, and says out loud what that test does NOT prove: it is hermetic (loopback, relay off, zero peers), so pool convergence and PEX exchange are unproven -- the DHT comes up, it does not converge. Co-Authored-By: Claude <noreply@anthropic.com> * fix(spec): correct a false test citation, claim 0.252.94, fix touched-file fmt (#240) Three fixes folded into one commit since they were all found by the same gate round on the same rebase: - genesis_bringup.rs and the bring-up integration test it names live only on the unmerged loop/240-genesis-bringup branch -- this crate has no such file, so SPEC.md's "Proven by tests/genesis_bringup.rs" was false the moment it landed. The underlying claim (genesis is real/non-zero, no code path produces an all-zero id) is still true and is verified here by genesis_challenge_from's own unit tests in peer.rs. Restated the still-open proof obligation (full bring-up + pool convergence) as open, pointing at issue #240 item 1 rather than a file this repo does not have. - Reclaimed the version at 0.252.94: 0.252.81 collided with two other open PRs (#518, #524) and 0.252.82-93 were also already claimed by other open branches at the time of this rebase. - cargo fmt on the five .rs files this branch's own commits touch (never --all): a module-declaration ordering in dig_peer/mod.rs and a single-line-fits signature in tests/server.rs. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
…l genesis (#240) (#533) * test(peer): stub for genesis bring-up e2e (#240) Co-Authored-By: Claude <noreply@anthropic.com> * test(peer): prove bring-up installs the downstream engines on the real genesis (#240) The acceptance test dig-node#240 has been open for: with the default DIG mainnet genesis and no DIG_NETWORK_GENESIS override, the peer-network bring-up gets past gossip-config validation and installs the pool handle, the P2P content engine and the DHT inventory-refresh hook, then binds the mTLS peer-RPC listener. Asserts the DOWNSTREAM post-conditions rather than peerStatus.running, which is set before GossipService::new and so holds even when the pool, DHT, content engine and PEX all fail. Also corrects a stale doc-comment that still described the genesis as a pre-launch placeholder that invalidates the gossip config -- the reason the #213 test sees no P2P convergence is environmental (relay off, loopback only), not a rejected config. Co-Authored-By: Claude <noreply@anthropic.com> * style(peer): rustfmt the #240 bring-up test Co-Authored-By: Claude <noreply@anthropic.com> * chore(release): v0.253.5 Merge origin/main into loop/240-genesis-bringup and take the next available patch version (0.253.0-0.253.2 are already claimed by concurrent open PRs #524/#528/#518). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
Closes #496.
What changed
-32000had TWO producers on one port publishing TWO machine names. Measured onorigin/main: the embedded read path mints it at 11 sites (lib.rs:3043,3149,3307,3310,3510,3513,3595,3598,3685,3965,4017), all througherrors::error_object, which resolves the name fromdig-rpc-protocolby number and therefore emitsSERVER_ERROR. The shell mints it at 2 sites (server.rs:1342,1402) asDISPATCH_FAILED.server.rs:1336-1344returns core's frame verbatim, so both names reach the same client at the same number, and a client branching ondata.codecannot see which layer answered — which is exactly whatdata.originis for.SERVER_ERRORis authoritative. The shell is reconciled onto it.meta.rssources-32000's number and name fromdig_rpc_protocol::ErrorCode::ServerError. The Rust variant keeps the nameDispatchFailed: internal identity, not the wire. The three comments asserting the (false) claim that the shell alone mints-32000are rewritten.DispatchFailedexclusion is deleted rather than replaced by a list. The loop already sweepsErrorCode::all(), so removing the carve-out is the whole fix; floor 7 → 8.-32001):peer.rs:1269carried only{reason},dispatch.rs:1042carried nodata, anddig-runtime'sNODE_UNAVAILABLE_JSONRPCwas a raw string literal invisible to every type-scanning guard. All three now mint through the canonical builder;dig-runtime's is now BUILT rather than written down.SPEC.md(×4 incl. §5.2 step 3, plus the residue sentence),README.md:359,USER_JOURNEY.md:169.content_serve::SERVE_UNREADABLEdeleted: itscodefield's only sink (server.rs:1886) destructures{message, ..}and answers 502, so the number never reached a wire — a dead const, not the taxonomy gaplib.rs's comment claimed.dig_node_core::rpc_errbecomespubso the frame the crate actually emits is reachable from an integration test rather than a restatement of it.Blast radius checked
ErrorCode::DispatchFailed(name/code),errors::error_object/error_frame,PlaintextOutcome::Unreadable,NODE_UNAVAILABLE_JSONRPC.Unreadablehas exactly one constructor (content_serve.rs:994) and one reader (server.rs:1886,{message, ..}) — verified by grep over allUnreadable {sites. Ecosystem consumer sweep across all 92 submodules: zero consumers branch on the stringDISPATCH_FAILED(dig-sdk/test/miss.test.mjs:84branches on the NUMBER, unaffected).docs.dig.net: zero hits, control-tested againstRESOURCE_UNAVAILABLE(3+ files). Risk: LOW — this removes a name that never reliably worked rather than breaking one that did.meta.rs:1318's wallet-band guard is scoped-32049..=-32040andcontinues outside it, so-32000is skipped and it does not go red.Evidence
test result: FAILED. 0 passed; 1 failed; 10 filtered out—the shell says "DISPATCH_FAILED", the read path says "SERVER_ERROR".openrpc_drift_guard— 11 passed; 0 failed; 0 filtered out.meta.rs's name to the literal"DISPATCH_FAILED"→ 9 passed; 2 failed, both new-guard failures with the exact drift text. The test is load-bearing.cargo test -p dig-node-core --lib— 1101 passed; 0 failed; 0 filtered out.cargo test -p dig-node-service --lib— 793 passed; 0 failed; 0 filtered out.cargo build -p dig-runtime— clean.Versions
Workspace
0.252.95(kept aboveorigin/mainthrough repeated merges as sibling PRs landed; renumbered under the MSI ProductVersion ceiling by #521, current tip also carries #509's collateral-census and #531's string-escape fix).dig-node-core0.67.0 → 0.68.0 (additive publicrpc_err).dig-runtime0.3.0 → 0.4.0 (envelope change).Deliberately not done
dig-node-control-interfacedeclares-32000 → DISPATCH_FAILEDin the-32000..-32019banddig-rpc-protocolowns. Different repo, different writer — reported for filing, not touched. dig-node mints noControlErrorCode::DispatchFailed.dispatch.rs:1042keeps the number-32000though it is semantically an upstream-proxy failure arguably belonging at-32010 UPSTREAM_ERROR. Changing the number is a separate wire change.-32050..-32052) remains undeclared upstream. Pre-existing.