feat!: harden wire-boundary enums with #[non_exhaustive] + pin tests#66
Merged
feat!: harden wire-boundary enums with #[non_exhaustive] + pin tests#66
Conversation
Addresses the "cheap win" from the freenet-core#3860 hardening discussion: future variants on these five wire-boundary enums should NOT require a downstream source break, and a refactor that reorders variants should fail loudly at test time rather than silently rewriting the wire format for deployed contracts and delegates. Adds `#[non_exhaustive]` to: - `delegate_interface::InboundDelegateMsg` — pair of the already- `non_exhaustive` `OutboundDelegateMsg`. - `contract_interface::update::UpdateData` — contract update wire type. - `delegate_interface::DelegateError` — returned from delegate process(). - `contract_interface::error::ContractError` — contract validation errors. - `versioning::APIVersion` — historical panic site (fixed in 0.1.8, this adds belt-and-braces protection). All five are source-level breaking for downstream code that matches exhaustively; they are NOT wire-format breaking. `#[non_exhaustive]` is a source attribute only. Bincode discriminants, serde impls, and byte layouts are unchanged, so deployed contract/delegate WASM compiled against any earlier 0.x stdlib continues to deserialize identically. Adds wire-format pin tests mirroring the style introduced for `MessageOrigin` in 0.5.0: - `inbound_delegate_msg_wire_format_is_stable` — asserts `ApplicationMessage(..)` stays at variant tag 0 and round-trips. - `update_data_wire_format_is_stable` — asserts `State(..)` at tag 0 and `Delta(..)` at tag 1 and round-trips both. These pin tests are the regression guard for refactors that innocently reorder variants: without them, a renamed/reordered variant would silently remap the wire tags and break every deployed consumer without a compile error. Bumps stdlib 0.5.0 → 0.6.0 (minor breaking per 0.x semver). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
sanity
added a commit
to freenet/freenet-core
that referenced
this pull request
Apr 14, 2026
…stive arms stdlib 0.6.0 (freenet/freenet-stdlib#66) adds `#[non_exhaustive]` to five wire-boundary enums (`InboundDelegateMsg`, `UpdateData`, `DelegateError`, `ContractError`, `APIVersion`) and pins their bincode wire formats with new test coverage. The bump is source-level breaking but wire-compatible: deployed contracts and delegates compiled against any earlier 0.x stdlib continue to deserialize identically because bincode discriminants for existing variants are byte-identical (now permanently locked by the new pin tests). This commit fixes every freenet-core / fdev / test-fixture match site that the compiler flagged after the bump: - `wasm_runtime/delegate.rs` (2 sites): `inbound_msg_name` lookup gets an "Unknown" sentinel; the `for msg in inbound` loop forwards future variants through the same generic exec path so a delegate built against a newer stdlib can still receive them. Annotated `#[allow(clippy::wildcard_enum_match_arm)]` with a comment because expanding the wildcard into `pat | _` would defeat the non_exhaustive safety net. - `client_events.rs` (1 site): UPDATE conversion to 'static rejects unknown UpdateData variants with an explicit `Error::Node` (forwards a "rebuild freenet-core against the stdlib emitting this variant" diagnostic to the client) rather than silently dropping the payload. - `contract.rs` (2 sites): delegate UPDATE path falls into the existing "unsupported variant" warn+reject branch; the `ContractHandlerEvent:: UpdateQuery` path stays `unreachable!()` because the producer at the edge is now responsible for rejecting unknowns. - `node/request_router.rs` (1 site): hash dedup uses sentinel discriminant 255 for unknown variants so dedup keys stay distinct. - `operations/update.rs` (1 site): state-extraction returns `None` for unknowns (treated as "no state to extract"). - `contract/executor/mock_wasm_runtime.rs` (1 site): mock-only path ignores unknowns for the merge. - `tests/test-contract-mock-aligned/src/lib.rs` (1 site): test fixture ignores unknowns. - `crates/fdev/src/commands.rs` (2 sites): `extract_update_bytes` returns an empty slice for unknown variants; `describe_update_variant` returns "unknown". Test fixture lockfile bump: `tests/test-delegate-messaging/Cargo.toml` already pinned 0.5.0 (the published version at the time of the prior commit); now bumped to 0.6.0 to match. Adds `.claude/rules/git-workflow.md` guidance: stdlib-first release policy (don't use [patch.crates-io] git overrides for cross-repo coordination — publish stdlib first, then open the consumer PR) and a walk-through of the non_exhaustive bump pattern so the next person who adds a wire-boundary variant doesn't have to rediscover the cheap-win shape from scratch. Verified locally: full freenet-core lib test suite passes (2245 tests, 4 more than baseline — the `resolve_message_origin_tests` precedence suite from the prior commit), `cargo clippy --locked -- -D warnings` clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.
Problem
freenet-stdlib repeatedly hits source-level breakage cycles when wire-boundary enums grow new variants (most recently #65 / freenet/freenet-core#3860 for
MessageOrigin, the v0.2.11 streaming incident forHostResponse, etc.). Each break forces every downstream consumer that pattern-matches exhaustively to add a wildcard arm — a coordination tax that this PR pays once for five enums at once so future variants are non-events at the source level.A separate failure mode: a refactor that innocently reorders enum variants would silently rewrite the bincode wire tags and break every deployed consumer without a compile error. There is no regression guard for that today on any enum except
MessageOrigin(added in 0.5.0).Approach
Add
#[non_exhaustive]to the five wire-boundary enums that don't already have it:delegate_interface::InboundDelegateMsg(companion toOutboundDelegateMsg)contract_interface::update::UpdateDatadelegate_interface::DelegateErrorcontract_interface::error::ContractErrorversioning::APIVersionAdd wire-format pin tests for two of them mirroring the
MessageOriginpattern from 0.5.0:inbound_delegate_msg_wire_format_is_stable— assertsApplicationMessage(..)stays at variant tag 0 and round-tripsupdate_data_wire_format_is_stable— assertsState(..)at tag 0 andDelta(..)at tag 1 and round-trips bothCompatibility
Source-level breaking, wire-compatible:
#[non_exhaustive]is a source attribute only. No effect on bincode discriminants, serde impls, byte layout, or wire format.Bumps 0.5.0 → 0.6.0 (minor-breaking per 0.x semver).
Testing
webapp_origin_wire_format_is_stableanddelegate_origin_wire_format_is_stablepin tests continue to pass.inbound_delegate_msg_wire_format_is_stable,update_data_wire_format_is_stable.cargo testfull sweep clean.cargo clippy --all-targets --all-featuresclean (only the pre-existing__frnt__fill_buffersnake-case warning unrelated to this PR).Release coordination
This unblocks the second hardening pass on freenet/freenet-core#3865 (which folds the dep bump and any required wildcard arms into the same PR rollout, per the "fold-into-#3865" decision).
[AI-assisted - Claude]