.wasm, most of your reverse-engineering work is still valid. The
same functions exist, doing the same things, at shifted table indices. The diff engine
recovers that work automatically, classifies every function in the new binary, ports annotations
forward, and hands you a focused changelog of what actually changed in the application.
This is Phase 3 of the WARDEN pipeline: diff/engine.py, driven by warden diff.
The diff engine reuses the identical fingerprint and similarity engine as the Oracle: same
hash compositions, same
similarity() function, different corpus. Any improvement to the
fingerprinting algorithm benefits both. See core concepts for the full fingerprint
breakdown.How it works
diff_versions() loads all defined functions for both versions from the KB, reconstructs their
fingerprints from the stored rows, and runs three passes in sequence. Each pass consumes
functions from a shared “unmatched” set so a function is never counted twice.
Pass 1: exact-body match
Functions with an identicalexact_hash (SHA-256 of the raw body bytes) are matched first. This
is O(n) via a dictionary lookup. No similarity math is needed.
- Same index in both versions → classified unchanged.
- Different index → classified moved.
stable_id row in the symbols table,
so their annotations are already there. There is nothing to port.
Pass 2: stable identity match
Functions that share astable_id but whose raw body differs slightly (for example, a literal
constant was patched in a way that structural_hash absorbs) are matched by identity key lookup.
Because the KB’s symbols table is keyed on stable_id (not on a version or function index),
these functions also already share an annotation row.
- Classified unchanged or moved by the same index-comparison rule.
- Score is 0.99 to distinguish from a literal exact-body match.
Pass 3: greedy fuzzy match
Remaining functions (those that changed meaningfully enough to get a newstable_id) are paired
by the highest similarity().overall score among all remaining-from candidates.
MODIFIED_THRESHOLD = 0.6. Accepted pairs
are classified modified. Functions left unmatched after all three passes become new (in
the newer version only) or deleted (in the older version only).
The 0.6 threshold is deliberately lenient. A modified function that retains its general call
pattern and opcode character but gained a bounds check or an extra branch will typically score
0.65–0.85. Setting the threshold higher risks losing carry-over for legitimately modified
functions; setting it lower creates false pairings. The value is defined as
MODIFIED_THRESHOLD in diff/engine.py and can be overridden if you are working with a
heavily optimized corpus.Classification summary
| Class | Meaning | Index change | Annotation ported? |
|---|---|---|---|
unchanged | Identical body (exact_hash or stable_id match), same index | No | Already shared |
moved | Identical body or identity, different index | Yes | Already shared |
modified | Fuzzy match above threshold; body changed meaningfully | Maybe | Copied with penalty |
new | No match found in the older version | N/A | None; queued for analysis |
deleted | No match found in the newer version | N/A | Archived |
Annotation carry-over: identical vs. fuzzy
Unchanged and moved: zero work
Functions classifiedunchanged or moved share the same stable_id between both versions.
Because the symbols table is keyed to stable_id, not to a version row or function index,
these functions already point at the same symbol. There is nothing to copy. The name, type
signature, summary, provenance, and confidence from your v1 work are immediately visible in v2
with zero intervention.
Modified: copied with a confidence penalty
When a fuzzy match is accepted,_carry_symbol() runs:
- Look up the older function’s symbol by its
stable_id. - Check whether the newer function’s
stable_idalready has a symbol. If it does, leave it alone. A pre-existing annotation from a higher-authority source takes precedence. - Write a new symbol for the newer
stable_idwith:- The same name, type signature, and summary as the source.
provenance = "diff-carry"(rank 40 in the provenance economy, beloworacle(90) but aboveagent(30)).confidence = old.confidence × CARRY_PENALTYwhereCARRY_PENALTY = 0.7.
parseToken with confidence 0.92 after the Oracle and human review will arrive
in v2 as parseToken with confidence 0.644 and provenance diff-carry. The penalty signals
“probably still right, worth a second look.” Agents will not overwrite this (their rank is lower);
Oracle re-identification can upgrade it if the function still hits a corpus signature.
The evidence field records the carry trail:
The semantic changelog
After classification,render_changelog() produces a human-readable report that does two things
ordinary binary diff tools cannot: it counts only app-code changes and explains the rest as
runtime/toolchain churn.
A function is tagged as runtime churn if its name (from the current or previous version) starts
with any of a list of known prefixes:
Sample changelog
The warden diff command
diffs table as a JSON
DiffReport and printed as the semantic changelog.
warden diff completes, you can inspect carry-over results directly:
Full pipeline: v2 in practice
Ingest the new version
Review only the app deltas
The changelog’s “Needs review” section lists the functions that actually changed in
application code. Inspect each:If the carried name is still correct, lock it:
Re-run the Oracle and agents on new functions
New and heavily modified functions have no annotation yet. The Oracle may identify runtime
additions from a toolchain bump; agents cover the rest.
What the DiffReport contains
The full report is stored in the diffs table as JSON (the result of DiffReport.as_dict())
and contains every Change record:
review: true marks non-runtime modified functions. These are exactly the functions that appear in “Needs
review” in the changelog. carried_name is non-null when _carry_symbol() wrote a symbol for
this pairing.
Provenance economy position of diff-carry
diff-carry sits at rank 40 in the provenance hierarchy: below oracle (90), export (60),
and import (55), but above agent (30). In practice this means:
- An Oracle re-identification pass on v2 will upgrade a diff-carry annotation if the function still hits a corpus signature at score ≥ 0.82.
- An agent pass will not overwrite a diff-carry annotation, regardless of claimed confidence.
- A
warden set-namecall (provenancehuman) always wins.
Core concepts
Stable identity, the shared fingerprint engine, and the provenance economy: the three ideas
behind how carry-over works.
CLI reference
Full flag documentation for
warden diff and every other command.