PROPOSAL 0.7 PT3 — the three items left open after the 2026-09-02 round, planned for build¶
Status: a record. Drafted 2026-09-03, decided with the maintainer the same day, and closed the same day when its third item landed. It no longer wins over the roadmap files: RM174, RM160 and RM171 are all in ROADMAP_HISTORY, and anything still open is tracked in ROADMAP like any other item. Two dated addenda sit at the foot of this file — RM160's authored column pair, which the release-class line below priced at none, and RM171's five departures from the build order.
Scope: three items, all decided, none designed from scratch here. Each already has its shape settled — RM160 by a maintainer choice between three written options, RM171 by a strategy document the maintainer wrote, RM174 by a phase measurement that replaced its own premise. What this file adds is the build: the order, the surfaces each one touches, what a first cut owes, and what it must not do. Nothing below reopens a decision.
Release class: all three are additive and fit the uncut 0.7.0. A new parquet column, a new verification-check member, a new cache lane and a new draft source are every one of them minor-legal under P3/P8. No model field is removed, promoted to required or retyped. The 0.6 charter amendment prices the authored layer: RM174 adds parquet columns only (approximately free), RM160 adds no authored column at all, and RM171 adds no schema — it writes rows into tables that already exist. (RM160's half of that sentence is wrong and is corrected in the addendum at the foot of this file: it shipped with one optional authored column pair. Still minor-legal, still additive; the price was misquoted, not the legality.)
Not in scope, and named so nobody widens into them. RM164 stays parked to 0.8 — the MITOMAP probe confirmed its measured negative rather than moving it, and no source publishes a heteroplasmy level per tissue. RM28 stays parked on its corpus, which RM174 just gave its first real entry. The representation of a two-variant claim is RM28's and is not attempted here.
Build order, and why¶
RM174 → RM160 → RM171, smallest first, and the order is not only size.
RM174 touches civic_build and nothing else; it is a day's work and it makes the CIViC parquet stop
stating something false, which every later CIViC-reading pass inherits. RM160 also reads CIViC and
adds the first live API read to the enricher's CIViC surface, so it wants RM174's columns already in
place — a citation recovered for a combination profile should be able to name the profile it belongs
to. RM171 is the largest by a wide margin (a new lane, a derived child lane, a registry field, a
draft source, a SourceTerms row) and depends on neither, so it goes last where a long build cannot
block two short ones.
RM174 — publish the evidence item's own molecular profile¶
Decided: repair 2 of the two written — keep the fan-out, publish the real profile beside it. Not decided here and not attempted: representing a two-variant claim, which is RM28's.
What it is¶
_submitted_evidence_row stamps molecular_profile_id from the variant's
single_variant_molecular_profile_id, because that column is the join key into the profile map and
the evidence item's own profile would not join. The result is that CIViC evidence item 8721 — one
statement about VHL S183L (c.548C>T) AND VHL D126N (c.376G>A), in trans — is written as two rows
each claiming to be a single-variant refutation, and the column that would have said otherwise has
been overwritten.
The build¶
- Two new parquet columns on
civic.parquet, filled from the CSQ blockCivicVcfEntry.molecular_profile_idalready parses: evidence_molecular_profile_id— the profile the evidence item actually belongs to.evidence_molecular_profile_name— CIViC's own rendering of it, so a reader seesVHL S183L (c.548C>T) AND VHL D126N (c.376G>A)without a second lookup.
On a single-variant row the id equals the existing molecular_profile_id — deliberately, since
a column that is null on the common case invites a reader to treat null as "not a composite", and
the honest statement is that every row knows its own profile. The name is null on a TSV-sourced
row, because MolecularProfileSummaries.tsv publishes none; filling it from the variant's name
would state a profile name the source never wrote.
-
molecular_profile_idkeeps its meaning — the single-variant profile the row was joined through. Renaming it would be a wire break for a consumer that already reads it, and the field is doing a real job. -
A derived count, not a stored boolean. Whether a row is a composite is
evidence_molecular_profile_id != molecular_profile_id— derived-not-stored is the pattern for a convenience number (@derived-not-stored), and a stored flag would be a second thing that can disagree with the two ids. -
release.jsongainscomposite_profile_rows, the count of rows where those two differ, so the class is a published number rather than something each consumer re-derives (@dont-discard-computed). -
The TSV path is left alone, and the asymmetry is documented rather than repaired. A multi-variant profile arriving through the TSV is dropped
combination_profile, counted; through the VCF it is fanned out and now labelled. Making both paths keep it is a wider change that would move an accepted-basis build's numbers, and@parity-by-checksays to audit an asymmetry deliberately rather than inherit it — this entry audits it and leaves it, with the reason in ENRICHER.md.
Tests¶
- Over the real slice: every row's
evidence_molecular_profile_idis non-null, and a row whose evidence item belongs to a multi-variant profile carries a different value frommolecular_profile_idwhile a single-variant row carries the same. release.json'scomposite_profile_rowsequals the count derived from the frame — a relationship, not a copied number.- A rebuild stays byte-identical (P7).
- RM170's check still reports one finding over two subjects, which it must, because it keys on the evidence id and never on the profile column.
What moves¶
artifact.digest for any module compiled against a rebuilt CIViC snapshot, and the snapshot's own
digest. That is expected and is not a reason to defer.
RM160 — the provenance half, at enrich time¶
Decided: shape 3 (read SUBMITTED at enrich time), the recovered citations are drafted,
and each drafted row carries a retrieval pin that a later run re-asks. Shape 1 (hash a capture) and
shape 2 (a second parquet) were both available and not taken.
What it is¶
civic build reads dated files. The wider basis RM169 adopted comes from a VCF, and a VCF record
needs a POS — so submitted evidence attached to a variant with no GRCh37 coordinate is published
on one surface only, the API. That is the class RM160 opened on: ten records whose hidden citations
nothing local can reach, including variant 1955, whose only reachable evidence for a numbering
convention (EID 9969, Dollfus 2002, PMID 12202531, free full text) exists in the API and in no file
the builder reads.
The build¶
-
A per-variant API read in the enricher,
evidenceItems(variantId:, status: ALL), behind the same offline discipline as every other fetch. CIViC is CC0, socheck_declared_usedoes not gate it;--offlinedoes, and the skip isofflinerather than an empty answer (@unreachable-not-absent). -
It drafts
literature.csvandstudies.csvrows for the citations the snapshot's basis does not carry. Appending, never rewriting (@draft-appends), matched on the identity key so a second run adds nothing. -
The canary, which is what makes drafting from a live read honest. Every drafted row records when the API was asked and on which basis, pinned on the row's
SourceRowrather than restated beside it. A later run re-asks CIViC for the same variant and reports when the answer has moved — an item accepted since, rejected since, or newly added. Without that pin an API-drafted row is a claim about a moment nobody wrote down, and the objection to drafting from a live read is exactly that. With it, the row is auditable.
The re-ask is a VALID_VERIFICATION_CHECKS member, warning in both modes, never gating: CIViC
re-curating is not an authoring error (@a-source-recuring-is-not-a-strict-matter), and the two
currency findings stay apart.
-
statusrides asconfidence/confidence_unit, unconverted — CIViC's own instrument named rather than translated into a house grade, which the entry settled before this round. Anacceptedrow and asubmittedrow must not be indistinguishable once both are in a file. -
civic buildandcivic reproduceare untouched. That is the whole point of shape 3: the published snapshot keeps its byte-reproducibility contract and does not grow.
Landed 2026-09-03, as written apart from the one correction in the addendum below. See ROADMAP_HISTORY for what shipped.
What a first cut owes¶
- The skip must distinguish the API said this variant has nothing more from nobody asked. Three outcomes, not two.
- The read is per variant by construction, which is why it fits
enrichand would not fit a build — say so in the docstring, because the first repair anyone proposes is to batch it into the builder. - A pacing gate shared with the other network clients, and a probe that the disabling switch actually
disables (
@off-switch-needs-a-probe).
Tests¶
- A drafted citation carries a retrieval pin, and a second run with an unchanged API adds no row.
- A moved answer fires the currency check and names which variant moved.
--offlinerecordsskipped/offline, neverran, findings=0.- The tautology guard: a module whose CIViC rows were drafted from the same basis is not re-reported against itself.
RM171 — adopt the increment, never the photocopies¶
Decided: build it; both mmutation and rtmutation in the first increment; a published
mitomap-miss child lane; terms authored from the live read of 2026-09-03.
The design is rm171_diff_strategy and is not restated here —
this section is the build order against it.
Why both tables, settled¶
reference_examples/mt_heteroplasmy carries two variants and both live in rtmutation; neither is
in mmutation. An mmutation-only lane would draft nothing the one existing mtDNA module needs,
and the strategy's own warning is that shipping one table and finding the sibling later is how RM164
happened. It is the same join with a second input table, not a second shape.
The build, in order¶
-
MITOMAP_TERMSinlicensing.py, from the live r5 (2026-06-30) read:CC-BY-3.0, commercial and clinical use stated free, attributionMITOMAP (mitomap.org)or Lott et al. 2013 (PMID 25489354),share_alike=False,redistribution=True. Written as a floor — the page says unless otherwise noted, so a per-record note outranks it (@a-hosts-terms-are-not-its-contents-terms). The compile gate does not fire. -
A
mitomapcache lane. Acquire is the dump (byte-identical to what RM164 held, no interstitial on the data surface). Build extractsmmutation,rtmutation,mmutation_reference/rtmutation_referenceandreference.SourceRow.datasetpins the dump'sLast-Modifiedand sha256. -
A
parentsfield onCacheLane— a tuple of lane names, empty for the twelve that shipped with RM176 — plus a guard that a child cannot rebuild before its parents, and a rebuild outcome ofbuilt=Nonewhen either parent is absent. Additive to a registry that shipped yesterday. -
The
mitomap-misschild lane. Not a download: its acquire stage is both parents on disk. Its build is the join on exact(chrom="MT", start=position, ref=refna, alt=regna)against the ClinVar chrMT parquet, upper-cased both sides, no position-only matching. Itsrelease.jsonpins both parent digests, so a ClinVar rebuild without a child rebuild is detectably stale. -
Three buckets, and only one of them drafts: photocopy (in ClinVar — nothing, the VCEP call is already adopted with ClinVar's provenance); rated miss (absent, bracket in the five documented classes — a
variants.csvrow withclin_sigfrom the existing normalizer); unrated miss (absent, no mappable bracket — counted, noclin_siginvented). -
draft --source mitomap-miss, appendingvariants.csv+studies.csv+ theSourceRow.genotypeis stubbed with a before-validator the author must replace (@stub-cannot-compile) — MITOMAP publishes no called genotype and never will, so a MITOMAP-drafted module cannot compile until a human writes those cells. That is honest, and it is the cost of this adoption.
What this must not do¶
Straight from the strategy, and each is a test:
- Never map
Reported/Cfrm/Conflicting reportsontoclin_sig. MITOMAP states in as many words that its confirmation token is not an assignment of pathogenicity. - Never map
VUS*. It is undocumented, it is not one of the five VCEP classes, it is not the legend's diamond, and it is not APOGEE'sVUS+/VUS-. Withhold, count, and revisit if a legend turns up. - Never draft a photocopy so a concordance check has something to disagree with — that is a tautology
against a source this repo already adopts (
@tautology-zero). - Never left-anchor the
:deletions in format or compiler. That needs an rCRS base atposition-1and Principle 2 forbids those tiers from fetching. They stay in the unmintable count. - Never put a count in a constant. "16" is a fact about one ClinVar vintage, not about MITOMAP. The first build owes a rejoin against the ClinVar cache of that day, and every test asserts a relationship rather than a number.
What a first cut still owes, carried from the strategy's §7¶
nlmid was verified as a PMID on 4 of 4 sampled values and is empty on 397 of 6,770 reference rows —
the column has to be walked before the lane claims "every citation". The 250 unbracketed rows absent
from ClinVar are an identity increment with no mappable class, counted as unrated miss, and whether
their identity earns a row at all is a second, smaller call that can wait.
What "done" looks like for this round¶
Each item lands as its own commit block — behaviour with its test, doc and changelog line — and moves from ROADMAP to ROADMAP_HISTORY with its RM_TOC row restated. When all three have landed this file becomes a record, and the next open item is tracked in ROADMAP like any other, not by this proposal.
One thing that is not code and is owed to whoever publishes: the ClinPGx HuggingFace snapshot is
still the 2025 parquet until clinpgx build + clinpgx publish run. RM175 rebuilt the builder, not
the published artifact. Publishing is outbound and stays the maintainer's.
Addendum, 2026-09-03 — RM160 shipped with an authored column pair, which this file priced at none¶
The release-class paragraph at the top says RM160 "adds no authored column at all". It shipped with
two: StudyRow.confidence and StudyRow.confidence_unit, optional, 0.7.0.
Why the estimate was wrong. The item's own point 4 requires status to ride as
confidence/confidence_unit — a requirement settled before this round, in RM160's entry, and
restated here as non-negotiable. What nobody checked while writing the release-class line is that no
authored model carried that pair. ClinSigAuthorityCallRow has it, and that is a machine-written
concordance row about clinical significance, not a citation. So the requirement and the price were
written a paragraph apart and could not both be true, and the one that had a test attached won.
The legality does not move. A new optional column is minor-legal under P3/P8; nothing was removed,
promoted to required or retyped. content_signature does not move for a module that fills neither
cell — asserted by hashing a module that declares the columns empty against one written before they
existed — and the parquet the pair lands in is studies.parquet, which RM140 had already moved on the
ten reference examples carrying one, inside this same uncut 0.7.0. The 0.6 amendment's price is real
and was paid: the authored layer is full cost, and the gate it asks — will this burden the author? —
is answered by both cells being optional and by the model refusing only the incoherent combination.
The rule this records, since a closed proposal is closed against reopening its decisions and not against recording one taken inside the same release. A release-class line that prices an item at "no authored column" is a claim about the schema surface as it stands, and it has to be checked against the models rather than inferred from the item's shape. This one could have been settled by a single grep for the field name.
Addendum, 2026-09-03 — RM171's five departures from the build order above¶
The item shipped as written in every decision it was given: both tables, a published mitomap-miss
child lane, terms from the live r5 read, [VUS*] withheld, the confirmation token never mapped, no
photocopy drafted, the : deletions left unanchored, and no count in a constant. What follows is
recorded because a closed proposal is closed against reopening its decisions, never against recording
what the build had to do differently — and a silent contradiction of a build order is worse than a
noisy one.
-
The lane is
mitomap_miss;mitomap-missis a spelling, accepted everywhere and folded to it. Point 4 above names the lanemitomap-miss. The registryCacheLanelives in is walked by identity —resolve_<name>_reference,<NAME>_SUBDIR,<name>_build.py— so a hyphen cannot be a lane name anddrug_labelsis the standing precedent.caches.lane_namefolds-to_and returns the declared member for--only,--pin,--sourceanddraft-panel --source(@vocab-separator-slip: a caller who merely calls a normalizer and keeps their own string has done nothing). -
The command is
draft-panel --source mitomap-miss, not a baredraft --source. Point 6 and the strategy's §5 both writedraft --source mitomap-miss.draftis the CPIC command and takes a required--gene;draft-panelis the one that writesvariants.csv+studies.csvfrom a--sourcevocabulary, which is exactly the shape described.--genebecame optional for this source alone and is still refused as absent for the other three: the increment is asked for as a whole, where an unfiltered ClinVar draft would be the whole 4.4 M-record snapshot. -
datasetcomes from the dump's ownedit_date, not from HTTPLast-Modified. Point 2 says theSourceRow.datasetpins the dump'sLast-Modified"the same way ClinVar recordsclinvar_file_date" — and those two halves point in opposite directions, because ClinVar's label is##fileDate, a statement the file makes about itself. The precedent won: a build frommitomap build --dump <local file>now produces the same label a downloaded one does, where a header-derived label would have left every off-switch build unlabelled and incomparable. Both tables' dates, because both are adopted and they are curated separately; the header and the sha256 are recorded inrelease.json, where provenance of the fetch belongs. -
The derived child carries its own citations parquet, for the non-photocopy rows only. Nothing in the plan called for it. Without it the drafter needs both parents at draft time, which lets a draft run against a MITOMAP snapshot that is not the one the join used — the exact class of staleness the parent pin exists to make visible.
-
STATE_BY_CLIN_SIGmoved toclin_sig.py. Not in the plan, and forced by it:pubmind_draftwas already importing the map out ofclinvar_draft's private namespace, and a third drafter folding the same call made the private home indefensible on the shared normalizer's own argument.
And the finding the first build owed, which reshapes §7.2 of the strategy without contradicting it.
The rejoin against the ClinVar of the build's own day (clinvar_file_date 2026-06-27, 3,104 chrMT
alleles) gives a rated miss of six, not sixteen. All sixteen of the probe's bracketed-and-absent
mmutation rows reproduce exactly — but thirteen of them are : deletions, which the strategy's own
§6 puts in the unmintable count until an enricher pass anchors them against the rCRS. Three insertions
survive from mmutation and three more come from rtmutation. So the number the item was filed about
was never sixteen new draftable calls; it was sixteen rows, thirteen of which the same document says
this tier may not mint. That is the sharpest available argument for the rule the item shipped under —
the number is derived on every rebuild, never stored — and it is why "16" was right to keep out of a
constant even before either parent moved.