ADR-080: Persisting the Advanced-DWARF Extraction Status¶
Date: 2026-10-12
Status: Accepted — implemented. The in-memory status was implemented
earlier; the status is now persisted as dwarf_advanced.extraction in schema
v68 (from v67), with the maintainer's decisions D3 (one-time digest change at
the bump) and D4 (a mixed-provenance note, not a gate) recorded below.
Drafted for the known gap
advanced-facts-collected-infers-advanced-dwarf-extraction-ran,
which is now FIXED.
Context¶
AdvancedDwarfMetadata.extraction (not_run, completed, failed, plus
unrecorded for objects that never recorded an outcome) is set by the
extraction itself. model.dwarf_facts.advanced_facts_collected reads it, so a
completed parse that found nothing is distinguishable from a run that never
happened, and a failed read is never reported as an empty surface. That part is
live for every snapshot built in the same process as its compare.
It was not persisted as of 2026-10-12 (schema v67): storage/snapshot_encode.py dropped the key
(_drop_advanced_extraction_status). The reason is the rule in AGENTS.md and
the known-gaps write-up: the encoder walks every dataclass field, so adding the
field without dropping it would add a key to every stored document, and that is
a snapshot-schema change, which needs an ADR and a migration. This keeps the
stored document byte-identical to v58.
The cost is real. A snapshot that goes through dump to a file and is later
loaded by compare decodes with unrecorded, so the predicate falls back to
the payload inference the known gap describes. That inference is sound for
every shape reachable from a stored snapshot today, except one: a successful
parse that found nothing and whose target_arch is empty. For that shape the
stored snapshot still reads "not collected", so the detector is skipped on both
sides without saying why.
Options considered¶
- Keep the drop; accept the stored-snapshot gap. Current state. It is correct and changes nothing, but it leaves the failure mode the known-gap entry names in place for every file-based compare.
- Persist
dwarf_advanced.extractionwith a schema bump. Proposed. It closes the gap for stored snapshots, and it is the only option that does. - Derive the status at decode time from a new
target_archsentinel. Rejected. It would maketarget_archcarry a second meaning, which is the inference the known gap exists to remove, moved to a different field.
Decision (accepted 2026-10-29)¶
D1. One persisted key. dwarf_advanced gains extraction, written only
when the status is not unrecorded. A DWARF dump always records completed
or failed, and a presence-only dump records not_run, so in practice the key
appears on every DWARF-derived snapshot. BTF, CTF and PDB objects stay
unrecorded and write no key.
D2. Absent means unrecorded. A pre-v59 document, and any v59 document
without the key, decodes to unrecorded. The reader then uses the existing
inference, so a stored snapshot changes its reading only when it carries a
recorded status. This is the same convention ADR-079 uses for export_read.
D3. Snapshot digests change once (accepted). The content digest covers the encoded document, so every DWARF snapshot's digest changes at the bump. Snapshot caches are keyed by schema version, so this invalidates them once rather than producing a silent mismatch. Maintainer decision: accepted. The bump is v67 to v68 (the schema version this repository is at; the v58 to v59 numbering in the draft is stale).
D4. Comparability when the two sides disagree (maintainer decision). A
stored completed side paired with a legacy unrecorded side is compared the
same way the detector compares them now: both must be collected for the
detector to run. The open question is whether a pair where one side is
unrecorded and the other is completed should be flagged in the assurance
output as a mixed-provenance comparison. The proposal is a note, not a gate and
not a new exit code.
Chosen: the note. The recommended option is taken as proposed. A pair with
one side unrecorded and the other completed, with both sides carrying
advanced DWARF, keeps the detector running on both sides (no gate, no exit
code change). analysis_assurance's DWARF-context notes name the
unrecorded side in a mixed provenance note, and the context status stays
clean, since nothing was skipped. Implemented in
policy/analysis_assurance/_context.py (_advanced_provenance_notes).
Migration and compatibility¶
- Schema v67 to v68, one additive nested key (
dwarf_advanced.extraction). No top-level key is added, so the section allowlists (legacy_sections,sparse_section_codec) do not change. ADR-079, which drafted a shared v59 bump, is still Proposed and did not take a bump, so v68 carries only this field. - A pre-v68 reader drops the
extractionkey silently and reads the inference, the same result as before. So an old reader is not made less correct, and the key needs no_MIN_SCHEMA_VERSION_REQUIRING_HARD_REJECTIONentry. - No default, exit code, CLI flag or report field changes. The one report change is the D4 note, which only appears for a mixed-provenance pair.
What this ADR does not decide¶
- Whether
unrecordedshould be retired entirely. It stays for hand-built objects and for BTF/CTF/PDB until those producers record their own status. - Per-compile-unit failures inside a completed parse. A skipped CU still reads
completedtoday, because the parse walked the file. Recording a partial parse would be a fourth status and is a separate decision.
Verification (done)¶
- Round trip per status,
completed/failed/not_run, as a case oftests/unit/storage/test_snapshot_field_roundtrip.py(KNOWN_LOSSYis now empty, ceiling 0);unrecordedwrites no key (tests/test_advanced_extraction_status.py). - Digest: a recorded
completedsnapshot and itsunrecordedtwin differ; two identicalcompletedsnapshots agree (tests/test_advanced_extraction_status.py). - Reader: a pre-v68 document (no key, schema 67) decodes to
unrecorded, and a garbled status is refused (tests/test_advanced_extraction_status.py). - Live versus stored:
tests/test_live_stored_advanced_dwarf_parity.py, a realgcc -gpair.