Skip to content

feat: add Ironwood support, AKA Bump to nu6.3#278

Open
zancas wants to merge 26 commits into
devfrom
bump_to_NU6.3
Open

feat: add Ironwood support, AKA Bump to nu6.3#278
zancas wants to merge 26 commits into
devfrom
bump_to_NU6.3

Conversation

@zancas

@zancas zancas commented Jul 5, 2026

Copy link
Copy Markdown
Member

Atop #277

  • NU6.3 activated across the regtest stack (breaking). The zebrad config writer emits "NU6.3" = when configured and omits the key when unset (so pre-6.0.0 zebrad configs stay parseable); the devtool activation-heights TOML gains nu6_3; activation_heights_from_getblockchaininfo reads "NU6.3" back. Canonical heights advance in lockstep (new ADR 0002): devtool-supported heights put NU6.3 at 2, test heights co-activate NU6.1/6.2/6.3 at 5, and the regtest-launcher's defaults and all= sweep include it.

  • Hardened the zebrad config writer (breaking). It now rejects activation heights it cannot express instead of silently dropping or rewriting them: upgrades through Canopy must be 1, and a configured NU7 panics until the writer gains NU7 emission. Regression tests pin NU6.3 emission/omission and both rejections (the silent-drop failure mode is what cost First NU6.3 block's coinbase is served as Orchard, not Ironwood (zebrad routing off-by-one, or a zaino pool-swap?) zaino#1368 three diagnostic rounds).

  • WalletBalance.ironwood_spendable, consumed from devtool balance --json. Requires zcash-devtool at or past 8eccaceb (support_ironwood_scan_model).

  • Shield test asserts the ironwood pool. With NU6.3 active, the shielded value plus coinbase subsidies accrue to ironwood_spendable, and orchard_spendable is asserted to stay 0 — consensus forbids value entering orchard from NU6.3 onward.

  • Binary floor / known gap: zebrad ≥ 6.0.0; zainod ≤ 0.4.2 cannot parse zebra 6.x getblockchaininfo (fixed 5-entry valuePools), so indexer-sync paths wait on a NU6.3-aware zainod (Regtest fixture has zero NU6.1 coverage — depends on zcash_local_net lockbox disbursements zaino#1076).

  • Docs: ADR 0002, CONTEXT.md, CHANGELOG Unreleased entries.

zancas and others added 13 commits July 3, 2026 10:26
bip0039 existed only to re-derive the hardcoded ABANDON_ART_SEED
constant inside a unit test, dragging ~18 crates (zerocopy, the old
rand 0.8/getrandom 0.2 stack, pbkdf2/hmac/password-hash, zeroize,
unicode-normalization) into every consumer's deploy graph. The test
now validates the constant's structure against the canonical BIP-39
zero-entropy vector directly.

The unmaintained json crate had a single call site in the zcashd
validator; migrated to serde_json, matching the idiom already used
in get_activation_heights.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The getblocktemplate -> proposal_block_bytes -> submitblock sequence
existed in three copies: Zebrad::generate_blocks and both of
regtest-launcher's mining loops (bootstrap and steady-state). Any
change to proposal assembly or submission semantics had to land in
all three or they drifted silently.

One public round trip now serves all callers, returning a
BlockSubmission (height, block hash, raw response) whose accepted()
documents the duplicate-response ambiguity that generate_blocks'
chain-advance polling exists to resolve.

regtest-launcher no longer hex-encodes anything; drop its hex dep.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Launch path (findings #1-#3 of the duplication survey):
- PortPins trait on launch configs replaces the per-daemon
  read/clear closure pairs fed to with_retry_on_collision; the three
  ceremonial single-field *Ports structs are gone (ZebradPorts stays
  - its four-field atomic pick rationale is real). Collision
  signatures remain per-daemon, next to their contract docs.
- launch::spawn_and_wait owns pipe-setup -> spawn -> readiness wait
  (with uniform timing instrumentation) for all four daemons;
  launch::wait is now private.
- MAX_LAUNCH_ATTEMPTS hoisted; the take/re-Some dance on the
  additional-log accumulator in wait() collapsed via as_mut().

Shared helpers:
- activation_heights_from_getblockchaininfo now owns the upgrades
  extraction both validators duplicated.
- zebra_rpc decode_hex/fixed_bytes<N> replace three hand-rolled
  InvalidHex mappings.
- JsonLine wrapper unifies the balance/get-info line-scan parsers in
  the devtool client.
- config.rs write_config_file is the single config write path.
- The inherent logs_dir() getters duplicated the LogsToDir impls
  byte-for-byte on five structs; the trait is now the one definition.

Left alone deliberately: the six identical Drop impls (Rust forbids
blanket Drop; a macro would trade explicitness for less than it
saves) and the per-endpoint RPC probe tests (separate tests preserve
failure granularity).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Function-first triage: the data_dir() getters on both validators
duplicated their Validator::data_dir trait impls byte-for-byte, so
those die by plain deletion, no macro needed. What remains is
exactly what helper fns cannot express - trait impl blocks and
method definitions:

- macros::impl_stop_on_drop! replaces the five identical
  drop-delegates-to-stop impls (Zcashd, Zebrad, Zainod,
  Lightwalletd, Empty). Opt-in per type because the language forbids
  the blanket impl<T: Process> Drop for T this would otherwise be.
  LocalNet's generic Drop stays hand-written.
- macros::ref_getters!/copy_getters! generate the ~20 remaining
  field accessors across the daemon and devtool structs, doc
  comments preserved per field.
- zingo-consensus: local height_getters!/height_setters! collapse
  the 11 activation-height getters and 11 builder setters.

These are in-repo macro_rules, not a derive dependency - the getset
removal's objection was the dependency, not macros.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lt feature

Phase 1 of the two-phase excision recorded in
docs/adr/0001-excise-legacy-stack.md: everything zcashd- and
lightwalletd-shaped moves behind a new non-default `legacy-stack`
cargo feature; phase 2 (within two weeks) deletes the gated code and
the feature entirely. zcash_local_net bumps 0.6.0 -> 0.7.0.

Gated under `legacy-stack` (UNSUPPORTED and UNTESTED — CI never
enables it; it exists only as a short-lived migration stopgap):
- validator::zcashd and indexer::lightwalletd
- ProcessId::{Zcashd, Lightwalletd}
- LaunchError::{UnsupportedZcashdCapability, CapabilityProbeFailed}
- Validator::get_zcashd_conf_path + zebrad's impl and the
  compatibility zcash.conf zebrad wrote solely for lightwalletd
- config::{write_zcashd_config, write_lightwalletd_config} and the
  ZCASHD/LIGHTWALLETD filename constants, logs::LIGHTWALLETD_LOG
- all legacy integration and unit tests (kept, not deleted, so one
  manual `--features legacy-stack` smoke remains possible pre-release)

Deleted outright (no consumer even under the gate):
- the checked-in zcashd-generated chain cache
  (chain_cache/client_rpc_tests/) and generate_zcashd_chain_cache;
  in-repo tests only use the zebrad-generated client_rpc_tests_large
- cert/cert.pem (no consumer anywhere; lightwalletd runs
  --no-tls-very-insecure)
- the CI step symlinking zcashd/zcash-cli/lightwalletd binaries and
  the legacy lines in the utils/*.sh binary scripts

Also: generate_zebrad_large_chain_cache launches a bare Zebrad
instead of LocalNet<Zebrad, Lightwalletd>; the executable_finder
ignored test probes zebrad instead of zcashd; crate docs/README
rewritten around the zebrad + zainod + zcash-devtool core stack.

New: CONTEXT.md (glossary: Legacy stack, Core stack, Compatibility
conf) and docs/adr/0001-excise-legacy-stack.md capturing why one
feature instead of two, why the gate is deliberately untested, and
why the Validator/Indexer abstractions stay.

Verified: cargo check --workspace --all-targets (-D warnings),
clippy, fmt, and cargo doc clean in both feature configs; lib unit
tests pass 24/24 (default) and 35/35 (legacy-stack).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Findings from a dependency audit of the workspace:

- zcash_local_net: delete the [build-dependencies] section (hex, tokio
  with three features). The crate has no build.rs, so the section was
  inert manifest noise. cargo-machete does not flag this class.

- regtest-launcher: drop assert_cmd (-9 lockfile entries). Its entire
  usage was one macro call locating the test binary; cargo provides
  this natively to integration tests via env!("CARGO_BIN_EXE_<name>").

- regtest-launcher: drop insta (-4 lockfile entries) and its orphaned
  [profile.dev.package] opt-level override. The single snapshot
  assertion is now a plain golden-file compare against
  tests/golden/regtest_first_mine.txt; set UPDATE_GOLDEN=1 to re-bless
  in place of `cargo insta review`.

Audited and deliberately kept: sha2 (crypto, never hand-roll), hex and
owo-colors (zero transitive deps), regex/nix/anyhow (dev, properly
used), serde (required by serde_json integration). A reqwest
replacement (~70 entries hang off two localhost HTTP calls) was
prototyped and rejected: not worth the breaking public API surface
(RpcClientError::Transport, RpcRequestClient::call, Zebrad
healthy/ready signatures).

Verified: cargo check --workspace --all-targets in both feature
configs, 24/24 lib unit tests, cargo fmt --check, and the
regtest-launcher e2e golden test passing against real zebrad + zainod.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Retitle the Unreleased section to 0.7.0 (2026-07-03) and add the
entries missing since 0.6.0: the rpc_client and zebra_rpc modules,
the zingo-consensus type migration and MinerPool (both breaking),
the getset -> hand-written accessor swap, and the removal of every
zebra/librustzcash/zcash ecosystem dependency plus bip0039, json,
and getset.

Also correct the RUSTSEC reference in the macros.rs module doc: the
advisory this repo actually tracked (and whose cargo-deny ignore the
getset removal deleted) is RUSTSEC-2026-0173 against proc-macro-error2
itself, not RUSTSEC-2024-0370 against its predecessor.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…fig writer

The zebrad config writer emits `"NU6.3" = <height>` (requires zebrad >=
6.0.0) and now rejects activation heights it cannot express — upgrades
through Canopy must be Some(1), and a configured NU7 panics until the
writer gains NU7 emission — instead of silently dropping them, the
failure mode zingolabs/zaino#1368 reconstructs against this writer at
v0.7.0. `activation_heights_from_getblockchaininfo` reads "NU6.3" back.

The devtool client's canonical heights gain nu6_3 = 2, staying in
lockstep with devtool DEFAULT_REGTEST (zcash/zcash-devtool#205, ADR
0002); its activation-heights TOML writer emits nu6_3, hard-requiring a
devtool binary whose schema knows the key. The height-5 fixture and the
launcher default co-activate NU6.3 with NU6.1/NU6.2; the launcher's
`all=` sweep includes nu6_3 and main.rs gains the previously dropped
set_nu6_3 conversion (its mirror test already had it). CONTEXT.md gains
the Devtool contract and Canonical heights terms.

Unit tests pin NU6.3 emission and omission, the canopy/NU7 rejections,
and the CLI-string <-> fixture equivalence. Verified live: the devtool
faucet send flow passes end-to-end on an NU6.3-active chain (zebra
6.0.0-rc.0, zaino PR #1362, devtool PR #205 re-pinned to librustzcash
dw/ironwood-scan-model); shielding remains blocked upstream
(fees::common::OutputManifest has no ironwood field, reported toward
librustzcash#2539).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
zingolabs/zcash-devtool 8eccaceb (PR #209 line) added the field;
WalletBalance carries it as a required key per the ADR-0002 hard-require
pattern, and the contract stamps now name that commit as the tested
devtool (its 0.1.0 package version does not distinguish branches).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
With NU6.3 active for the whole test window, consensus forbids value
entering orchard: coinbase subsidies and the shielded output land in
ironwood, so faucet_shields asserts ironwood_spendable and pins
orchard_spendable to zero. Verified green against zebra 6.0.0-rc.0,
zainod PR #1362, and devtool PR #209 (librustzcash basic_ironwood
@ d7270910, which completes the Ironwood change-output path).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@zancas zancas requested a review from dorianvp July 5, 2026 18:06
@zancas zancas changed the title Bump to nu6.3 feat: add ironwood support, AKA Bump to nu6.3 Jul 6, 2026
@zancas zancas changed the title feat: add ironwood support, AKA Bump to nu6.3 feat: add Ironwood support, AKA Bump to nu6.3 Jul 6, 2026
@zancas zancas requested a review from nachog00 July 6, 2026 15:51
zancas and others added 13 commits July 6, 2026 21:47
… enforced at compile time (ADR 0003)

Remove the devtool client's canonical-heights equality guard and delete
the ClientError::UnsupportedActivationHeights variant. A height vector
can no longer be written into a wallet config at all:
ZcashDevtoolConfig::network becomes the new client::WalletNetwork enum,
whose regtest variant demands a client::ValidatorHeights — an opaque
type whose only public constructor, WalletNetwork::from_validator,
queries the running validator for its schedule. A wallet whose heights
disagree with its validator is therefore unrepresentable (ADR 0003, new
in docs/adr/). The faucet and recipient constructors take the network
as a parameter, the config's Default impl and the ClientConfig Default
bound are removed, and network_flag() becomes infallible. The client
serializes the derived heights into the devtool's --activation-heights
TOML, omitting keys for inactive upgrades, and now panics on a
configured NU7 instead of silently dropping it.

ZainodConfig::network narrows to the new payload-free
zingo_consensus::NetworkKind: an Indexer config that accepts activation
heights is a false affordance under the same invariant, and the config
writer only ever emitted the kind string.

Golden unit tests pin a mid-chain fixture (NU6.3 at height six) to its
acceptance TOML byte for byte and pin absent-key omission; the
integration tests derive every wallet's network from the validator, and
a new cross-boundary orchard-to-ironwood test is added, ignored until
the Indexer side of the invariant lands.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nvergence barrier

Replace kernel-ephemeral allocation in network::pick_unused_port.
Ports now come from a fixed band below every default ephemeral range
(16384–32767), partitioned into per-process slices by process id,
walked sequentially, and bind-checked before they are returned. The
band prevents the kernel from reusing a picked port for an outgoing
connection in the pick-to-child-bind window; the slices keep parallel
nextest processes out of each other's territory; the bind-checked walk
steps over squatted ports deterministically. The launch-time
retry-on-collision machinery remains as the backstop for the residue
partitioning cannot remove.

Add LocalNet::generate_blocks_converged and
LocalNet::await_indexer_convergence. Mining returns as soon as the
Validator has the blocks, while the Indexer syncs on its own cadence,
so tests that read through the Indexer right after mining race it; the
barrier blocks until the Indexer's chain index reports the Validator's
tip, retiring that class of wallet-side polling workaround. The
observation channel is zainod's "Syncing block, height: N" stdout
line, read by Zainod::logged_sync_height after ANSI stripping, because
the fetch backend proxies the protocol's height answers straight to
the validator and the log is the only view of the Indexer's own
progress. Failure is loud and precise by design: the new
IndexerSyncError surfaces an unreadable log, a drifted log format
carrying the offending line, or a timeout carrying the target height,
the last observed height, and the log tail — never a silent hang. The
log contract is captured from zainod 0.4.3-ironwood.1, pinned offline
by parser unit tests against a recorded line, and pinned live by the
generate_blocks_converged_reaches_validator_tip integration test.
CONTEXT.md gains the "Indexer convergence" glossary term.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The External Types CI gate rejects any public API that references an
external type missing from the crate's allowlist, and the previous
commit exposed the new zingo_consensus::NetworkKind in two places:
the ZainodConfig::network field and the protocol module's re-export.
Add NetworkKind to zcash_local_net's allowed_external_types beside its
sibling NetworkType. Verified locally with the same nightly toolchain
and cargo check-external-types invocation the CI job runs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CI's nextest profile excludes integration tests by name
(test(zebra) | test(zaino) | test(devtool_client)) because the runner
image provisions no Zcash binaries, and the new convergence test's
name matched no filter despite driving zebrad and zainod. Rename it to
zainod_converges_to_validator_tip_after_generate_blocks so the
existing filter covers it, and point the documentation references at
the new name. Verified with cargo nextest list --profile ci that the
test no longer runs under that profile, and locally that it still
passes against the real binaries.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Seventeen of the repository's eighteen shell scripts were dead weight:
six operated on the long-gone fetched_resources binary tree, one
inspected artifacts of the equally retired fetcher build script, and
the whole zcash_local_net/utils directory was byte-identical copies of
the root utils scripts plus a one-line nextest wrapper. The remaining
one-line cargo wrappers had no references from CI, documentation, or
code; the only still-meaningful one, the chain-cache generator,
survives as the generate-chain-caches justfile recipe. The one script
CI actually runs, utils/trailing-whitespace.sh, stays for the next
commit, which ports it to a Rust workbench crate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The contributor guidelines require checked-in tooling that
collaborators or CI run to be Rust in a workbench crate, and
utils/trailing-whitespace.sh was the last shell script standing — the
only one CI executed. Introduce the workbench workspace member (no
dependencies, publish = false) with a trailing-whitespace subcommand
preserving the script's exact contract: fix and reject modes, the
.rs/.md/.toml/.yaml file set, .git and target pruned, spaces-only
matching (tabs and CRLF are content), repo-root anchoring, and exit 1
on findings. Files are processed as bytes end to end, so non-UTF-8
content passes through untouched. Unit tests pin the stripping,
detection, and walk-pruning behavior; a live run against the tree
agrees with the shell script's verdict. The Trailing Whitespace CI
workflow now runs the crate, and the script and the emptied utils
directory are deleted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The whitespace job now compiles the workbench crate, so it gains the
same Swatinem/rust-cache step the repository's other cargo-running
workflows use. The first run on a branch pays the toolchain install
and build once; subsequent runs restore the cache.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The justfile held two pieces of logic: the pinned-nightly
cargo-check-external-types invocations for the two allowlisted crates,
and the chain-cache generator recipe. Both become workbench
subcommands. check-external-types checks every manifest even after a
failure, so one run reports every offending crate, and the pinned
nightly lives in one Rust constant whose documentation names the
coupled cargo-check-external-types version and the CI job that
installs both. generate-chain-caches wraps the ignored generator test.
The External Types job now runs the workbench under its
already-installed nightly and no longer installs just; the justfile is
deleted, which leaves the repository with no shell scripts and no just
glue — checked-in tooling is Rust in the workbench, per the
contributor guidelines.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rename the client layer to the Wallet abstraction: the client module
becomes wallet, the Client and ClientConfig traits become Wallet and
WalletConfig, and ClientError becomes WalletError, whose messages now
name the operation rather than one binary. The trait is the interface
through which the harness drives any wallet implementation, and
implementations live with their binaries: the zcash-devtool one
remains in-tree for now, and a zingo-cli implementation lands in the
zingolib repository against zingolabs/zingolib#2419 (the request spec
is added as zingolib-wallet-impl-spec.md; nothing implements the
second wallet here).

Add LocalNet::launch_wallet::<W>(make_config), the generic actuation
path: it mints the WalletNetwork from the running Validator, wires the
Indexer connection, and launches whichever implementation the caller
names. The integration tests' helper now routes through it. Add
ValidatorHeights::activation_heights(), a read-only accessor, because
a foreign implementation must serialize the reported schedule into its
own binary's configuration; construction remains private, so the ADR
0003 provenance guarantee is untouched. CONTEXT.md gains the Wallet
glossary term.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat!: define the Wallet abstraction the harness actuates generically
…-validator hop

zingolib's test suite runs a pipeline observatory: recording TCP relays
on every hop of wallet <-> zainod <-> zebrad <-> harness, built to
adjudicate an investigation into warm-run chain-cache failures whose
signature is cross-wired RPC traffic between concurrently launching
tests. Two hops are tappable from zingolib because it mints both
endpoints, but the zainod->zebrad hop was not: LocalNet's launch wires
the indexer to the validator internally, between the two process
launches, so a consumer had no seam for interposing a relay.

LocalNet::from_parts(validator, indexer) closes that gap. It is purely
additive: the caller launches the validator, wires the indexer config's
validator connection itself (possibly to a proxy), launches the
indexer, and assembles; the assembled net's Drop stops both processes
exactly as with launch_from_two_configs, whose behavior is unchanged.

A regression test pins the seam end to end against the real binaries:
it launches zebrad, interposes a minimal in-test tokio TCP relay in
front of its JSON-RPC port, launches zainod against the relay,
assembles via from_parts, mines to Indexer convergence, and asserts
nonzero bytes crossed the relay — proving zainod really spoke to zebrad
through the interposed hop.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every published port and address accessor of the core stack now
returns a per-listener front proxy instead of the process's own
listener. The front binds 127.0.0.1:0 before its backend starts, so
the public port is assigned atomically by the kernel and the public
surface carries no check-then-bind race; the backend's real endpoint
becomes a private detail of launch plumbing. Because the harness's own
launch-time clients — the RPC readiness probe, the gRPC listener
probe, and the regtest launch-mine — obtain their addresses through
the same accessors, their traffic crosses the front too. That closes
the structural blind spot motivating this change: traffic issued
inside Process::launch used to dial the backend's real port before any
external tap could exist, and no userspace relay can interpose a
connection between endpoints that already know each other's real
address (spec: front-proxy inversion, serving zingolib's pipeline
observatory).

The front is a transparent TCP relay with an observer registration
point. A front::FrontObserver registered through the new
ZebradConfig::rpc_front_observer or ZainodConfig::grpc_front_observer
fields is in place before the backend starts and receives one
front::ChunkEvent per relayed chunk (timestamp, connection id,
direction, byte count, payload); the default is passthrough. The relay
and the observer hook are built from std and tokio primitives already
in the dependency tree, and no manifest changed.

Each front relays on a dedicated thread driving a private
single-threaded tokio runtime. Relaying must not depend on the
launching runtime staying responsive: this crate's own wallet layer
blocks its runtime thread in synchronous subprocess waits while the
subprocess's traffic crosses the fronts, which deadlocked a
runtime-hosted relay in the devtool wallet suite and would do the same
to any consumer with a blocking wait.

The front machinery is defined against a minimal crate-internal
backend abstraction, backend::Backend, which adds log access and raw
listener endpoints as socket addresses — never bare ports — to the
Process lifecycle. A future container backend whose endpoints are
published host mappings could implement it without changing a line of
the front layer; processes remain the only implementors, and the trait
stays crate-internal because exporting raw endpoints would reopen the
hole the fronts close.

Empirical port-0 findings reshape the allocator. zebrad 6.0.0-rc.0
binds every listener on port 0 and logs each assigned address, so
unpinned zebrad listeners now bind port 0 and the harness discovers
the raw addresses from the launch log — a log contract pinned by unit
tests, with discovery failures surfacing as the new
LaunchError::ListenerEndpointsUndiscovered. pick_unused_port is no
longer used for any zebrad listener. zainod 0.4.3-ironwood.1 binds
port 0 happily but never logs the assigned address, so its raw gRPC
listener retains the picked-port allocator. The collision-retry
machinery remains as the backstop for explicitly pinned ports, the
only remaining collision surface.

New tests pin the design's two claims. The decisive one,
observer_on_zebrad_front_captures_the_launch_mine, registers an
observer before launch and asserts the launch-mine's submitblock call
appears in its record — the previously unobservable window, now
observed. concurrent_zebrad_launches_are_collision_free launches six
zebrads concurrently and asserts every published endpoint is distinct,
the :0 guarantee. The existing launch, convergence, from_parts,
collision-recovery, and devtool wallet suites pass behaviorally
unchanged, now transiting fronts invisibly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The ci nextest profile hid binary-requiring tests by test-name
substring, which failed in both directions. The new from_parts
integration test contains no marker substring, so CI ran it and it
panicked spawning zebrad; meanwhile fifteen pure unit tests that
merely mention a binary in their names were silently skipped.

The profile now excludes the two binaries whose tests spawn real
zcashd/zebrad/zainod/lightwalletd/devtool processes,
zcash_local_net::integration and regtest-launcher::e2e, and the
redundant -E exclusion leaves the test.yaml run command. The ci
profile runs 71 tests locally, all passing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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