The fix that should have worked made things worse
After the reward-dedup fix from a couple days ago, one known problem was still sitting there: the same edge in the graph can get pulled into service by more than one context, and a single weight on that edge can’t represent what it means in each context separately. Push the weight up for one context and you’ve silently pushed it up for all the others too, whether that’s right for them or not.
The fix looked obvious: make the update context-aware. Instead of nudging one flat number, nudge it in a direction computed from the context that’s actually active. I was confident enough in the design that I expected this to be the milestone where the graph channel finally pulled ahead cleanly.
It didn’t. And the way it failed turned out to matter more than a clean pass would have.
The first run lied, and the instrument caught it
Before trusting any accuracy number out of this kind of experiment, I log the polarity of every reward update — how many positive vs. negative events fired, and whether their magnitudes balance the way the math says they should on a healthy channel. That check exists specifically because a channel can look like it’s learning while quietly measuring something else, which is exactly what happened three milestones ago.
First full-dose run of the context-aware update: 57% of the polarity log rows for the new update path had event counts recorded but no magnitudes — a defective logging path, not real data. A second arm, run with shuffled context as a placebo control, showed a 67% imbalance between positive and negative magnitude on a code path that should have been symmetric by construction.
Verdict on that run: void. Not “worse,” not “better” — untrustworthy, thrown out without being interpreted. The accuracy numbers underneath it were pointing in a bad direction anyway, but a broken instrument doesn’t get to cast that vote.
The rerun, and the real number
Fixed the logging, reran. This time the instrument came back clean — polarity balanced to zero imbalance, placebo arm quiet, no non-finite values anywhere in half a billion update events.
| arm | walker score | vs. no-learning baseline | read |
|---|---|---|---|
| baseline (no learning) | 0.095716 | — | reference |
| reward-dedup only (prior fix) | 0.103748 | +0.0080 | known-good, reproduced |
| context-aware update | 0.071285 | −0.0325 | fails badly, clean instrument |
| shuffled-context placebo | 0.092369 | −0.0114 | quiet, as expected |
The context-aware update didn’t just fail to beat the existing fix. It landed below the walker that never learns anything at all. Whatever the update was doing when it fired, it was actively wrong more often than not.
Digging into why: 462,685,018 of the update’s own trigger events resolved to a zero-direction no-op — the context needed to compute a direction was empty or wildcard almost every time. So the mechanism barely acted. On the times it did act, it moved the wrong way.
What I don’t know yet
The failed direction is closed on this substrate, pre-registered and instrument-clean, which is the outcome I actually wanted even though it isn’t the one I expected going in — a clean no beats a dirty maybe every time.
The next attempt takes a different shape: instead of one shared weight nudged by context, let the edge remember which contexts it’s actually served well, and bias selection toward the context that worked last time rather than trying to compute a correction in the moment. Early read at small scale: +0.0040 over the reward-dedup baseline, inside the pre-registered ±0.005 noise band — not a result yet, just a lean. Whether it holds at full scale is the open question, and this time I’m not going to guess the answer before running it.