Richard brought a bug with its measurement attached: every CIViC variant entity was named with its gene twice, BRAF BRAF V600E, including CAID:CA123643, Maureen’s somatic BRAF V600E. The cause was a single line in the connector. CIViC’s 2025 schema names evidence by molecular profile, and a profile name already starts with its gene. The old variant column held only the change, so the connector’s gene prefix had once been right, and it outlived the schema that justified it. The fix uses the profile name as it is. Most of the session went on two other questions: what the doubled names had already broken, and how to repair 9,179 stored rows without guessing.
Three consumers turned out to read variant names. The OncoKB annotator falls back to splitting the entity’s name when no row on the entity carries gene metadata, which is true of every CIViC-only variant. BRAF BRAF V600E splits into the alteration BRAF V600E. Its disk cache showed what OncoKB made of that: 3,644 such queries, none recognised, and in one twin pair the same BRAF V600E came back Oncogenic with treatments when asked properly and Unknown when asked with the gene inside. The patient graph’s variant match is an exact name comparison. Replaying it against her alteration rows found one, a bare BRAF V600E written on 2026-05-29 with no coding change. It matches nothing today and would match CA123643 once repaired. Her last materialisation predates that row, so the miss is latent. Her existing BRAF link came through ClinGen, from a different report row. The Explorer search is a substring match, so it still finds doubled names, but it ranks exact names first. 137 CIViC variants lose to a higher-degree superstring, and a search for KRAS G12, which is not one of her variants, resolves to KRAS G12D. Training is untouched: the export keys triples on CURIEs.
The repair was meant to remove exactly one leading gene, and to refuse any name still doubled after that removal, so a second run could never strip twice. On the first prod dry run it refused twelve rows: six CIViC rows and their six entities, all stored tripled (TGFBR3 TGFBR3 TGFBR3). The on-disk nightly explained them. CIViC’s own profile names repeat the gene for these six (TGFBR3 TGFBR3, AR AR alternative transcripts), so removing one prefix was correct, but the repaired name would still look doubled. A name’s shape cannot say whether a row has been repaired. The design changed. The connector now records the profile name as source_metadata.molecular_profile, the repair stamps the same key on every row it renames, and a stamped row counts as current. The nightly could not be the arbiter either: it is a newer release than the one stored, and CIViC has since renamed one of the six. Without that refusal, the first apply would have failed its own post-check and rolled back. The second dry run counted 9,179 rows and no problems.
One correction of my own. Before writing the script I counted the other sources’ rows that had copied the doubled entity name, with an ad-hoc join, and filed 923. The script’s exact count is 917. My join counted six ClinVar rows twice, on entities that carry two CIViC rows. The issue now carries both numbers and the reason.
PR #725, which renames CIViC’s raw predicates, edits the same connector and test file, so I kept my hunks clear of its lines and ran the tests both PRs touch on a trial merge of the two. When I opened the PR, nothing had been written to prod. OncoKB re-annotation stays a separate step, because it calls the API and adds relations.
Richard approved the repair later that morning, copies included. The apply renamed 9,179 rows and re-planned inside its own transaction to prove nothing was left. A dry run afterwards found nothing to rename, and CAID:CA123643 now reads BRAF V600E.
Rebuilding Maureen’s graph is where I made a mistake. I previewed the materializer inside a transaction I meant to roll back, so that I could diff its output before committing it. The materializer deletes her edges first and only then spends ten minutes or more resolving names over the proxy, so the preview held row-exclusive locks on four KG tables the whole time. A backend booted meanwhile. Its migration chain holds its locks until it commits. It queued on an unguarded CREATE INDEX IF NOT EXISTS on mods_kg_edge while still holding the SHARE lock it had taken earlier on task_queue. All ten local workers queued behind that lock, and a second boot queued behind the workers. I noticed only because I looked at pg_stat_activity while waiting for the preview. Ending my own session cleared everything within seconds, after about seven minutes of stall, and nothing was lost. A preview that rolls back is still a write transaction while it runs. The unguarded index statements and the materializer’s lock-then-resolve order are now #745.
The rebuild then ran in two passes. The first ran the real materializer on a read-only connection with its writes stubbed. It recorded 1,019 SELECT results and five ClinGen lookups, which also made it an exact preview: 11 edges added, none removed. The second ran the real function with those lookups replayed, under a five-second lock_timeout, and committed in 76 seconds. Her bare BRAF V600E row now links to CA123643 by name. The 11 new edges all come from data that arrived after her May 18 build, among them binimetinib as a current medication and two RGCC results.