The plan was simple. Yesterday’s wider relation key meant a fresh run of each connector would add back the raw predicates the old key had dropped, and ON CONFLICT DO NOTHING would leave everything else alone. CIViC went first, and ClinGen’s allele registry returned a 500 forty seconds in. ClinGen fails the same way every time on three large ENST deletions, so the run aborted before its first batch was written. It left one mark: the failure handler set CIViC’s source status to planned.

Before retrying, I asked something I should have asked before the first launch: would a re-run resolve each endpoint to the entity it is stored on? The ingest asks the cache, ClinGen, Node Normalizer and a name match, and nothing memoizes those answers. A read-only dry run said no for 885 of 6,229 endpoints. JAK2 is the clearest example. In May there was one gene entity named JAK2. Since RTX-KG2 arrived there are eight, the others mouse, rat and other species, so the name match comes back ambiguous and the gene would go synthetic. Bevacizumab now matches a UNII twin of PrimeKG’s UMLS node, glioma hits the MONDO padding twins, and CIViC itself has since assigned CAIDs to variants that are stored on HGVS entities. The re-run would have written 4,960 relations a second time beside the stored ones, and restored 35.

So the restore pins identity. kg_pinned_reingest.py maps each endpoint through its stored (source, external_id) row and lets the resolver’s own upsert_relations build the rows, capturing them in place of the insert. Then it writes only keys that are missing. The test that matters drifts every hint and checks that the restored rows equal the lost ones exactly. With live resolution swapped back in, it fails.

The second mistake was mine and smaller. The loop that applied ClinVar and OncoKB passed the data directory through a double-quoted bash string, the backslashes arrived halved, and \$dir became a literal kg$dir. The connector created that empty directory and fetched every ClinVar summary live, so its 69 rows reflected today’s ClinVar, not the May cache the dry run had checked. I stopped OncoKB before it wrote anything, deleted the 69 by id from a backup and applied the reviewed 22. The script now refuses a missing data directory, and an apply refuses unless its plan matches the dry run’s count.

The final tally is 61 CIViC restorations and 127 new CIViC evidence rows, 11 and 11 for ClinVar, and 67 for OncoKB. All went onto entities the sources already had. What the restore cannot do is refresh. It skips 440 CIViC relations on variants added since May, and it keeps 105 CIViC rows the current nightly no longer asserts. Fixing either means first making live resolution trustworthy, and that is #708.