/** * Drift observation over the binding registry (A5+A16, verdict #11). * * A long-lived process may keep `lastObservedRevision`, but it may use it for * exactly one thing: comparing against the revision of a FRESH read. What it may * never do is stand in for the current fact -- the current fact always comes from * a fresh read, and after a drift is reported the CURRENT value is the one that * gets used. Nothing here infers who changed it or which project it now belongs * to; the fact stated is "changed since last observed", with the revisions. * * The same drift is never reported twice: the observation is updated on every * read, so a second read of an unchanged world reports no change. And a first * observation has no baseline, so it never reports drift either. */ import type { Binding, BindingStore } from "../core/binding.ts"; export interface DriftObservation { /** The binding as of THIS fresh read. Use this; never the remembered one. */ current: Binding | null; /** * True when this is not the first observation and the durable revision differs * from the previously observed one. A legacy binding without a revision pairs * as `null -> N`, which is still a reportable fact (provenance appeared). */ changedSinceObserved: boolean; /** The revision previously observed, when one was recorded. */ fromRevision: number | null; /** The revision on disk now, when the entry carries provenance. */ toRevision: number | null; /** What this process remembered before this observation (diagnostic only). */ lastObservedRevision: number | null; } export class BindingObserver { private readonly observed = new Map(); /** Fresh read + comparison against what this process observed before. */ observe(store: BindingStore, localSessionId: string): DriftObservation { const current = store.findBySession(localSessionId); const toRevision = current?.revision ?? null; const lastObservedRevision = this.observed.has(localSessionId) ? (this.observed.get(localSessionId) ?? null) : null; const firstObservation = !this.observed.has(localSessionId); const changedSinceObserved = !firstObservation && lastObservedRevision !== toRevision; const fromRevision = changedSinceObserved ? lastObservedRevision : null; // Update the baseline AFTER computing the report, so the same change is // never reported twice and a settled state reports no drift at all. this.observed.set(localSessionId, toRevision); return { current, changedSinceObserved, fromRevision, toRevision, lastObservedRevision }; } }