Interesting work. I’m thinking about a comparison with Adapton-style algorithms. In particular, Adapton implementations tend to store cache information at each node of the graph of cells, rather than relying on a global invalidation signal.
That’s actually how the Glimmer implementation works as well. (As the callout at the end of the post notes, Glimmer is actually directly inspired by Adapton.) Each node gets a “tag,” which is a reference back to the node and its current and previous clock values. The functions I described as markAsUsed and markAsChanged are actually consumeTag and dirtyTagFor respectively. Those cache current and previous clock values for that node. You still need some degree of global invalidation for this use case—so “dirtying” any tag does that—because you have to schedule a re-render of the UI, but there’s no global cache.
An earlier draft included those details, but it got so far into the weeds that it became very difficult to keep the big picture in view, so I ended up cutting them.