
[](https://github.com/sjkim1127/Fission/actions/workflows/ci.yml)
[](https://www.rust-lang.org/)
[](https://www.gnu.org/licenses/agpl-3.0.html)
Fission
Fission is a Rust-native reverse-engineering and binary decompilation workspace. It is built around a Fission-owned intermediate-representation pipeline, with Ghidra-style Sleigh semantics feeding Rust-owned NIR, HIR, structuring, rendering, automation, and quality gates.
The project goal is not only to decode instructions. The goal is to produce decompiler output that is mechanically traceable, semantically defensible, and eventually readable enough to be useful in day-to-day reverse engineering.
This README is intentionally long. It is a practical orientation document for contributors, operators, and future agents working in the repository. The shorter source-of-truth references remain in docs/, AGENTS.md, and the crate-local AGENTS.md files.
Table of Contents
Project Status
- Fission is a research-heavy Rust workspace for binary loading, instruction lifting, decompiler IR, structuring, automation, and product surfaces.
- The canonical semantic layer is
fission-pcode, especially the NIR/HIR and structuring modules.
- Application orchestration belongs in
fission-decompiler and user-facing command routing belongs in fission-cli.
- Binary parsing and provenance belong in
fission-loader.
- Static facts and native preparation services belong in
fission-static.
- Sleigh decode and raw p-code lift behavior belong in
fission-sleigh.
- Quality reports and automation lanes belong in
fission-automation.
- Vendor trees are references only. Production paths must not depend on them at runtime or build time.
- Checked-in utility resources under
utils/ must be accessed through the existing resource-root mechanisms, not hardcoded absolute paths.
- Current day-to-day quality work prioritizes x86 and x86-64 decompilation correctness and readable pseudocode.
Fission should be treated as a system with strict ownership boundaries. If a semantic issue is discovered in the final pseudocode, the fix should land where the behavior is owned, not in the renderer or README-level presentation.
Quick Start
Clone
git clone https://github.com/sjkim1127/Fission.git
cd Fission
Install Requirements
- Rust 1.85 or newer.
cargo-nextest for the preferred local test runner.
- A modern C toolchain if local fixtures or corpus binaries need to be rebuilt.
rustup update
cargo install cargo-nextest --locked
Pull Required Runtime Assets
The Rust workspace can build without every large resource, but real decompilation needs Sleigh specifications and some quality lanes need signature or support data. utils/ is not checked into git; pull the bundled release asset instead:
mkdir -p utils
curl -L --fail --show-error \
"https://github.com/sjkim1127/Fission/releases/download/assets-v1/fission-utils.tar.gz" \
-o /tmp/fission-utils.tar.gz
tar -xzf /tmp/fission-utils.tar.gz -C utils --strip-components=1
Build the CLI
cargo build -p fission-cli --release
./target/release/fission_cli --help
For local iteration, prefer --profile quick-release over --release:
[profile.release] uses fat LTO + codegen-units = 1 for shipping-quality
runtime perf, which serializes codegen/linking and dominates rebuild time.
[profile.quick-release] (workspace Cargo.toml) drops those two knobs but
keeps opt-level = 3, so decomp-speed/output checks stay representative —
measured ~2.9x faster on a touch-one-core-crate rebuild (44s → 15s), with
byte-identical output on the real-binary regression set and ~9% slower
runtime on the heaviest measured function. Binaries land in
target/quick-release/. Use plain --release for anything going into a
benchmark run, a release build, or a perf measurement that needs to match
production codegen.
cargo build -p fission-cli --profile quick-release
./target/quick-release/fission_cli --help
Run Common Checks
cargo nextest run -p fission-pcode
cargo check -p fission-pcode
cargo check -p fission-decompiler
cargo check -p fission-automation
Repository Tour
The repository is organized as a Cargo workspace plus documentation, resource bundles, scripts, CI workflows, and reference-only vendor trees.
| Path |
Purpose |
crates/ |
Rust workspace crates. |
docs/ |
Architecture, CLI, evaluation, release, versioning, onboarding, roadmap, and ADR documents. |
utils/ |
Checked-in resource bundle material such as Sleigh specs and Ghidra data manifests. |
vendor/ |
Reference-only third-party source trees and datasets. |
scripts/ |
Local helper scripts for testing, corpus work, and maintenance. |
.github/workflows/ |
CI, reusable workflow jobs, release tag workflow, fuzzing, and heavy gates. |
image/ |
Project logo and icon assets. |
target/ |
Local Cargo build output; not source. |
Primary Documentation
AGENTS.md defines repository-level engineering rules and ownership boundaries.
PROJECT.md tracks the active normalize pass-pipeline migration: current per-stage status, the two pass-framework tracks, and recurring pitfalls found while migrating.
docs/PROJECT_MAP.md is the quick navigation map.
docs/architecture/ARCHITECTURE.md is the architecture source of truth.
docs/CLI.md documents command-line behavior.
docs/EVALUATION.md describes evaluation posture.
docs/QUALITY_METRICS.md documents metric contracts.
docs/RELEASE.md and docs/VERSIONING.md describe release discipline.
docs/adr/ records architectural decisions.
docs/onboarding/ contains practical first-task guides.
docs/roadmap/RUST_DECOMPILER_ROADMAP.md tracks long-term direction.
Architecture in One Page
The shortest useful architecture summary is: binary bytes are loaded by fission-loader, instruction semantics are lifted through fission-sleigh, canonical IR and decompiler semantics are owned by fission-pcode, orchestration is performed by fission-decompiler, and user or automation surfaces consume those results without inventing new semantic policy.
Binary bytes
-> fission-loader
-> fission-static facts and provenance
-> fission-sleigh decode and p-code lift
-> fission-pcode NIR
-> fission-pcode HIR
-> structuring, cleanup, rendering
-> fission-decompiler result contracts
-> CLI, TUI, GUI, automation, reports
fission-pcode is the semantic owner.
fission-pcode::nir::structuring is the structuring owner.
fission-decompiler is the orchestration owner.
fission-static is the facts and preparation owner.
fission-loader is the binary-format owner.
- Printer surfaces are consume-only.
- Benchmark and report code must project canonical telemetry instead of inventing parallel counters.
Core Pipeline
- Load bytes from a supported binary format.
- Classify format, architecture, sections, symbols, imports, exports, and executable regions.
- Attach provenance and identity hints without changing IR semantics.
- Prepare static facts needed by the decompiler.
- Decode instructions through Sleigh language definitions.
- Emit raw p-code in a form that can be compared against parity expectations.
- Lower p-code into Fission NIR.
- Normalize NIR while preserving semantics.
- Recover type, calling convention, stack, pointer, and data-flow hints when evidence supports them.
- Build HIR as a human-readable representation.
- Apply structuring passes using CFG, dominance, post-dominance, SCC, and proof evidence.
- Render pseudocode without inventing missing semantics.
- Report telemetry through canonical counters.
- Use automation lanes to compare quality over time.
Crate Guide
| Crate |
Role |
Editing rule |
fission-script |
Script-facing helpers and experiments for automation or user scripting. |
Keep behavior inside its owner; do not leak semantic policy to callers. |
fission-automation |
Quality lanes, reports, summaries, and go/stop automation signals. |
Keep behavior inside its owner; do not leak semantic policy to callers. |
fission-core |
Shared core types, path configuration, resource roots, and utilities. |
Keep behavior inside its owner; do not leak semantic policy to callers. |
fission-loader |
Binary loading, sections, symbols, relocations, virtual types, and identity reports. |
Keep behavior inside its owner; do not leak semantic policy to callers. |
fission-pcode |
Canonical IR, NIR, HIR, structuring, CFG analysis, type hints, and printer. |
Keep behavior inside its owner; do not leak semantic policy to callers. |
fission-decompiler |
Decompilation orchestration, request/result contracts, Rust-Sleigh bridge, and render routing. |
Keep behavior inside its owner; do not leak semantic policy to callers. |
fission-signatures |
Signature datasets and lookup logic. |
Keep behavior inside its owner; do not leak semantic policy to callers. |
fission-static |
Static analysis facts, native preparation, discovery, xrefs, patches, and strings. |
Keep behavior inside its owner; do not leak semantic policy to callers. |
fission-dynamic |
Dynamic-analysis support surfaces. |
Keep behavior inside its owner; do not leak semantic policy to callers. |
fission-ttd |
Time-travel and trace-adjacent support. |
Keep behavior inside its owner; do not leak semantic policy to callers. |
fission-emulator |
Pure-Rust P-Code execution engine, OS HLE, TTD recording, and taint-aware concolic execution. |
Keep behavior inside its owner; do not leak semantic policy to callers. |
fission-solver |
Pure-Rust SMT/constraint engine: SymExpr AST, Solver node registry, path condition management. |
Keep behavior inside its owner; do not leak semantic policy to callers. |
fission-plugin |
Plugin contracts, manager, loader, and runtime hooks. |
Keep behavior inside its owner; do not leak semantic policy to callers. |
fission-cli |
Command-line product surface. |
Keep behavior inside its owner; do not leak semantic policy to callers. |
fission-sleigh |
Sleigh decode and p-code lift runtime. |
Keep behavior inside its owner; do not leak semantic policy to callers. |
fission-ai |
AI-facing assistance surfaces and integration points. |
Keep behavior inside its owner; do not leak semantic policy to callers. |
fission-tui |
Terminal UI with ratatui-based interaction. |
Keep behavior inside its owner; do not leak semantic policy to callers. |
fission-dioxus |
Pure Rust desktop GUI surface. |
Keep behavior inside its owner; do not leak semantic policy to callers. |
fission-script
Script-facing helpers and experiments for automation or user scripting.
- Read
crates/fission-script/Cargo.toml before changing dependencies.
- Prefer local patterns already used inside
crates/fission-script.
- Do not create a parallel metric, loader rule, or semantic policy if another crate already owns it.
- Run the narrowest relevant test first, then broaden to crate checks.
fission-automation
Quality lanes, reports, summaries, and go/stop automation signals.
- Read
crates/fission-automation/Cargo.toml before changing dependencies.
- Prefer local patterns already used inside
crates/fission-automation.
- Do not create a parallel metric, loader rule, or semantic policy if another crate already owns it.
- Run the narrowest relevant test first, then broaden to crate checks.
fission-core
Shared core types, path configuration, resource roots, and utilities.
- Read
crates/fission-core/Cargo.toml before changing dependencies.
- Prefer local patterns already used inside
crates/fission-core.
- Do not create a parallel metric, loader rule, or semantic policy if another crate already owns it.
- Run the narrowest relevant test first, then broaden to crate checks.
fission-loader
Binary loading, sections, symbols, relocations, virtual types, and identity reports.
- Read
crates/fission-loader/Cargo.toml before changing dependencies.
- Prefer local patterns already used inside
crates/fission-loader.
- Do not create a parallel metric, loader rule, or semantic policy if another crate already owns it.
- Run the narrowest relevant test first, then broaden to crate checks.
fission-pcode
Canonical IR, NIR, HIR, structuring, CFG analysis, type hints, and printer.
- Read
crates/fission-pcode/Cargo.toml before changing dependencies.
- Prefer local patterns already used inside
crates/fission-pcode.
- Do not create a parallel metric, loader rule, or semantic policy if another crate already owns it.
- Run the narrowest relevant test first, then broaden to crate checks.
fission-decompiler
Decompilation orchestration, request/result contracts, Rust-Sleigh bridge, and render routing.
- Read
crates/fission-decompiler/Cargo.toml before changing dependencies.
- Prefer local patterns already used inside
crates/fission-decompiler.
- Do not create a parallel metric, loader rule, or semantic policy if another crate already owns it.
- Run the narrowest relevant test first, then broaden to crate checks.
fission-signatures
Signature datasets and lookup logic.
- Read
crates/fission-signatures/Cargo.toml before changing dependencies.
- Prefer local patterns already used inside
crates/fission-signatures.
- Do not create a parallel metric, loader rule, or semantic policy if another crate already owns it.
- Run the narrowest relevant test first, then broaden to crate checks.
fission-static
Static analysis facts, native preparation, discovery, xrefs, patches, and strings.
- Read
crates/fission-static/Cargo.toml before changing dependencies.
- Prefer local patterns already used inside
crates/fission-static.
- Do not create a parallel metric, loader rule, or semantic policy if another crate already owns it.
- Run the narrowest relevant test first, then broaden to crate checks.
fission-dynamic
Dynamic-analysis support surfaces.
- Read
crates/fission-dynamic/Cargo.toml before changing dependencies.
- Prefer local patterns already used inside
crates/fission-dynamic.
- Do not create a parallel metric, loader rule, or semantic policy if another crate already owns it.
- Run the narrowest relevant test first, then broaden to crate checks.
fission-ttd
Time-travel and trace-adjacent support.
- Read
crates/fission-ttd/Cargo.toml before changing dependencies.
- Prefer local patterns already used inside
crates/fission-ttd.
- Do not create a parallel metric, loader rule, or semantic policy if another crate already owns it.
- Run the narrowest relevant test first, then broaden to crate checks.
fission-plugin
Plugin contracts, manager, loader, and runtime hooks.
- Read
crates/fission-plugin/Cargo.toml before changing dependencies.
- Prefer local patterns already used inside
crates/fission-plugin.
- Do not create a parallel metric, loader rule, or semantic policy if another crate already owns it.
- Run the narrowest relevant test first, then broaden to crate checks.
fission-cli
Command-line product surface.
- Read
crates/fission-cli/Cargo.toml before changing dependencies.
- Prefer local patterns already used inside
crates/fission-cli.
- Do not create a parallel metric, loader rule, or semantic policy if another crate already owns it.
- Run the narrowest relevant test first, then broaden to crate checks.
fission-sleigh
Sleigh decode and p-code lift runtime.
- Read
crates/fission-sleigh/Cargo.toml before changing dependencies.
- Prefer local patterns already used inside
crates/fission-sleigh.
- Do not create a parallel metric, loader rule, or semantic policy if another crate already owns it.
- Run the narrowest relevant test first, then broaden to crate checks.
fission-ai
AI-facing assistance surfaces and integration points.
- Read
crates/fission-ai/Cargo.toml before changing dependencies.
- Prefer local patterns already used inside
crates/fission-ai.
- Do not create a parallel metric, loader rule, or semantic policy if another crate already owns it.
- Run the narrowest relevant test first, then broaden to crate checks.
fission-tui
Terminal UI with ratatui-based interaction.
- Read
crates/fission-tui/Cargo.toml before changing dependencies.
- Prefer local patterns already used inside
crates/fission-tui.
- Do not create a parallel metric, loader rule, or semantic policy if another crate already owns it.
- Run the narrowest relevant test first, then broaden to crate checks.
fission-dioxus
Pure Rust desktop GUI surface.
- Read
crates/fission-dioxus/Cargo.toml before changing dependencies.
- Prefer local patterns already used inside
crates/fission-dioxus.
- Do not create a parallel metric, loader rule, or semantic policy if another crate already owns it.
- Run the narrowest relevant test first, then broaden to crate checks.
CLI Guide
The CLI is the fastest product surface for validating loader and decompiler behavior locally. It should expose capabilities, not own semantic fixes.
fission_cli --help
fission_cli info <binary>
fission_cli list <binary>
fission_cli decomp <binary> --addr 0x1400010a0
fission_cli decomp <binary> --all --json
- Use
info for loader-level identity and provenance questions.
- Use
list for function discovery questions.
- Use
decomp for function-level pseudocode output.
- Use JSON output when comparing rows, caches, or automation artifacts.
- Keep command-line compatibility shims separate from semantic behavior.
Resource Bundle
Fission keeps large or operationally specific data out of git and ships it as a standalone utils/ resource bundle (fission-utils.tar.gz) attached to the assets-v1 GitHub Release.
utils/sleigh-specs/ contains Sleigh language resources and manifests.
utils/ghidra-data/ contains reference data and provenance notes.
utils/MANIFEST.md documents utility resources.
- Runtime code should use
PathConfig, PATHS, resource_roots, or existing loaders.
- Production code must not hardcode
/Users/sjkim1127/Fission/utils.
- CI fetches the bundle via
.github/actions/setup-utils (cached by assets_tag); .github/workflows/publish-utils-assets.yml regenerates it from a given ref.
mkdir -p utils
curl -L --fail --show-error \
"https://github.com/sjkim1127/Fission/releases/download/assets-v1/fission-utils.tar.gz" \
-o /tmp/fission-utils.tar.gz
tar -xzf /tmp/fission-utils.tar.gz -C utils --strip-components=1
fission_cli resources status
Decompiler Quality Loop
Quality work starts from a concrete function or row, not a vague aggregate. The loop below is the standard operating model for decompiler-quality changes.
- Anchor the exact row, binary, address, function name, behavior status, and quality scores.
- Record stdout, stderr, line count, byte count, and static feature gaps.
- Find the canonical owner: Sleigh, NIR, type recovery, structuring, cleanup, printer, benchmark, or automation.
- Add focused coverage for the invariant.
- Make the smallest invariant-based production change.
- Run the targeted test first.
- Run relevant crate-level tests and checks.
- Run the focused source-semantic row with stale caches disabled when available.
- Inspect artifacts, not only aggregate numbers.
- Run broader smoke or automation checks after a focused improvement.
- Report whether behavior mechanically changed and whether quality improved.
Quality Evidence Rules
- A passing synthetic test is necessary but not sufficient for a decompiler-quality claim.
- A changed pseudocode file is not automatically a quality improvement.
- Aggregate metrics must not hide row-level regressions.
- Raw p-code parity must be checked before NIR or HIR interpretation when the lift is suspect.
- Ghidra may be used as a cleanroom reference for behavior, not as a copied implementation source.
NIR and HIR Policy
NIR and HIR have different contracts.
| Layer |
Contract |
Consequence |
| NIR |
Semantically identical to the source behavior. |
Correctness and parity are the highest priorities. |
| HIR |
Human-readable pseudocode derived from correct semantics. |
Readability can be prioritized when the underlying semantics remain traceable. |
- NIR must not be prettified by losing behavior.
- HIR may remove unnecessary temporaries when that improves readability without hiding semantics.
- Printer code must not fake structure that the structuring owner did not prove.
- Type and data abstraction should be recovered at semantic layers, not by output-only substitution.
Sleigh Runtime
The Sleigh runtime is the decode and raw p-code lift path. It should execute .sla ConstructTpl semantics rather than accumulating manual opcode mappings.
- Keep
.sla execution as the success source.
- Raw p-code canaries are admission gates for lift changes.
- Validate x86 and x86-64 shared-token cases carefully.
- Preserve Ghidra-style materialization behavior where downstream parity depends on it.
- Do not grow legacy token cursor or compatibility-classifier debt without a clear retirement path.
Loader Policy
The loader owns binary format parsing and metadata provenance. It does not own decompiler repair.
detect stage in the loader pipeline.
probe/load-spec stage in the loader pipeline.
map stage in the loader pipeline.
symbols stage in the loader pipeline.
finalize stage in the loader pipeline.
- Known unsupported formats should fail closed with typed messages.
- Raw binary loading must not silently become a fallback for unknown bytes.
- Container formats must be classified before executable loading.
- Loader provenance is a public contract consumed by CLI and GUI surfaces.
- Function lists must use loader-owned views rather than surface-specific filtering rules.
Static Facts
Static facts help the decompiler, but they are not the final semantic owner. The fact layer should provide evidence, provenance, and analysis services.
- Xrefs and discovery facts.
- Patch and string facts.
- Native decompiler preparation.
- FactStore-style aggregation for decompilation contexts.
- Binary-derived helper services.
Dynamic Analysis and Emulator
Fission includes a pure-Rust dynamic analysis engine (fission-emulator) capable of executing arbitrary x86/x86-64 P-Code sequences produced by Sleigh, without requiring any external runtime like QEMU or Unicorn.
The emulator is a core pillar for future dynamic reasoning capabilities:
- Tracing and Differential Analysis — Execute binaries with a full instruction trace and diff the output against expected behavior.
- Time-Travel Debugging (TTD) — Record/replay execution with memory and register delta snapshots.
- Concolic Execution — Use TTD snapshots to fork execution at unexplored conditional branches.
- Symbolic Taint Tracking — Propagate symbolic expressions through P-Code operations and track tainted data flows from inputs (e.g.
stdin) through memory and registers.
Architecture
fission-emulator
├── core.rs — Emulator state, main run loop, register I/O, TTD hooks
├── pcode/
│ ├── eval.rs — P-Code opcode evaluator (taint-aware)
│ └── state.rs — MachineState: address spaces + shadow memory
├── arch/ — Architecture descriptors, calling conventions
├── os/
│ ├── linux/ — Linux syscall HLE (read, write, mmap, brk, exit, ...)
│ ├── windows/ — Windows HLE stubs
│ └── bare_metal/ — No-OS / embedded HLE
├── sym/
│ └── mod.rs — SymbolicExecutor: TTD-backed concolic path exploration
├── trace.rs — TraceLog: per-instruction audit trail
├── snapshot.rs — Lightweight snapshot helpers
└── loader.rs — Binary loading bridge for the emulator
CLI Integration
# Emulate a binary (default: auto-detect OS, no limits)
fission_cli run <binary>
# Limit to N instructions
fission_cli run <binary> --max-inst 10000
# Provide stdin mock data
fission_cli run <binary> --stdin "hello\n"
# Emit full instruction trace (JSON)
fission_cli run <binary> --trace --json
# Enable TTD recording (snapshot every 1000 instructions)
fission_cli run <binary> --ttd-record 1000
# Seek TTD to step N (rewind and replay from that point)
fission_cli run <binary> --ttd-record 1000 --ttd-seek 5000
# Enable concolic path exploration
fission_cli run <binary> --ttd-record 500 --sym-explore
Operating System HLE
The emulator uses High-Level Emulation (HLE) rather than emulating an actual OS kernel. Each architecture+OS pair has its own HLE handler:
| OS |
HLE Handler |
Key syscalls |
| Linux x86-64 |
os/linux/mod.rs |
read, write, mmap, brk, exit_group, open, close, fstat, stat |
| Windows x86-64 |
os/windows/hle.rs |
VirtualAlloc, HeapAlloc, WriteFile, ExitProcess, and common NT stubs |
| Bare Metal |
os/bare_metal/mod.rs |
Minimal: semihosting stubs only |
HLE handlers can intercept function calls by name (via PLT/IAT hooks) or by recognized calling patterns.
Calling Convention Support
The arch/ subsystem provides architecture-agnostic calling convention helpers:
Emulator::read_arg(n) — read the nth integer argument per the current ABI
Emulator::write_return_val(v) — write to the return register
Emulator::simulate_return() — pop the stack or read the link register and set PC
Currently supported: System V AMD64 ABI (Linux x86-64), Microsoft x64 (Windows), and a stub for ARM AArch64.
Time-Travel Debugging (TTD)
The fission-ttd crate provides a deterministic execution recorder and replayer. It is the backbone for all forms of non-linear execution analysis.
How TTD Works
- Recording Phase — As the
Emulator executes, every N instructions (configurable via --ttd-record <N>) it captures:
- Complete CPU register state (
RegisterState)
- Memory write deltas:
(address, old_bytes, new_bytes)
- Shadow state (taint) deltas:
(space_id, address, old_node_id, new_node_id)
-
Snapshot Storage — Snapshots are stored in a bounded ring buffer (TTDRecorder) with configurable capacity. Older snapshots are evicted when capacity is reached.
- Seeking / Rewinding —
Emulator::ttd_seek(step) locates the most recent snapshot at or before step, then restores:
- All GP registers from
RegisterState
- All memory bytes from
MemoryDelta records
- All symbolic taint mappings from
ShadowDelta records
- Replay — After rewinding,
emulator.run() continues forward from the restored state.
TTD Data Structures
| Type |
Crate |
Purpose |
TTDRecorder |
fission-ttd |
Manages the ring buffer of ExecutionSnapshots |
ExecutionSnapshot |
fission-ttd |
Single-point snapshot: registers + memory + shadow deltas |
MemoryDelta |
fission-ttd |
Before/after memory diff for one address range |
ShadowDelta |
fission-ttd |
Before/after taint AST node diff for one byte position |
RegisterState |
fission-ttd |
Full x86-64 GP register snapshot |
Symbolic Execution and Taint Engine
Fission’s taint and symbolic engine is a pure-Rust implementation — no Z3 bindings, no C++ FFI, no external SMT dependencies. It is designed for long-term maintainability and architecture-agnostic reasoning.
Two-Layer Architecture
fission-solver — Pure-Rust SMT/Constraint engine
├── ast.rs — SymExpr: the symbolic expression AST
└── solver.rs — Solver: node registry, path conditions, SAT stub
fission-emulator — Concrete + Symbolic execution
└── pcode/
├── state.rs — MachineState: shadow_memory (taint map)
└── eval.rs — Evaluator: taint propagation per P-Code opcode
SymExpr AST Nodes
Every symbolic value is represented as a node in the SymExpr tree:
| Variant |
Description |
Const { val, size } |
Concrete bitvector constant |
Var { id, name, size } |
Named symbolic variable (e.g. stdin_0x4000) |
Add(a, b) / Sub(a, b) / Mul(a, b) |
Integer arithmetic |
And(a, b) / Or(a, b) / Xor(a, b) |
Bitwise operations |
Shl(a, b) / Lshr(a, b) |
Shift operations |
Eq(a, b) / Neq(a, b) / Ult(a, b) / Ule(a, b) |
Comparisons (return 1-bit) |
Ite { cond, t, f } |
If-then-else |
Extract { expr, lsb, size } |
Bit extraction |
Concat(a, b) |
Bitvector concatenation |
Shadow Memory (Taint State)
The MachineState holds a parallel “shadow” layer alongside the concrete memory:
// shadow_memory: (space_id, byte_address) -> SymNodeId
pub shadow_memory: HashMap<(u64, u64), u32>
space_id = 2 → CPU registers (Sleigh register address space)
space_id = 3 → RAM (Sleigh ram address space)
When a concrete byte is written, its shadow entry is cleared. When a symbolic value is written, its shadow entry is updated with the corresponding AST node ID.
Taint Propagation Table
| P-Code Op |
Taint Behavior |
COPY |
Propagate source shadow to destination |
LOAD |
Propagate shadow from RAM byte to output varnode |
STORE |
Propagate shadow from source varnode to RAM byte |
INT_ADD |
If either input is tainted, build SymExpr::Add(a, b) and store new node |
INT_SUB |
Similar: build SymExpr::Sub |
| Other ops |
Currently concrete-only (no taint propagation yet) |
Taint Sources
The primary taint source is stdin. In os/linux/mod.rs, the sys_read(fd=0, ...) handler:
- Reads bytes from
stdin_buffer (the --stdin mock).
- Writes them into RAM as concrete bytes.
- For each byte, calls
solver.register_var("stdin_<addr>", 1) to create a SymExpr::Var.
- Tags the corresponding
shadow_memory entries with the new node ID.
From that point forward, any P-Code operation that reads those bytes will propagate the taint forward into new SymExpr constraint trees.
Concolic Path Exploration
Concolic (concrete + symbolic) execution combines real execution with symbolic state to explore multiple code paths automatically.
How It Works
- The emulator runs normally in concrete mode, recording TTD snapshots along the way.
- Every
CBranch (conditional branch) P-Code instruction emits a SymBranch event containing:
- The TTD step index at the branch point
- The current PC value
- Whether the branch was taken or not
- The alternate target (address or relative P-Code index)
- After the current path terminates, the
SymbolicExecutor pops unexplored branches from a queue.
- It rewinds the emulator to the snapshot closest to the branch step via
ttd_seek().
- It forces the PC (or P-Code index) to the alternate target and resumes execution.
- This continues until the exploration queue is empty.
Path 1: [A] → [B] → [D] → halt (branch at B taken = true)
Rewind to B
Path 2: [A] → [B] → [C] → [E] → halt (branch at B taken = false)
SymbolicExecutor
The SymbolicExecutor (sym/mod.rs) is the exploration driver:
pub struct SymbolicExecutor {
pub emu: Emulator,
pub queue: Vec<SymBranch>, // unexplored branch events
}
It calls emu.run() in a loop, drains emu.sym_events into the queue, and rewinds to the next unexplored branch. Each new execution path may reveal additional branches, which are added to the queue.
Future: Full Symbolic Mode
The current implementation is concolic scaffolding: it explores paths by forcing PC values, but does not yet invert branch conditions symbolically via the Solver. The planned evolution:
- When a
CBranch is encountered and a taint variable is used in the branch condition, add the negated constraint !condition to solver.assertions.
- Call
solver.check_sat() to verify the alternate path is feasible.
- Use
solver.get_value(var_id) to obtain a concrete input that triggers the alternate path.
- Replay with that concrete input instead of forcing PC.
This requires implementing the DPLL/CDCL bit-blasting core inside fission-solver, which is the next development milestone.
Structuring Model
The active structuring path is graph-oriented and proof-driven. It should use deterministic collapse rules and explicit fallback when legality is incomplete.
StructureGraph owns the collapsed overlay.
CollapseDriver applies deterministic collapse rules.
RegionProof records replacement and readiness evidence.
- Collapse only proof-complete and emit-ready regions.
- Fallback output should be explicit rather than disguised as clean structure.
- Printer and postprocess code must not reconstruct structure after the fact.
Pass Pipeline Rules
There are two independent pass-orchestration tracks; neither subsumes the
other, because they operate on different IR shapes. Current migration
status and the full per-stage backlog live in PROJECT.md.
- Structuring (pre-structuring, block-CFG level): new transformations
should be expressed as
NirPass implementations
(crates/fission-pcode/src/midend/pass/), operating on NirFunc (wraps
PreviewBuilder’s block/CFG state) through PassCtx instead of capturing
builder internals. Every pass declares an InvariantBasis (dominator
tree, postdominator tree, SCC, loop body, edge classification) so review
can reject address/function-specific overfitting. PassOutcome::changed
must be accurate. Binary-specific and address-specific guards are
forbidden in pass bodies.
- Normalize (post-structuring,
HirFunction/Vec<HirStmt> level): new
transformations should be expressed as action_pipeline::Pass
implementations (fission-midend-core::action_pipeline), registered as
ActionGroup entries in fission-midend-normalize/src/pipeline/groups.rs.
Prefer the existing composable primitives (fn_pass, cleanup_pass,
gated_followup, admission_gated) over new free-function control flow
in pipeline/stages.rs; this migration is in progress, is done
stage-by-stage with a real-binary before/after parity check per slice, and
is not something to attempt in one large change. cleanup_pass is
budget-gated (mirrors the legacy run_cleanup_block admission check on
EARLY_CLEANUP_BLOCK_STMT_LIMIT/BLOCK_LIMIT); fn_pass is not — using
the wrong one silently changes admission behavior on large functions
without failing any test on small ones, so always check which one the
original call site used before registering.
- A chain whose body calls something that itself needs
diag/perf
(apply_type_signature_fixed_point, run_cleanup_family_passes) cannot
go through fn_pass/GatedFollowupPass — neither primitive carries
diag/perf through to a callee. Keep those as a named stage_pass step
instead of dropping the forwarding silently.
Determinism
Decompiler output must be identical across separate process runs of the
same binary (AGENTS.md Core Rule 4). std::collections::HashMap/HashSet
use a per-process-random RandomState by default; any unsorted iteration
over one that feeds a .first()/.find_map()-style pick (not just a
.contains()/.get() membership check) is a real nondeterminism bug, not
a style nit — two were found and fixed this way in
fission-pcode::midend::structuring. When adding a new HashMap/HashSet
in fission-pcode::midend or fission-midend-structuring, prefer the
crate-local fixed-seed alias (rustc_hash::FxBuildHasher, already the
default there) over std::collections::HashMap directly, and never iterate
either collection to pick a specific value without sorting first.
Telemetry and Reports
Telemetry is useful only if the same counter means the same thing everywhere. Fission keeps canonical decompiler counters in NirBuildStats and projects them outward.
NirBuildStats is the canonical telemetry owner.
- Automation reports should consume canonical counters.
- Benchmark layers should not define parallel meanings for the same behavior.
- Regression reasons should map to structuring, materialization, type, or lift families.
- Reporting changes are not semantic fixes unless the row-level oracle moves.
Testing Matrix
| Scope |
Default command |
Use when |
| pcode tests |
cargo nextest run -p fission-pcode |
NIR, HIR, structuring, printer, and type-hint work. |
| pcode check |
cargo check -p fission-pcode |
Compile validation after semantic changes. |
| decompiler check |
cargo check -p fission-decompiler |
Orchestration or Rust-Sleigh glue changes. |
| automation check |
cargo check -p fission-automation |
Telemetry or reporting changes. |
| core tests |
cargo nextest run -p fission-core |
Resource path and shared core changes. |
| CLI build |
cargo build -p fission-cli --release |
Product and benchmark validation requiring the release CLI. |
| workspace check |
cargo check --workspace |
Broad compile confidence before larger handoff. |
- Use
cargo nextest run by default for Rust tests.
- Use
cargo test for doctests or harness-specific behavior.
- Run targeted tests before crate-wide tests.
- Do not claim success from a targeted test if crate-level regression remains.
- Call out known unrelated failures explicitly.
CI and Release
CI source of truth lives in .github/workflows/. Reusable workflows keep the main pipelines smaller and make heavy checks explicit.
.github/workflows/ci.yml
.github/workflows/ci-heavy.yml
.github/workflows/cd.yml
.github/workflows/release-tag.yml
.github/workflows/fuzz.yml
.github/workflows/ci-cd-monitor.yml
.github/workflows/reusable-build-cli.yml
.github/workflows/reusable-cli-smoke.yml
.github/workflows/reusable-corpus-validation.yml
.github/workflows/reusable-coverage.yml
.github/workflows/reusable-lint-format.yml
.github/workflows/reusable-miri.yml
.github/workflows/reusable-msrv.yml
.github/workflows/reusable-nir-check.yml
.github/workflows/reusable-nir-regression-gate.yml
.github/workflows/reusable-run-tests.yml
.github/workflows/reusable-security-check.yml
.github/workflows/reusable-setup-rust.yml
.github/workflows/reusable-upload-artifacts.yml
.github/workflows/reusable-benchmark.yml
Release tags should be shipped through Release Tag (CI green), which tags only a commit whose push run has already passed the required CI path.
Development Workflow
- Read the nearest
AGENTS.md before editing a scoped area.
- Confirm the owner layer before changing behavior.
- Inspect existing tests and local patterns.
- Add focused coverage for new invariants.
- Make scoped production changes.
- Run targeted validation.
- Run crate-level validation.
- Inspect Git status and stage only intended hunks.
- Report both mechanical change and quality impact.
Dirty Worktree Discipline
- Assume unrelated changes belong to the user.
- Do not revert changes you did not make.
- Ignore unrelated dirty files unless they block the task.
- If a touched file has user changes, read carefully and work with them.
- Use non-interactive Git commands when possible.
Security and Malware Sample Policy
Reverse-engineering repositories often touch untrusted binaries. Treat samples and externally sourced executables as hostile inputs.
- Do not execute unknown samples as part of normal decompiler validation.
- Prefer parsing and static analysis paths.
- Keep malware sample handling aligned with
docs/MALWARE_SAMPLE_POLICY.md and SECURITY.md.
- Do not upload private or suspicious binaries to third-party services without explicit approval.
- Keep sample provenance visible in reports.
Contributor Notes
- Prefer small invariant-based fixes over address-specific special cases.
- Use CFG, dominance, post-dominance, SCC, dataflow, and fixed-point reasoning where appropriate.
- Do not add runtime dependencies on
vendor/.
- Do not add C++ bindings to shortcut reference behavior.
- Do not bypass existing resource configuration helpers.
- Document new public contracts near the code and in
docs/ when they affect users.
Troubleshooting
| Symptom |
First check |
| Missing Sleigh specs |
Pull fission-utils.tar.gz from the assets-v1 release (see Resource Bundle) and check resource status. |
| CLI cannot find resources |
Check FISSION_RESOURCE_ROOT, --resource-root, and PathConfig::detect behavior. |
| Raw p-code mismatch |
Start in fission-sleigh before interpreting NIR output. |
| NIR is wrong but p-code is right |
Investigate NIR materialization, normalization, or type hint application. |
| HIR is unreadable but NIR is right |
Investigate structuring, cleanup, or printer consume behavior. |
| Report counters disagree |
Trace the counter back to NirBuildStats. |
| Loader identifies unsupported container |
Extract an executable child explicitly instead of raw-loading the container. |
| A test passes locally but CI fails |
Check LFS pulls, OS-specific paths, feature flags, and reusable workflow inputs. |
Glossary
| Term |
Meaning |
| CFG |
Control-flow graph. |
| HIR |
High-level intermediate representation for readable pseudocode. |
| NIR |
Normalized intermediate representation with strict semantic requirements. |
| P-code |
Ghidra-style low-level instruction semantics representation. |
| Sleigh |
Language specification system used for instruction decode and semantics. |
| Dominance |
Graph relation used to reason about control-flow ownership. |
| Post-dominance |
Graph relation used to reason about exits and structured regions. |
| SCC |
Strongly connected component, often used for loop analysis. |
| RegionProof |
Evidence that a region can be safely promoted during structuring. |
| NirBuildStats |
Canonical NIR telemetry contract. |
| FactStore |
Aggregated facts and provenance consumed by decompilation contexts. |
| FID |
Function identification through signatures. |
| LFS |
Git Large File Storage. |
| Taint |
A label on data indicating it originated from a symbolic (untrusted) source. |
| Shadow Memory |
Parallel memory map tracking symbolic AST node IDs alongside concrete bytes. |
| Concolic |
Execution that combines concrete runs with symbolic state to explore multiple paths. |
| HLE |
High-Level Emulation: OS syscall interception without full kernel emulation. |
| TTD |
Time-Travel Debugging: record/replay execution via memory and register snapshots. |
| SymExpr |
Symbolic expression AST node in fission-solver. |
| SAT |
Boolean satisfiability problem. Used to check if a path constraint can be fulfilled. |
Roadmap
- Improve x86 and x86-64 pseudocode quality on small sample binaries first.
- Continue strengthening control-flow recovery for if, else, switch, loop, break, and continue structures.
- Improve pointer, array, struct, and field-access expression recovery.
- Improve calling convention, parameter, local-variable, return-value, accumulator, and induction-variable cleanup.
- Maintain raw p-code parity gates for Sleigh changes.
- Improve FID and name recovery relative to signature ecosystems.
- Expand architecture and file-format breadth after x86/x86-64 quality is strong enough.
- Expand the pure-Rust symbolic execution engine: implement DPLL/CDCL bit-blasting in
fission-solver.
- Add more P-Code taint propagation opcodes in
fission-emulator.
- Connect taint-tracking results to decompiler type recovery.
- Continue the normalize
action_pipeline/ActionGroup migration stage by stage (see PROJECT.md for current status and backlog); each stage’s imperative run_stage_* free function is replaced with declarative passes, validated with a real-binary before/after check.
Appendix A: Commands
Build
cargo build -p fission-cli --release
cargo check --workspace
Test
cargo nextest run -p fission-pcode
cargo nextest run -p fission-core
cargo fmt --all --check
cargo clippy --workspace --all-targets -- -D warnings
CLI smoke
./target/release/fission_cli --help
./target/release/fission_cli info <binary>
./target/release/fission_cli list <binary>
Resources
mkdir -p utils
curl -L --fail --show-error \
"https://github.com/sjkim1127/Fission/releases/download/assets-v1/fission-utils.tar.gz" \
-o /tmp/fission-utils.tar.gz
tar -xzf /tmp/fission-utils.tar.gz -C utils --strip-components=1
fission_cli resources status
Appendix B: Review Checklists
Semantic change
Loader change
Sleigh change
Automation change
Documentation change
Appendix C: File Ownership Index
| Path |
Owner meaning |
AGENTS.md |
Repository-level contributor instructions. |
Cargo.toml |
Workspace members and profile settings. |
crates/fission-pcode/src/nir/ |
Core NIR/HIR implementation. |
crates/fission-pcode/src/nir/structuring/ |
Structuring algorithms and region collapse. |
crates/fission-pcode/src/nir/types/ |
NIR/HIR type contracts and build stats. |
crates/fission-decompiler/ |
Decompiler orchestration and Rust-Sleigh bridge. |
crates/fission-sleigh/ |
Sleigh runtime. |
crates/fission-static/src/analysis/ |
Static facts and analysis services. |
crates/fission-loader/src/loader/ |
Binary loader implementations. |
crates/fission-automation/src/report/ |
Automation report implementation. |
crates/fission-cli/src/cli/ |
CLI command ownership. |
docs/architecture/ARCHITECTURE.md |
Architecture source of truth. |
docs/adr/ |
Architectural decision records. |
utils/ |
Checked-in resource bundle material. |
vendor/ |
Reference-only third-party material. |
Appendix D: Quality Investigation Playbook
Raw p-code suspect
- Compare raw p-code against a known-good reference.
- Check Sleigh token cursor placement.
- Check dynamic memory output materialization.
- Add a row-level canary before broad benchmark runs.
NIR suspect
- Inspect p-code to NIR lowering.
- Check temporary, register, stack, and memory varnode handling.
- Inspect normalization rules and wave stats.
- Validate semantic preservation before HIR cleanup.
Type recovery suspect
- Check calling convention source.
- Check stack slot and parameter hints.
- Check import and function hints.
- Avoid output-only substitution.
Structuring suspect
- Inspect CFG shape.
- Inspect dominance and post-dominance.
- Inspect SCC and loop facts.
- Check
RegionProof and collapse readiness.
- Prefer explicit fallback over invalid structure.
Printer suspect
- Confirm HIR is already correct.
- Keep printer consume-only.
- Avoid reconstructing missing control flow.
- Add snapshot coverage if formatting changes.
Appendix E: Documentation Index
docs/CLI.md
docs/EVALUATION.md
docs/MALWARE_SAMPLE_POLICY.md
docs/PROJECT_MAP.md
docs/QUALITY_METRICS.md
docs/RELEASE.md
docs/VERSIONING.md
docs/adr/README.md
docs/architecture/ARCHITECTURE.md
docs/architecture/DECOMPILER_ACTIONS.md
docs/architecture/DIAGRAMS.md
docs/architecture/GHIDRA_PARITY_GAP_AUDIT.md
docs/architecture/XREF_INDEX.md
docs/contributing/LABELS.md
docs/onboarding/ADDING_A_LOADER_TEST.md
docs/onboarding/DEBUGGING_A_DECOMP_FAILURE.md
docs/onboarding/FIRST_30_MINUTES.md
docs/roadmap/RUST_DECOMPILER_ROADMAP.md
CONTRIBUTING.md
SECURITY.md
THIRD_PARTY.md
LICENSE
Field Guide: Practical Rules by Area
01. P-code parity
Verify instruction semantics before diagnosing decompiler output.
- Owner check: identify the crate that owns p-code parity before editing.
- Evidence check: record the concrete function, row, sample, test, or artifact that motivated the change.
- Coverage check: add focused coverage for the invariant rather than only inspecting output manually.
- Regression check: run the smallest useful test first and then the relevant crate check.
- Reporting check: state whether the change is semantic, presentational, telemetry-only, or documentation-only.
02. NIR materialization
Preserve source behavior even when output is temporarily verbose.
- Owner check: identify the crate that owns nir materialization before editing.
- Evidence check: record the concrete function, row, sample, test, or artifact that motivated the change.
- Coverage check: add focused coverage for the invariant rather than only inspecting output manually.
- Regression check: run the smallest useful test first and then the relevant crate check.
- Reporting check: state whether the change is semantic, presentational, telemetry-only, or documentation-only.
03. HIR cleanup
Improve readability only after the semantic basis is correct.
- Owner check: identify the crate that owns hir cleanup before editing.
- Evidence check: record the concrete function, row, sample, test, or artifact that motivated the change.
- Coverage check: add focused coverage for the invariant rather than only inspecting output manually.
- Regression check: run the smallest useful test first and then the relevant crate check.
- Reporting check: state whether the change is semantic, presentational, telemetry-only, or documentation-only.
04. Type hints
Promote evidence-backed types and keep provenance inspectable.
- Owner check: identify the crate that owns type hints before editing.
- Evidence check: record the concrete function, row, sample, test, or artifact that motivated the change.
- Coverage check: add focused coverage for the invariant rather than only inspecting output manually.
- Regression check: run the smallest useful test first and then the relevant crate check.
- Reporting check: state whether the change is semantic, presentational, telemetry-only, or documentation-only.
05. Stack recovery
Model stack slots consistently across calls, locals, and spills.
- Owner check: identify the crate that owns stack recovery before editing.
- Evidence check: record the concrete function, row, sample, test, or artifact that motivated the change.
- Coverage check: add focused coverage for the invariant rather than only inspecting output manually.
- Regression check: run the smallest useful test first and then the relevant crate check.
- Reporting check: state whether the change is semantic, presentational, telemetry-only, or documentation-only.
06. Pointer recovery
Prefer data-flow-backed pointer reasoning over text substitution.
- Owner check: identify the crate that owns pointer recovery before editing.
- Evidence check: record the concrete function, row, sample, test, or artifact that motivated the change.
- Coverage check: add focused coverage for the invariant rather than only inspecting output manually.
- Regression check: run the smallest useful test first and then the relevant crate check.
- Reporting check: state whether the change is semantic, presentational, telemetry-only, or documentation-only.
07. Array recovery
Recover indexed forms when stride and base evidence are present.
- Owner check: identify the crate that owns array recovery before editing.
- Evidence check: record the concrete function, row, sample, test, or artifact that motivated the change.
- Coverage check: add focused coverage for the invariant rather than only inspecting output manually.
- Regression check: run the smallest useful test first and then the relevant crate check.
- Reporting check: state whether the change is semantic, presentational, telemetry-only, or documentation-only.
08. Struct recovery
Recover field access only when layout evidence supports it.
- Owner check: identify the crate that owns struct recovery before editing.
- Evidence check: record the concrete function, row, sample, test, or artifact that motivated the change.
- Coverage check: add focused coverage for the invariant rather than only inspecting output manually.
- Regression check: run the smallest useful test first and then the relevant crate check.
- Reporting check: state whether the change is semantic, presentational, telemetry-only, or documentation-only.
09. Calling convention
Derive parameters and returns from ABI facts and observed uses.
- Owner check: identify the crate that owns calling convention before editing.
- Evidence check: record the concrete function, row, sample, test, or artifact that motivated the change.
- Coverage check: add focused coverage for the invariant rather than only inspecting output manually.
- Regression check: run the smallest useful test first and then the relevant crate check.
- Reporting check: state whether the change is semantic, presentational, telemetry-only, or documentation-only.
10. Loop structuring
Use SCC and dominance facts before emitting structured loops.
- Owner check: identify the crate that owns loop structuring before editing.
- Evidence check: record the concrete function, row, sample, test, or artifact that motivated the change.
- Coverage check: add focused coverage for the invariant rather than only inspecting output manually.
- Regression check: run the smallest useful test first and then the relevant crate check.
- Reporting check: state whether the change is semantic, presentational, telemetry-only, or documentation-only.
11. Switch recovery
Use jump-table evidence and bounds before emitting switch syntax.
- Owner check: identify the crate that owns switch recovery before editing.
- Evidence check: record the concrete function, row, sample, test, or artifact that motivated the change.
- Coverage check: add focused coverage for the invariant rather than only inspecting output manually.
- Regression check: run the smallest useful test first and then the relevant crate check.
- Reporting check: state whether the change is semantic, presentational, telemetry-only, or documentation-only.
12. Goto fallback
Use explicit fallback when legal structure is not proven.
- Owner check: identify the crate that owns goto fallback before editing.
- Evidence check: record the concrete function, row, sample, test, or artifact that motivated the change.
- Coverage check: add focused coverage for the invariant rather than only inspecting output manually.
- Regression check: run the smallest useful test first and then the relevant crate check.
- Reporting check: state whether the change is semantic, presentational, telemetry-only, or documentation-only.
Render the model; do not create semantic facts.
- Owner check: identify the crate that owns printer formatting before editing.
- Evidence check: record the concrete function, row, sample, test, or artifact that motivated the change.
- Coverage check: add focused coverage for the invariant rather than only inspecting output manually.
- Regression check: run the smallest useful test first and then the relevant crate check.
- Reporting check: state whether the change is semantic, presentational, telemetry-only, or documentation-only.
14. Loader identity
Attach evidence without changing parse semantics.
- Owner check: identify the crate that owns loader identity before editing.
- Evidence check: record the concrete function, row, sample, test, or artifact that motivated the change.
- Coverage check: add focused coverage for the invariant rather than only inspecting output manually.
- Regression check: run the smallest useful test first and then the relevant crate check.
- Reporting check: state whether the change is semantic, presentational, telemetry-only, or documentation-only.
15. Resource lookup
Route through resource roots and path config.
- Owner check: identify the crate that owns resource lookup before editing.
- Evidence check: record the concrete function, row, sample, test, or artifact that motivated the change.
- Coverage check: add focused coverage for the invariant rather than only inspecting output manually.
- Regression check: run the smallest useful test first and then the relevant crate check.
- Reporting check: state whether the change is semantic, presentational, telemetry-only, or documentation-only.
16. Automation reports
Project canonical metrics and keep outputs deterministic.
- Owner check: identify the crate that owns automation reports before editing.
- Evidence check: record the concrete function, row, sample, test, or artifact that motivated the change.
- Coverage check: add focused coverage for the invariant rather than only inspecting output manually.
- Regression check: run the smallest useful test first and then the relevant crate check.
- Reporting check: state whether the change is semantic, presentational, telemetry-only, or documentation-only.
17. CI gates
Prefer focused reusable jobs with explicit inputs.
- Owner check: identify the crate that owns ci gates before editing.
- Evidence check: record the concrete function, row, sample, test, or artifact that motivated the change.
- Coverage check: add focused coverage for the invariant rather than only inspecting output manually.
- Regression check: run the smallest useful test first and then the relevant crate check.
- Reporting check: state whether the change is semantic, presentational, telemetry-only, or documentation-only.
Tag only after successful push CI for the exact commit.
- Owner check: identify the crate that owns release tags before editing.
- Evidence check: record the concrete function, row, sample, test, or artifact that motivated the change.
- Coverage check: add focused coverage for the invariant rather than only inspecting output manually.
- Regression check: run the smallest useful test first and then the relevant crate check.
- Reporting check: state whether the change is semantic, presentational, telemetry-only, or documentation-only.
19. Vendor reference
Consult for invariants without copying or depending on code.
- Owner check: identify the crate that owns vendor reference before editing.
- Evidence check: record the concrete function, row, sample, test, or artifact that motivated the change.
- Coverage check: add focused coverage for the invariant rather than only inspecting output manually.
- Regression check: run the smallest useful test first and then the relevant crate check.
- Reporting check: state whether the change is semantic, presentational, telemetry-only, or documentation-only.
20. Documentation
Keep claims grounded in current source and docs.
- Owner check: identify the crate that owns documentation before editing.
- Evidence check: record the concrete function, row, sample, test, or artifact that motivated the change.
- Coverage check: add focused coverage for the invariant rather than only inspecting output manually.
- Regression check: run the smallest useful test first and then the relevant crate check.
- Reporting check: state whether the change is semantic, presentational, telemetry-only, or documentation-only.
Contributor Playbooks
This section expands the repository rules into concrete scenarios. It is intentionally operational: each playbook describes where to start, what to avoid, and what evidence should exist before claiming the work is done.
01. Fixing a raw p-code mismatch
Start in fission-sleigh; compare emitted p-code before touching NIR or HIR.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
02. Fixing an NIR materialization bug
Start in fission-pcode/src/nir; preserve exact source behavior before readability work.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
03. Improving HIR readability
Start from correct NIR; prefer cleanup passes over printer substitutions.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
04. Changing structuring logic
Start in fission-pcode/src/nir/structuring; require CFG evidence and proof completeness.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
05. Adding a new NIR pass
Implement a pass with declared analysis dependencies and accurate changed status.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
06. Debugging an if-else recovery failure
Inspect dominance, post-dominance, and region exits before modifying emitted syntax.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
07. Debugging a loop recovery failure
Inspect SCCs, loop headers, latches, exits, and break or continue candidates.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
08. Debugging a switch recovery failure
Inspect jump table evidence, bounds, case targets, and default target handling.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
09. Cleaning unnecessary temporaries
Confirm the temporary has no semantic or ordering role before removing it.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
10. Recovering function parameters
Use ABI facts, call uses, stack/register evidence, and type context together.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
11. Recovering local variables
Tie stack slots, stores, loads, and lifetimes to stable local names.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
12. Recovering return values
Check accumulator registers, call sites, and observed return uses.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
13. Recovering pointer arithmetic
Prefer base plus offset or indexed forms only when data-flow evidence supports them.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
14. Recovering arrays
Require stable stride, base object, and index expression evidence.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
15. Recovering struct fields
Require layout evidence before rendering field access.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
Fail closed for unknown or unsupported families; never hide uncertainty as raw bytes.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
17. Adding loader provenance
Attach evidence without changing parsing semantics or decompiler behavior.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
18. Changing import handling
Keep true imports, import thunks, undefined externals, and debug-only symbols distinct.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
19. Changing export handling
Preserve symbol provenance and loader-owned function views.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
20. Changing resource lookup
Route through path config and resource roots; do not embed local absolute paths.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
21. Changing utility manifests
Keep manifests deterministic and explain what data is required at runtime.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
22. Changing signature lookup
Keep signature hits evidence-backed and separate from semantic repair.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
23. Changing automation reports
Project NirBuildStats and other canonical counters; do not redefine metrics.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
24. Changing JSON report contracts
Version or document the contract and keep output deterministic.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
25. Changing CLI output
Keep CLI as a surface; do not fix semantics in formatting code.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
26. Changing CLI command parsing
Separate compatibility shims from command ownership and behavior.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
27. Changing TUI behavior
Preserve backend contracts and avoid UI-specific semantic rules.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
28. Changing Dioxus GUI behavior
Consume shared contracts and avoid duplicate function filtering rules.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
29. Changing AI integration surfaces
Keep AI assistance advisory and preserve deterministic core behavior.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
30. Changing plugin contracts
Keep contracts explicit, stable, and separated from core crate internals.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
31. Changing dynamic-analysis support
Keep dynamic evidence labeled and do not blur it with static facts.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
32. Changing time-travel support
Keep trace-derived facts explicit and reproducible.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
33. Adding a new test fixture
Document provenance, architecture, compiler, and why the fixture is useful.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
34. Adding a regression test
Name the invariant, not just the failing sample.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
35. Updating snapshots
Inspect semantic meaning before accepting changed text.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
36. Investigating benchmark movement
Compare exact rows, artifacts, scores, stdout, stderr, and feature gaps.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
37. Investigating a pass-count change
Trace whether the change is semantic, presentational, telemetry-only, or noise.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
38. Investigating a size change
Inspect line count and byte count together with readability and semantics.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
39. Investigating a CI-only failure
Check OS, LFS resources, workflow inputs, feature flags, and rust version.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
40. Investigating a resource-missing failure
Check LFS pull scope, resource roots, and CLI resource status.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
41. Investigating a panic
Capture command, input, backtrace, crate owner, and minimal reproducer.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
42. Investigating non-determinism
Check map iteration, filesystem order, random seeds, timestamps, and local paths.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
43. Changing dependencies
Justify long-term maintenance value and avoid dependency shortcuts for core semantics.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
44. Consulting Ghidra
Use it for invariants and expected behavior, not copied implementation.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
45. Consulting RetDec
Use it as reference material without creating runtime dependency.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
46. Touching vendor trees
Do not add production links, shell-outs, bindings, or copied shortcuts.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
47. Touching utils/
Use existing loaders and manifests instead of bypassing resource configuration.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
48. Writing architecture docs
State owner boundaries and avoid implying surface layers own semantics.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
49. Writing user docs
Prefer commands and observed behavior over aspiration.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
50. Writing troubleshooting docs
Map symptom to first owner and first command.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
51. Writing release notes
Separate features, fixes, quality movement, and known limitations.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
52. Preparing a commit
Stage intended hunks only and keep unrelated dirty work untouched.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
53. Preparing a PR
Lead with behavior change, validation, and residual risk.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
54. Reviewing a PR
Prioritize bugs, regressions, missing tests, and ownership drift.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
55. Refactoring shared code
Keep behavior stable unless the refactor explicitly includes a measured semantic change.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
56. Adding an abstraction
Add it only when it removes real duplication or encodes a real invariant.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
57. Deleting legacy code
Prove the active path no longer depends on it and keep compatibility expectations visible.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
58. Changing telemetry names
Check every consumer and avoid parallel meanings.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
59. Changing public structs
Consider CLI JSON, GUI, automation, and downstream compatibility.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
60. Changing error types
Keep errors typed enough for users and automation to act on.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
61. Changing logging
Keep logs useful for debugging without making tests flaky.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
Measure before and after when the change affects common loops.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
63. Changing memory-heavy paths
Check large binaries and avoid unbounded accumulation.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
64. Changing parser code
Use structured readers and bounds checks rather than ad hoc slicing.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
65. Changing graph algorithms
Prefer explicit graph facts over lexical ordering or sample-specific assumptions.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
66. Changing dataflow analysis
Document convergence, lattice meaning, and budget behavior.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
67. Changing fixed-point loops
Make termination, changed status, and budget behavior inspectable.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
68. Changing type inference
Keep confidence and provenance visible; avoid overconfident names.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
69. Changing ABI handling
Keep architecture and calling convention boundaries explicit.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
70. Changing x86 behavior
Validate exact sample first, then the broader x86/x86-64 family.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
71. Changing non-x86 behavior
Do not regress x86/x86-64 priority while expanding breadth.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
72. Changing docs only
Do not claim semantic improvement from documentation changes.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
73. Changing logo or README assets
Keep icon and README logo responsibilities separate.
- Owner: identify the crate and module that owns the behavior before editing.
- Evidence: preserve the command, binary, address, row, fixture, or artifact that motivated the change.
- Coverage: add or update the smallest test that captures the invariant.
- Validation: run the targeted check first, then the relevant crate-level check.
- Regression: compare existing passing rows or smoke lanes when behavior is user-visible.
- Report: separate mechanical change from quality improvement.
Review Question Bank
Use these questions during code review or before handing off a quality cycle.
Maintainer Handoff Template
Use this template when handing off a substantial decompiler-quality change.
- Problem:
- Root cause:
- Owner layer:
- Implementation summary:
- Tests run:
- Benchmarks or row checks:
- Artifacts inspected:
- Quality result:
- Regressions checked:
- Known risks:
- Follow-up work: