Conversation
Add a duplicate-logic gate: the workbench bin `check-code-duplication` runs cargo-dupes' AST-normalizing analyzer (crates.io `dupes-core` / `dupes-rust`, pinned =0.2.1) over `packages/` (excluding the generated `zaino-proto` output and in-crate `tests/` modules), `live-tests/`, and `tools/`, and fails on any exact or near duplicate group. Identifiers normalize to positional placeholders, so renamed copies still match. The gate runs locally via the `lint-code-duplication` task under `makers lint` and in CI as a `repo-guards` step, so it executes on every pull request. `min_nodes` 50 was calibrated empirically: it is the floor at which every reported group in this tree was a genuine twin; the calibration rationale and the annotated `.dupes-ignore.toml` escape hatch are documented in the bin. The thresholds are zero because this commit also deduplicates every existing finding. In zaino-state, the six copy-pasted stream newtypes collapse into one generic `ChannelStream<T>` with six type aliases; the accumulator's added/removed-output methods share one body parameterized by a private `OutputDelta` enum; the median-time-past walk (which carried a TODO prescribing exactly this) is one function generic over the per-block fetch; the ~130-line `get_block_range` / `get_block_range_nullifiers` twins share `spawn_block_range_stream`, parameterized over the per-block mapping; and `RpcRequestError` classification happens once in `RpcRequestFallback::classify`, adopted by the `MempoolError`, `NonFinalisedStateError`, and `FinalisedStateError` conversions — the third of which the new gate itself surfaced. Deduplicating the `Broadcast` / `BroadcastSubscriber` `Debug` impls also fixes a copy-paste bug: the subscriber printed itself as "Broadcast" and labeled its `watch::Receiver` a `Sender`. The `BlockData::new` and `BlockMetadata::new` constructors were pure field copies flagged as structural twins; both are deleted in favor of named struct literals at their call sites. In the live tests, the five helper twins shared by `devtool.rs` and `devtool_zcashd.rs` (plus their near-duplicate launch preambles) become generic bodies in `e2e::devtool`, with the expected shield pool (Ironwood under NU6.3 heights, Orchard on the zcashd column) passed explicitly; the clientless `get_block_header` oracle closures share `clientless::assert_get_block_header_matches`; and the orchard/ironwood coinbase predicates share `is_valid_shielded_coinbase` over a `CoinbaseRewardPool` enum. The `zcashd_support` feature path does not currently compile from this tree (nothing forwards `legacy-stack` to zcash_local_net — a pre-existing condition of the zcashd deprecation, see docs/adr/0005), so the `devtool_zcashd.rs` changes are consistent with the zebrad column but compiler-unverified. The gate was negative-tested: a planted duplicate with renamed identifiers fails the bin with exit code 1 and a per-member report, and removing it restores a passing run over 138 files with zero groups. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
zancas
requested review from
idky137 and
nachog00
and removed request for
idky137
July 15, 2026 22:01
nachog00
approved these changes
Jul 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
This PR adds a CI gate that fails when new duplicate Rust logic enters the tree, and deduplicates every existing finding first so the gate lands at zero-tolerance thresholds with an empty ignore list.
The gate
The workbench bin
check-code-duplicationruns cargo-dupes' AST-normalizing analyzer (mpecan/cargo-dupes, published asdupes-core/dupes-rust, pinned=0.2.1in the workbench lockfile) over three scopes:packages/(excluding the generatedzaino-prototonic output and in-cratetests/module directories),live-tests/, andtools/. Identifiers normalize to positional placeholders and literals are erased, so a renamed copy of a function still matches. Any exact or near duplicate group of at least 50 AST nodes fails the build. The floor of 50 was calibrated empirically against this tree: it is the level at which every reported group was a genuine dedup-worthy twin, while lower settings flag structurally-rhyming but semantically unrelated small functions. The calibration rationale and the escape hatch (annotated entries in.dupes-ignore.toml, created viacode-dupes ignore <fingerprint> --reason "...") are documented in the bin's module docs.The gate runs locally as
lint-code-duplicationundermakers lint, and in CI as a step in the existingrepo-guardsjob, so it executes on every pull request on a GitHub-hosted runner. It was negative-tested: a planted duplicate with renamed identifiers fails with exit code 1 and a per-member report naming each twin's file and line range.The deduplication
Every finding the detector reported was fixed with a plain function or generic type — no macros — per the repo DRY rule:
zaino-state/src/stream.rscollapse into one genericChannelStream<T>; the original six names survive as type aliases, so no call site changed.FinalisedTxOutSetInfoAccumulator::apply_added_output/apply_removed_outputshare one body parameterized by a privateOutputDeltaenum; the XOR fold thatcombinealso repeated is one method.MockchainSourceand the validator connector (and carrying a TODO prescribing exactly this fix), is one function generic over the per-block fetch closure.get_block_range/get_block_range_nullifierstwins sharespawn_block_range_stream, parameterized over the per-block mapping (identity vscompact_block_to_nullifiers).RpcRequestErrorclassification happens once inRpcRequestFallback::classify, adopted by theMempoolError,NonFinalisedStateError, andFinalisedStateErrorconversions. The gate itself surfaced the third copy after the first two were unified.Broadcast/BroadcastSubscriberDebugimpls fixes a copy-paste bug: the subscriber printed itself as"Broadcast"and labeled itswatch::ReceiveraSender.BlockData::newandBlockMetadata::newwere false twins — unrelated types whose field-copy constructors merely rhyme structurally — so both no-value positional constructors are deleted in favor of named struct literals at their call sites.e2e/tests/devtool.rsanddevtool_zcashd.rs(plus their near-duplicate launch preambles) become generic bodies ine2e::devtool, with the semantically load-bearing difference — which pool a shield lands in — lifted into an explicit parameter; the clientlessget_block_headeroracle closures shareclientless::assert_get_block_header_matches; and the orchard/ironwood coinbase predicates shareis_valid_shielded_coinbaseover aCoinbaseRewardPoolenum.Verification
cargo check --all-targets,cargo clippy --all-targets, andcargo fmt --all --checkare green on the production set, as are the workbench crate's fmt/clippy/tests andcargo check --all-targetsfor thee2eandclientlesscrates. Two caveats are pre-existing and were verified by stash-testing the clean tree: the--all-featuresclippy failure inwrite_core.rs(transparent_address_history_experimentalbit-rot), and thezcashd_supportfeature path not compiling (nothing forwardslegacy-stacktozcash_local_net), which leaves thedevtool_zcashd.rschanges consistent with the zebrad column but compiler-unverified.🤖 Generated with Claude Code