Plan 002: Stop compiling src/event_de/ into default and downstream builds
Executor instructions: Follow this plan step by step. Run every
verification command and confirm the expected result before moving to the
next step. If anything in the "STOP conditions" section occurs, stop and
report — do not improvise. When done, update the status row for this plan
in plans/README.md — unless a reviewer dispatched you and told you they
maintain the index.
Drift check (run first): git diff --stat f252b15..HEAD -- src/lib.rs src/event_de/
NOTE: this plan was written against commit f252b15 plus uncommitted
working-tree changes that introduced the bench-internals feature. If the
"Current state" excerpts below do not match the live files, treat it as a
STOP condition.
Status
- Priority: P2
- Effort: M
- Risk: LOW
- Depends on: plans/001-ci-cover-bench-internals-feature.md
- Category: tech-debt
- Planned at: commit
f252b15 + uncommitted changes, 2026-06-10
Why this matters
src/event_de/ is ~3,657 lines (~2,670 excluding its unit tests) of
event-backed serde deserializer that is a benchmark-only experiment. Its sole
consumer, the __unstable_event_serde module in src/lib.rs, is gated behind
the non-default bench-internals feature — but mod event_de; itself is
declared unconditionally, so every downstream user of saneyaml compiles all of
it for nothing, under a blanket #![allow(dead_code)] that also suppresses
genuine-rot warnings inside the module. For a crate that markets a minimal
trusted surface (runtime deps locked to exactly ryu + serde by CI tests),
shipping ~2.7k lines of unreachable compiled code is both a compile-time tax
and a trust-surface inconsistency. Gating the module to
cfg(any(test, feature = "bench-internals")) removes it from user builds while
keeping its 50 unit tests running in every default cargo test.
Current state
src/lib.rs:27-38 — module declarations; event_de is the only internal
module without a cfg gate that has a feature-gated consumer:
mod ast;
mod de;
mod emit;
mod error;
mod event_de;
mod key_identity;
#[cfg(feature = "lossless")]
pub mod lossless;
mod parse;
mod schema;
mod ser;
mod yaml11;
src/lib.rs:48-87 — the only consumer of crate::event_de, already gated:
#[cfg(feature = "bench-internals")]
#[doc(hidden)]
pub mod __unstable_event_serde {
...
crate::event_de::from_documents_str_with_options(input, options)
...
crate::event_de::document_iter_reader_with_options(reader, options)?.collect()
}
Verified: grep -rn "event_de" src/ | grep -v "^src/event_de" returns only
src/lib.rs lines 31, 66, 85. No other module references it.
src/event_de/mod.rs:1-3 — the blanket lint suppression and stale comment:
#![allow(dead_code)]
// Compiled work-in-progress: this module is exercised by unit tests before it is
// wired into public Serde entrypoints.
src/event_de/mod.rs:196-197 — unit tests are a cfg(test) submodule:
Baseline: cargo test --locked --lib event_de → 50 passed; 0 failed.
- Helpers
event_de imports from other modules (merge_policy_for_schema,
parse_scalar_with_schema, schema_for_directives in src/parse.rs;
check_duplicate_with_tracker_at_depth_limit in src/key_identity.rs;
utf8_error_span in src/error.rs) are all also used by parse.rs/de.rs
themselves, so gating event_de does not orphan them. (Verified at planning
time: e.g. parse.rs:553/580/948 call merge_policy_for_schema.)
- CI gates that must stay green:
cargo clippy --locked --all-targets -- -D warnings, scripts/check-feature-clippy.sh (which after plan 001 includes
--features bench-internals --all-targets), cargo test --locked,
RUSTDOCFLAGS='-D missing_docs' cargo doc --locked --no-deps, and a
cargo public-api diff job. event_de is private and __unstable_event_serde
is #[doc(hidden)], so neither docs nor public API should change.
Commands you will need
| Purpose |
Command |
Expected on success |
| Tests (default) |
cargo test --locked |
exit 0 |
| event_de unit tests |
cargo test --locked --lib event_de |
50 passed; 0 failed |
| Clippy (default) |
cargo clippy --locked --all-targets -- -D warnings |
exit 0 |
| Feature matrix |
scripts/check-feature-clippy.sh |
exit 0 |
| Feature tests |
cargo test --locked --features bench-internals --lib |
exit 0 |
| Docs gate |
RUSTDOCFLAGS='-D missing_docs' cargo doc --locked --no-deps |
exit 0 |
Scope
In scope (the only files you should modify):
src/lib.rs (one attribute on the mod event_de; declaration)
src/event_de/mod.rs (lint attribute + the stale comment at the top)
Out of scope (do NOT touch, even though they look related):
- Deleting or refactoring anything inside
src/event_de/ — the module is kept
intentionally as the benchmark comparison path (see
docs/EVENT_BACKED_SERDE.md); this plan only changes when it compiles.
src/parse.rs, src/de.rs, src/key_identity.rs, src/error.rs — shared
helpers stay as they are; they have non-event_de users.
Cargo.toml, docs/**, examples/**, tests/**,
scripts/check-feature-clippy.sh — already consistent with this change.
Git workflow
- Branch:
advisor/002-gate-event-de
- One commit. Message style: short imperative subject matching repo history.
Suggested: Gate event_de module behind tests and bench-internals.
- Author/committer must be
jskoiz <20649937+jskoiz@users.noreply.github.com>.
Do not add any co-author or tool-attribution trailers.
- Do NOT push or open a PR unless the operator instructed it.
Steps
Step 1: Gate the module declaration
In src/lib.rs, change:
to:
#[cfg(any(test, feature = "bench-internals"))]
mod event_de;
any(test, ...) keeps the module compiled for unit-test builds (so the 50
tests in src/event_de/tests.rs keep running under plain cargo test) while
removing it from cargo build/downstream compilations, where only the
bench-internals feature pulls it back in.
Verify: cargo build --locked → exit 0.
Verify: cargo test --locked --lib event_de → 50 passed; 0 failed.
Step 2: Replace the blanket dead_code allow with an accurate, scoped one
In src/event_de/mod.rs, first try deleting line 1 (#![allow(dead_code)])
and updating the comment to describe the new reality, e.g.:
// Benchmark-only event-backed Serde path. Compiled for unit tests and under
// the `bench-internals` feature; not part of default or downstream builds.
Then run the two lint gates below. Expected outcome: under
cfg(test)-without-feature builds, items used only by the feature-gated
wrapper (from_documents_str_with_options,
document_iter_reader_with_options, and anything reachable only from them)
may now warn as dead code.
- If there are no warnings: done, keep the allow removed.
- If there are warnings: restore a scoped suppression instead of the blanket
one — #![cfg_attr(not(feature = "bench-internals"), allow(dead_code))]
with a one-line comment saying the public entrypoints are only referenced by
the feature-gated wrapper in lib.rs. This keeps full dead-code linting
whenever the feature is on (which CI now compiles, per plan 001).
Verify: cargo clippy --locked --all-targets -- -D warnings → exit 0.
Verify: scripts/check-feature-clippy.sh → exit 0 (this is where the
feature-enabled clippy from plan 001 proves the module is warning-free when
fully wired).
Step 3: Full gate sweep
Verify: cargo test --locked → exit 0.
Verify: cargo test --locked --features bench-internals --lib → exit 0.
Verify: RUSTDOCFLAGS='-D missing_docs' cargo doc --locked --no-deps → exit 0.
Test plan
No new tests. The regression bar is the existing suite:
cargo test --locked --lib event_de must still report exactly
50 passed; 0 failed — proof the cfg gate did not silently drop the module's
unit tests (the most likely failure mode of this change).
scripts/check-feature-clippy.sh must pass — proof the feature-enabled
combination (lib + examples) still compiles.
Done criteria
Machine-checkable. ALL must hold:
STOP conditions
Stop and report back (do not improvise) if:
- Plan 001 is not DONE (check
plans/README.md) — without feature-enabled CI
coverage this change would make event_de rot invisible outside cargo test.
grep -rn "event_de" src/ | grep -v "^src/event_de" | grep -v "lib.rs"
returns anything — a new consumer of the module appeared since planning, and
gating would break it.
- After Step 1,
cargo test --locked --lib event_de reports fewer than 50
tests — the gate is wrong (e.g. tests no longer compiled).
- Step 2 produces dead-code warnings in modules other than
src/event_de/ — that means something outside the module depended on it in
a way planning missed.
- Any verification fails twice after a reasonable fix attempt.
Maintenance notes
- If
event_de is ever promoted to a real public API (the original intent per
its old comment), the cfg gate and the scoped allow are the two things to
remove; docs/EVENT_BACKED_SERDE.md describes the promotion criteria.
- Reviewer should scrutinize: that the gate is
any(test, feature = ...) and
not just the feature (which would silently stop running 50 unit tests in
default CI), and that no pub use of event_de items leaked into ungated code.
- Deferred (deliberately): deleting
event_de outright. It is the in-repo
benchmark comparison path against serde-saphyr and is documented; removal is
a maintainer decision, not tech-debt cleanup.
Plan 002: Stop compiling
src/event_de/into default and downstream buildsStatus
f252b15+ uncommitted changes, 2026-06-10Why this matters
src/event_de/is ~3,657 lines (~2,670 excluding its unit tests) ofevent-backed serde deserializer that is a benchmark-only experiment. Its sole
consumer, the
__unstable_event_serdemodule insrc/lib.rs, is gated behindthe non-default
bench-internalsfeature — butmod event_de;itself isdeclared unconditionally, so every downstream user of saneyaml compiles all of
it for nothing, under a blanket
#![allow(dead_code)]that also suppressesgenuine-rot warnings inside the module. For a crate that markets a minimal
trusted surface (runtime deps locked to exactly
ryu+serdeby CI tests),shipping ~2.7k lines of unreachable compiled code is both a compile-time tax
and a trust-surface inconsistency. Gating the module to
cfg(any(test, feature = "bench-internals"))removes it from user builds whilekeeping its 50 unit tests running in every default
cargo test.Current state
src/lib.rs:27-38— module declarations;event_deis the only internalmodule without a cfg gate that has a feature-gated consumer:
src/lib.rs:48-87— the only consumer ofcrate::event_de, already gated:grep -rn "event_de" src/ | grep -v "^src/event_de"returns onlysrc/lib.rslines 31, 66, 85. No other module references it.src/event_de/mod.rs:1-3— the blanket lint suppression and stale comment:src/event_de/mod.rs:196-197— unit tests are a cfg(test) submodule:cargo test --locked --lib event_de→50 passed; 0 failed.event_deimports from other modules (merge_policy_for_schema,parse_scalar_with_schema,schema_for_directivesinsrc/parse.rs;check_duplicate_with_tracker_at_depth_limitinsrc/key_identity.rs;utf8_error_spaninsrc/error.rs) are all also used byparse.rs/de.rsthemselves, so gating
event_dedoes not orphan them. (Verified at planningtime: e.g.
parse.rs:553/580/948callmerge_policy_for_schema.)cargo clippy --locked --all-targets -- -D warnings,scripts/check-feature-clippy.sh(which after plan 001 includes--features bench-internals --all-targets),cargo test --locked,RUSTDOCFLAGS='-D missing_docs' cargo doc --locked --no-deps, and acargo public-apidiff job.event_deis private and__unstable_event_serdeis
#[doc(hidden)], so neither docs nor public API should change.Commands you will need
cargo test --lockedcargo test --locked --lib event_de50 passed; 0 failedcargo clippy --locked --all-targets -- -D warningsscripts/check-feature-clippy.shcargo test --locked --features bench-internals --libRUSTDOCFLAGS='-D missing_docs' cargo doc --locked --no-depsScope
In scope (the only files you should modify):
src/lib.rs(one attribute on themod event_de;declaration)src/event_de/mod.rs(lint attribute + the stale comment at the top)Out of scope (do NOT touch, even though they look related):
src/event_de/— the module is keptintentionally as the benchmark comparison path (see
docs/EVENT_BACKED_SERDE.md); this plan only changes when it compiles.src/parse.rs,src/de.rs,src/key_identity.rs,src/error.rs— sharedhelpers stay as they are; they have non-event_de users.
Cargo.toml,docs/**,examples/**,tests/**,scripts/check-feature-clippy.sh— already consistent with this change.Git workflow
advisor/002-gate-event-deSuggested:
Gate event_de module behind tests and bench-internals.jskoiz <20649937+jskoiz@users.noreply.github.com>.Do not add any co-author or tool-attribution trailers.
Steps
Step 1: Gate the module declaration
In
src/lib.rs, change:to:
any(test, ...)keeps the module compiled for unit-test builds (so the 50tests in
src/event_de/tests.rskeep running under plaincargo test) whileremoving it from
cargo build/downstream compilations, where only thebench-internalsfeature pulls it back in.Verify:
cargo build --locked→ exit 0.Verify:
cargo test --locked --lib event_de→50 passed; 0 failed.Step 2: Replace the blanket dead_code allow with an accurate, scoped one
In
src/event_de/mod.rs, first try deleting line 1 (#![allow(dead_code)])and updating the comment to describe the new reality, e.g.:
Then run the two lint gates below. Expected outcome: under
cfg(test)-without-feature builds, items used only by the feature-gatedwrapper (
from_documents_str_with_options,document_iter_reader_with_options, and anything reachable only from them)may now warn as dead code.
one —
#![cfg_attr(not(feature = "bench-internals"), allow(dead_code))]with a one-line comment saying the public entrypoints are only referenced by
the feature-gated wrapper in
lib.rs. This keeps full dead-code lintingwhenever the feature is on (which CI now compiles, per plan 001).
Verify:
cargo clippy --locked --all-targets -- -D warnings→ exit 0.Verify:
scripts/check-feature-clippy.sh→ exit 0 (this is where thefeature-enabled clippy from plan 001 proves the module is warning-free when
fully wired).
Step 3: Full gate sweep
Verify:
cargo test --locked→ exit 0.Verify:
cargo test --locked --features bench-internals --lib→ exit 0.Verify:
RUSTDOCFLAGS='-D missing_docs' cargo doc --locked --no-deps→ exit 0.Test plan
No new tests. The regression bar is the existing suite:
cargo test --locked --lib event_demust still report exactly50 passed; 0 failed— proof the cfg gate did not silently drop the module'sunit tests (the most likely failure mode of this change).
scripts/check-feature-clippy.shmust pass — proof the feature-enabledcombination (lib + examples) still compiles.
Done criteria
Machine-checkable. ALL must hold:
grep -n 'cfg(any(test, feature = "bench-internals"))' src/lib.rs→ one match, on the line abovemod event_de;grep -c '#!\[allow(dead_code)\]' src/event_de/mod.rs→0(either removed or replaced by the scopedcfg_attrform)cargo test --lockedexits 0cargo test --locked --lib event_dereports50 passedcargo clippy --locked --all-targets -- -D warningsexits 0scripts/check-feature-clippy.shexits 0git status --porcelainshows onlysrc/lib.rsandsrc/event_de/mod.rsmodifiedplans/README.mdstatus row updatedSTOP conditions
Stop and report back (do not improvise) if:
plans/README.md) — without feature-enabled CIcoverage this change would make
event_derot invisible outsidecargo test.grep -rn "event_de" src/ | grep -v "^src/event_de" | grep -v "lib.rs"returns anything — a new consumer of the module appeared since planning, and
gating would break it.
cargo test --locked --lib event_dereports fewer than 50tests — the gate is wrong (e.g. tests no longer compiled).
src/event_de/— that means something outside the module depended on it ina way planning missed.
Maintenance notes
event_deis ever promoted to a real public API (the original intent perits old comment), the
cfggate and the scoped allow are the two things toremove;
docs/EVENT_BACKED_SERDE.mddescribes the promotion criteria.any(test, feature = ...)andnot just the feature (which would silently stop running 50 unit tests in
default CI), and that no
pub useof event_de items leaked into ungated code.event_deoutright. It is the in-repobenchmark comparison path against serde-saphyr and is documented; removal is
a maintainer decision, not tech-debt cleanup.