Richard picked option (a) for #701: one edge per measurement, with the predicate and headline values from the latest report and every report kept on the edge. A read-only dry run on prod showed it doing what was intended. All 135 RGCC edges came from the September draw, paclitaxel read 22 instead of 75, and naltrexone lost its stale second predicate. Two things the dry run settled that the code could not:

  • MDR1 never had an edge to begin with. It doesn’t resolve under that name, so my earlier claim that the graph showed 30 was wrong.
  • A ±1 shift in the predicate counts was entity drift since May, not my change.

I merged #733 and rebuilt her node in one pass. That rebuild held row-exclusive locks on four KG tables for fifteen minutes, and the merge’s own deploy booted into them. The boot’s migration chain waited behind CREATE INDEX IF NOT EXISTS, the startup probe failed, and Cloud Build reported a failed deploy. #745 describes exactly this, and the warning sat in a vector-memory note that surfaced only after I went looking. I also got the timeline wrong at first. created_at defaults to NOW(), which is the transaction’s start, so I read the start as the commit and concluded my locks predated the boot. The task file’s modification time gave the real commit, five seconds before the stuck boot moved again.

Richard said fix it. The fix has two halves.

  • The migrations. A test that runs the chain on a migrated database and lists its blocking locks found far more than the two statements the issue named:

    • task_queue, both KG edge and entity tables, tmf_chunk, the qs_* tables and more;
    • an ANALYZE on every boot;
    • a view re-created on every boot.

    _ensure_index reads the catalog first, and _ensure_view stamps a hash of the view’s DDL in its comment.

  • The materializer. It now makes every lookup before its first write. Variant entities that ClinGen resolves are queued as pending until the write phase, and the edges go in one statement per table. The next rebuild’s write phase took 3.3 seconds, and while it was resolving its connection held only ACCESS SHARE.

Prod’s boot log then said what my test had missed. Two migrations still created indexes and committed on every boot. A commit in the middle of the chain releases every lock taken before it, so my end-of-chain read could not see them, nor the locks of 005–007, which a probe had found separately. The test now reads locks after each migration, and fails if transaction_timestamp() changes. Against main it names 016 and 029.

#752 went in with the same PR: RGCC’s COX2 resolves as PTGS2, and a symbol that matches two genes gets no edge. The rebuild moved her RGCC COX2 result off MT-CO2, which it had been wrongly linked to, and onto PTGS2. The guard found nothing else on her node.

What I would do differently: read the issue a memory note names before running the thing it warns about, and time a database operation from the process, not from a column default.