Contributing
Contributions are welcome; see the contributing guide.
Building and testing
disrobe builds with a single stable Rust 1.95+ toolchain:
cargo build --release
cargo test -p <crate> --features <the crate's test features> # test a single crate
The JVM decompiler can be memory-intensive on adversarial input. Prefer per-crate test runs over a bare workspace-wide
cargo test --workspacewhen iterating locally.
Name the features a crate hides its tests behind
Some crates keep whole modules behind a feature that is off by default. Every pass crate keeps its
chain detector behind a non-default chain feature so the slim build can leave the chain runtime
out. The file starts with #![cfg(feature = "chain")], so a per-crate run that names no feature
builds none of the chain detector's tests and still prints a passing result for the tests it did
build. Name the feature to run them:
cargo test -p disrobe-pass-lua --features chain
cargo test -p disrobe-cli --no-default-features --features chain --test auto_dalvik_feature_gate
cargo test -p disrobe-pass-mobile --features native-image --test flutter_engine_fallback_identity
cargo test -p disrobe-pass-wasm-deob --features chain,sandbox --test linear_memory_local_offset
The second form covers a refusal that exists only when chain is enabled and jvm is disabled.
The Flutter engine identity test requires native-image for ELF parsing. The WebAssembly
differential requires sandbox for Wasmtime execution and chain for the registered-pass
assertion.
cargo run -p xtask -- health enforces this. It reads every crate's default feature set, finds
every test-bearing file the default set removes, and fails when a crate hides tests that no entry in
HIDDEN_TEST_SURFACE in xtask/src/feature_gated_tests.rs declares. It also reads every per-crate
test command written in the README, in docs/src, and in the workflows, and fails when one of them
names a test target the command's own feature set compiles away, or names no feature for a crate
that hides tests. The failure names the crate and prints the command to use instead.
The quality bar
Every commit on main must pass the workspace clippy gate with zero warnings:
cargo clippy --all-targets -- -D warnings -W unreachable_pub -W missing_debug_implementations -W unused
cargo fmt -p <crate> -- --check
The workspace lints are strict by design: unwrap_used is denied, expect_used is treated as a defect in production paths, and todo! and unimplemented! are denied. New code is fully type-annotated and self-documenting; the codebase carries durable context in dedicated docs rather than inline comments. Unsafe code is restricted to audited boundary code and does not belong in ordinary parsing or recovery paths.
README graphs
The dark-theme benchmark and architecture SVGs in the README are generated, not drawn by hand. The data lives in xtask/data/*.json (every plotted value cites its source gate or harness inline), and xtask renders deterministic SVGs into docs/assets/:
cargo run -p xtask -- graphs # regenerate docs/assets/*.svg
cargo run -p xtask -- graphs --check # fail if committed SVGs are stale (CI runs this)
After changing a number in xtask/data/, rerun graphs and commit the regenerated SVGs; the graphs CI job rejects any drift. Numbers come only from a committed test gate or a local measurement harness, never from the tool grading its own output, and no competitor recovery percentage is plotted.
Docs and the wiki
These pages under docs/src are the single source of truth. The GitHub wiki is generated from them by scripts/wiki_sync.py and the wiki-sync workflow, which runs on every push to main that touches docs/. Do not edit the wiki directly; it is overwritten on the next sync. Edit the page here, then preview the generated wiki locally:
python scripts/wiki_sync.py --out ./.wiki-build # build the wiki tree
python scripts/wiki_sync.py --check --out ./.wiki-build # fail on drift
Adding a pass
A new ecosystem pass is a new disrobe-pass-<name> crate that:
- Implements the shared
Passtrait, declaring its required and produced capabilities and its rung transition. - Climbs the five-rung IR ladder rather than jumping rungs.
- Ships a
pass_run_envelope_roundtriptest and at least one real-fixture integration test incrates/disrobe-cli/tests/. - Wires its standardized emits, returning explicit
applicable: falsestubs for emits it cannot produce.
Every capability claim must be backed by a fixture in corpus/ and a passing test; nothing aspirational ships as done. Fixtures are baked locally from known-good inputs by corpus/generate.{sh,ps1}; copyrighted third-party obfuscated bytecode is never committed to the public corpus.
No fabrication
A decode that only passes against synthetic, self-generated fixtures is not a feature. Per-pass work is verified against a real corpus and the upstream format spec. Partial recovery carries a confidence tier; detect-only is stated as detect-only. If you are not sure a capability works against real-world input, say so in the PR.
Reporting bugs
Generate an environment report to attach to an issue:
disrobe bug-report --out report.md
disrobe bug-report --out - # write to stdout
For security issues, do not open a public issue; use the private advisory channel. See Security.