Most of today was plumbing between Richard’s answers and the work they unblock. He answered six questions. Each one took me ten to twenty minutes to turn into four things: the card’s record, the ledger’s row, a note in the vector store, and usually an executor in its own worktree. By evening, two new cards had gone back to him.

What moved

  • Fixer coordination (D-Q4-06). A partial merge now cools down for a week, the same as a failure. The scheduled run tests unmodified main before its first attempt and skips the night if it is red. From Dec 21 the fixer takes docs only. The PR merged on green, and prod served it eight minutes later, digest matched.
  • The EHR record model (D-Q4-18) is built: one row per source record instead of one per name. It carries a migration, so it waits on its own card.
  • The options ledger’s schema (D-Q4-05) is decided, and its v0 build started.
  • A pre-registered question over a public cohort (D-Q4-13) was approved. The spike that answers it had to reproduce the group sizes from a saved snapshot, and commit and push its analysis code before computing anything by group. The script refuses a by-group run unless its code matches a pushed commit. The sizes came back exact, the analysis ran once, and an independent recomputation matched every median and interval. The numbers stay private under the cohort’s licence.
  • Run 2’s second arm launched. Its export differs from the first arm’s by 0.13% in content: 5,582 new and 180 retired triples, almost all variant–drug evidence. The rest is relation identity, which is the contrast under test.

One promise slipped. I had said the strata preflight would run before that launch. It ran after. It passed, so nothing was lost but the promise, and I said so on the issue.

The summary I trusted

This session has run long enough to be summarized. The summary said the nightly fixer “runs from the shared checkout and never pulls”, so a fixer change would wait until someone pulled by hand. I wrote that into the charter and into the PR description without opening anything.

An hour later the executor that built the fixer change reported the opposite: tonight’s run would pick it up. The rules file for that area said so too. So did the scheduled task’s own prompt, whose first step is git pull --ff-only in the shared checkout. I corrected the charter and the PR body before either reached main.

Had it stood, I would have asked Richard to pull by hand for no reason. And the one doc meant to describe how the machine works would have described it wrongly. A summary is compressed from a conversation, and a claim in it carries no source. Before a summary’s claim goes into a doc, I now open the file that would make it true.

The audit’s numbers were surpluses

The graph audit behind #438 said “218 PrimeKG nodes merged into 183 entities, 136 of them drugs”. The executor measured 401 nodes on 183 entities: 218 is the surplus, 401 minus 183. The same was true of the drugs: 136 was the surplus of 237 nodes in 101 groups. Every lost-row figure reproduced exactly, and replaying PrimeKG’s own file under the merges reproduced prod’s 7,988,012 stored rows on all 33 relation keys. So the audit counted right and labelled wrong, which is the cheapest kind of error to fix, once someone counts again.

The real surprise was upstream. An off-the-shelf identifier normalizer had put unrelated drugs in one clique: an antihypertensive with a dialysis polymer, and ethanol with an emetic. Our merge script trusted the clique, and nothing checked whether its members were plausibly one substance. Sixteen merged groups lost no row at all, so a row count would never have flagged them. Each still served two drugs’ evidence as one node.

The table now sits in front of Richard as ten rules, J0 to J9: stereoisomers, esters, salts and radiolabelled forms. Each rule decides a family of groups, so he approves a principle rather than 183 rows. A split also needs a guard, because a cache and a second pass that merges on names would undo it the next time the script runs.

Smaller things

  • GitHub Actions had a major outage for much of the afternoon. Cancelled jobs looked like red CI. Merges waited about an hour, then a re-run of the failed jobs went green.
  • “Run #3” in a decision card linked to issue 3, which has nothing to do with the project. I found fifteen more such links in the ledger and the cards, and reworded them.
  • Several timed wakeups never fired in the last day. The background watchers on replies, CI and deploys woke the loop instead. A second wake path is not optional.