Skip to content

Count FORMAT value items in the oracle, not only INFO ones - #15

Merged
ecrum19 merged 1 commit into
mainfrom
fix/oracle-format-value-items
Sep 22, 2026
Merged

ecrum19 merged 1 commit into
mainfrom
fix/oracle-format-value-items

Conversation

@ecrum19

@ecrum19 ecrum19 commented Sep 16, 2026

Copy link
Copy Markdown
Owner

The bug

Any VCF with a positional FORMAT key failed validation. The oracle reported the entire value-item layer as unexpected extra rows — 401,606 of them on a 100k-record GIAB file — and 13_query_cost failed 3/3 replicates at its large scale, on all three artifacts (aggregate, hdt, cottas).

This is the mirror image of #14: there the oracle expected triples the graph did not have; here the graph has triples the oracle does not expect.

Root cause

_emit_value_items is called from two places:

site field
vcf_rdfizer.py:4252 INFO
vcf_rdfizer.py:2842 (append_expanded_sample_rdf) FORMAT

emitted_record_counters only ever walked parse_info_entries(info). The FORMAT half was never counted — and format_numbers was already a parameter of that function, never read once. The counting had simply never been written.

The fix

The layer is emitted only by append_expanded_sample_rdf, so it is returned separately and merged by expected_census under representation == "expanded" — exactly as the genotype layer already is. The emitter's two preconditions are mirrored rather than re-derived: at least one ALT, and split_value_items yielding items (a missing . cell yields none).

Why no fixture caught it

HG005_GRCh38_r100000.vcf.gz   FORMAT positional = {AD: R, ADALL: R}   -> failed
test-10k.vcf / test-1k.vcf    FORMAT positional = none                -> passed

The fixtures declare no positional FORMAT key at all, so the bug needed real data to appear. Arithmetic checks out: 2 fields × 100k records × ~2 items (R = ref + each ALT) ≈ 400k, against the observed 401,606.

The guard

EmittedTermCensusCoverageTests diffs every vocab term the emitter produces against everything the oracle counts, classifying each use by its position in the triple so vocabulary objects (e.g. vcfc:ExpandedRepresentation, the object of representationProfile) are not mistaken for census terms. That distinction matters — without it the scan reports 39 false positives.

This is the guard for the whole bug class, which has now bitten twice. It ships with a waiver list of 35 terms that are genuinely unmodelled today:

feature terms
VCF 4.5 local alleles LocalAlleleSet, hasLocalAllele, localIndex, …
phase sets PhaseSet, inPhaseSet, phaseSetId, …
SV / confidence intervals VariantEvent, svClaim, ciLower, endPosition, …
gVCF reference blocks ReferenceBlock, referenceBlockLength, …
tandem repeats TandemRepeatAllele, repeatSequenceIndex, …
Number=M base modifications BaseModification, modifiedResidue, …

Each waived entry is a file shape that cannot pass validation today, so the list is a to-do rather than a licence — phased data (PS) and SV callsets are common, and a gVCF would hit the reference-block cluster. A second test fails if a waived term later becomes counted, so the list cannot rot.

I verified the guard is not vacuous: removing the phase-set terms from the waiver makes it fail with exactly those terms named.

Verification

377 existing tests plus 8 new ones, all passing. Unit tests drive emitted_record_counters directly rather than going through validation_fixtures, deliberately: that fixture is a hand-written mirror of the emitter, so a test built on it can be wrong in the same way the oracle is — which is how this survived.

End-to-end re-run of 13_query_cost on the benchmark VM is in flight; I'll confirm 6/6 before this is merged.

🤖 Generated with Claude Code

A run over any VCF with a positional FORMAT key failed validation. The oracle
reported the entire value-item layer as unexpected -- 401,606 extra
hasValueItem/forAllele/FieldValueItem rows on a 100k-record GIAB file -- and
13_query_cost failed 3/3 replicates at its large scale.

_emit_value_items is called from two places: the INFO pass, and
append_expanded_sample_rdf for FORMAT. emitted_record_counters only ever walked
parse_info_entries, so the FORMAT half was never expected. format_numbers was
already a parameter of that function and was never read once -- the counting had
simply never been written.

The layer is expanded-only, because append_expanded_sample_rdf is the only
emitter of it, so it is returned separately and merged by expected_census under
representation == "expanded", exactly as the genotype layer already is. The
emitter's two preconditions are mirrored rather than re-derived: at least one
ALT, and split_value_items yielding items (a missing cell yields none).

Why the fixtures never caught it: test-1k.vcf and test-10k.vcf declare no
positional FORMAT key at all, while HG005 declares AD and ADALL as Number=R. The
bug needed real data to appear.

Adds EmittedTermCensusCoverageTests, which diffs every vocab term the emitter
produces against everything the oracle counts, classifying each use by its
position in the triple so vocabulary objects are not mistaken for census terms.
This is the guard for the whole bug class -- it has now bitten twice, here and
in the INFO items under raw INFO. It ships with a waiver list of 35 terms that
are genuinely unmodelled today, grouped by feature: local alleles, phase sets,
SV/confidence intervals, gVCF reference blocks, tandem repeats and Number=M base
modifications. Each waived entry is a file shape that cannot pass validation, so
the list is a to-do, not a licence. A second test fails if a waived term later
becomes counted, so the list cannot rot.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 92.79279% with 8 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/validation/validation_runner.py 78.37% 8 Missing ⚠️

📢 Thoughts on this report? Let us know!

@ecrum19
ecrum19 merged commit 90cb06d into main Sep 22, 2026
24 checks passed
@ecrum19
ecrum19 deleted the fix/oracle-format-value-items branch September 22, 2026 14:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants