Skip to content
← Documentation

docs/world-diff.md


Snapshots and world diff

src/lib/world/snapshot.ts, src/lib/world/diff.ts, tests/world-diff.test.ts

Repository Adapter → Snapshot → World → Projection
                     ↑ cache boundary

The renderer asks world questions. It never asks git questions.

1. Identity decides what can be cached

A world built from repo@revision is immutable — the same revision yields the same facts forever — so the key is the identity and there is no invalidation problem to solve.

The corollary is the load-bearing half. A world whose revision is null cannot be snapshotted at all, because nothing can say whether a stored copy is still that world. The estate is exactly this case: it aggregates live substrates and reports revision: null honestly. store returns NotStored { reason: "no-identity", basis } rather than a silent miss.

So the three-state origin model is not bookkeeping. A system that had faked a revision would have cached a world that quietly stopped being true.

Measured on this repository, 157 buildings:

cold acquisition 1638 ms
from snapshot 9 ms
speedup ×182

The lookup happens after git rev-parse HEAD and before everything else: rev-parse is one process, and it is the only question that must be re-asked to know the answer is still current. The ~1.6 s is dominated by one git show per source file.

A world is a subject

The key was first hand-assembled as kind:name@revision, and the layering guard I.4 rejected it — "a module that needs an identity must call core for it, so there is exactly one implementation to be wrong."

Delegating forced the question the shortcut had skipped: what kind of subject is a world? Core already had the answer — environment, "a context that other subjects exist within", which is exactly what a world is to the buildings inside it. Keys are now environment:repository/name@revision, and a world sits in the same ontology as everything it contains.

The guard did not merely enforce a style. It located a missing entry in the ontology.

2. Attribution: most reported changes are not changes

The obvious diff reports "authentication-service: height +20%." That sentence is usually false about the subject. Height is world-scoped (warrant-audit): it is normalised against the largest value currently observed, so a building grows when something else grows.

A diff that cannot separate those causes is worse than no diff, because it sends a reader to investigate a subject that did not move.

Every reported change is therefore attributed by comparing the underlying magnitude:

magnitude mark cause
changed changed subject — the world really did change here
unchanged changed world-rescale — the frame moved, not the subject
not retained changed unattributable — stated, never guessed

The defect this exposed

Attribution was impossible when it was written: worldFromFacts read repo/bytes and repo/commits-touching, projected them into marks, and then set measurements: []. The magnitudes were discarded at the moment of projection, so no downstream consumer could ever recover them.

That is the fourth self-explanation question — what fact caused this — failing in the world layer rather than the projection layer. Fixed by retaining the measures beside the marks, carrying the observers' own citations through citeAll rather than re-narrating them.

Measured across 12 real commits of this repository

58af62d → c35f2dd, 146 → 157 buildings:

count
buildings reported as changed 146
attributable to the subject 11
frame moved only 135
mark changed subject rescale
height 11 11 0
activity 11 11 0
position 146 9 137

A naive diff would have reported 146 changes. Eleven were real. The other 92.5% are the embedding re-solving around eleven edits.

3. Anchoring is two conditions, not one

Revisions say when; the subject says what. Two revisions of different repositories diff perfectly and mean nothing — every building reads as added or removed because they were never the same city. So anchored requires both a matching origin subject and revisions on both sides.

The comparison is still produced either way. Refusing it would hide a legitimate use; what changes is whether the result may be called a history of one system.

4. Open finding — the embedding is not stable across revisions

Blocks the animation step, recorded rather than fixed.

Between those same two revisions, with the centroid fixed at the origin in both:

distinct move distances 111 of 146
median displacement 104.5 units (city span 300)
max 218.8 units
buildings keeping their distance from centre (±0.5) 9 / 146

So the motion is not a rigid rotation or reflection of the whole cloud — that hypothesis was tested and refuted. Classical MDS genuinely re-solves, and a third of the city's width is the typical displacement for a subject that did not change.

Animating position between two snapshots would therefore show 146 buildings flying an average of a third of the city, where 137 of those motions carry no information. Motion is the strongest visual signal available, and spending it on a solver artifact is the deception class from position-pipeline, not a polish issue.

Animation cannot be built on the current embedding. The candidate remedy is to align each new embedding to its predecessor before rendering, which is a different claim than the one measured here and is not yet tested.