# ReferentialIntegrity

Import it from `@grafloria/engine`.

Holds the diagram to its one hard invariant, and buffers the ops that cannot be applied
yet.

Drives the model directly. Its writes MUST NOT be captured as ops (they are derived, and
broadcasting them would race with the very ops they are derived from) — the Replica runs
every call into this class with capture suppressed.

```ts
class ReferentialIntegrity
```

**Methods**

- `constructor( private readonly diagram: DiagramModel, private readonly lww: LwwRegistry )`
- `get quarantined(): string[]` — Link ids currently held out of the document. Small; usually empty.
- `isHeld(id: string): boolean` — Is this link being held out of the document?
- `held(id: string): LinkModel | undefined` — The held instance, so a write can still reach a link that is out of the document.
- `release(id: string): void` — Force a held link back into the document, endpoints or no endpoints.

Only for undo: to UNDO the creation of a link that is currently quarantined, the link
has to be in the document for `removeLink()` to be a real mutation that capture can
mint an op from. Without this the undo silently does nothing, the link's presence
register still says "present", and resurrecting its node brings back a link the user
explicitly took back. The following reconcile() re-quarantines it if it is still
orphaned, so this is a keyhole, not a hole.
- `note(op: Op): void` — Keep the port map and the order watermark current with the ops that change structure.
- `divert(op: Op): boolean` — Can this op be applied to the document right now?

Returns `true` when the op was DIVERTED — written into a link this class is holding out
of the document — in which case the caller must not also hand it to applyOp.

A held link still takes its property writes, through the SAME mutators a live one uses. An entity that took a different write path in quarantine would drift from a live one, and
the drift would only surface on release, long after anything could point at the cause.
- `settle(op: Op): void` — The INCREMENTAL invariant check: only what THIS op could possibly have broken.

A full sweep after every local edit is O(links), and a local edit stream is n ops long, so
a bulk load — importing a document into a live session — was O(n²). Measured: 2,000 nodes
and 2,000 links took 8.5 SECONDS through a Replica, against ~90ms without one. Nothing
caught it, because no perf gate in this repo drives a Replica. That is the shape of defect
this codebase keeps shipping, and I very nearly shipped another one.

So each op pays only for what it can actually affect:

add node      → only a QUARANTINED link can become live. The quarantine is almost always
                  empty, so this is free. (A live link cannot break when a node ARRIVES.)
  add link      → that one link. O(1).
  remove link   → drop it from the quarantine. O(1).
  remove node   → live links attached to it can be orphaned. O(links) — but deleting a node
                  is a human action, not a loop.
  set ports     → same as removing a node: an endpoint may have vanished.
  set endpoints → that one link. O(1).

A bulk load is all adds, so it is linear again.
- `reconcile(): void` — Re-evaluate the invariant over the WHOLE document.

Once per BATCH of remote ops, and once at the end of a replay — where the invariant is a
function of the batch's final state, not of the path through it (a link orphaned mid-batch
and re-parented by the end of it was never really orphaned).
- `forget(op: Op): void` — Drop a link from quarantine for good — its presence register says it is gone.
