Skip to content

Supersession

One document occupies one set of addresses. When a new version arrives, the old generation is retired — its row and its facts leave is_current — so the address space holds one live generation and a count stops summing them. Nothing is deleted: every retired row stays readable with its lineage stamped, so an answer you gave from the old generation can still be audited.

How a generation is replaced

The store cannot tell a correction from an unrelated document with a reused name, so it never guesses. Two rules:

  • Identical bytes re-uploaded return the source you already hold — the reply carries its source_id, nothing is re-extracted, and no second row is written. The fold is logged by the server, not recorded in the store: duplicate_of on a generation entry is reserved for that record and is "" on every entry today.
  • Different bytes are a new document unless you say otherwise: replaces=<old source id> on POST /v1/public/connect (limma_connect) states that a re-pull is a new version of a named source, and the old generation is superseded. It is the only public verb that carries replaces today; limma_ingest and the document upload route do not, so a corrected document uploaded through them is a second document with both generations live until item 5.1 of the build (profile re-upload → generation supersession) lands.

Which generations exist: limma_generations

You hold whatever generation was current when your receipt was written. This is how you find out the ground moved under an answer you already gave.

r = client.get("/v1/public/generations", params={"workspace_id": WS, "source_id": OLD})
chain = r.json()["generations"]
print(len(chain), "generations, oldest first")
for gen in chain:
    print(gen["filename"], "| current:", gen["is_current"],
          "| superseded by the new one:", gen["superseded_by"] == NEW,
          "| duplicate_of:", repr(gen["duplicate_of"]))
2 generations, oldest first
acme_q1_2024.txt | current: False | superseded by the new one: True | duplicate_of: ''
acme_q1_2024.txt | current: True | superseded by the new one: False | duplicate_of: ''

superseded_by is the only lineage pointer the store writes today: it answers "where did my fact go" with replaced by this generation. duplicate_of is "" on every entry until the store records folds (see the identical-bytes rule above); do not read its emptiness as "this upload was not a duplicate".

What changed: limma_diff

The verb to call before re-using an old answer.

r = client.get("/v1/public/diff", params={
    "workspace_id": WS, "from_source_id": OLD, "to_source_id": NEW})
diff = r.json()
print("linked:", diff["linked"], "| counts:", diff["counts"], "| truncated:", diff["truncated"])
for change in diff["changes"]:
    move = round(change["relative_move"], 4) if change["relative_move"] is not None else None
    print(change["kind"], "|", change["coordinate"]["attribute"],
          "|", change["before"], "->", change["after"], "| relative_move:", move)
linked: True | counts: {'added': 1, 'removed': 1, 'changed': 1, 'unchanged': 1} | truncated: False
removed | headcount | 4300.0 -> None | relative_move: None
changed | revenue | 310000000.0 -> 312000000.0 | relative_move: 0.0064
added | orders | None -> 95000000.0 | relative_move: None

  • removed is the one that matters: a coordinate that stopped being populated. A re-read lost a fact you may have answered from.
  • changed carries relative_move, because a rounding correction and a restatement are the same boolean and different facts.
  • counts.unchanged is the denominator: 4 changed of 12 is a re-parse, 4 of 4,000 is a correction.
  • linked: false means you diffed two unrelated documents — arithmetically fine, and it means something completely different.

The count follows the live generation

r = client.post("/v1/public/facts", json={
    "workspace_id": WS, "question": "revenue", "mode": "enumerate",
    "coordinate": {"attribute": "revenue", "period": "Q1 2024"}, "output_mode": "compact"})
body = r.json()
print(body["enumeration"]["exist"], "live fact(s);",
      "from the new generation:", [f["source_id"] == NEW for f in body["facts"]])
1 live fact(s); from the new generation: [True]

A receipt against a retired generation

A receipt names its generation by source_id. limma_region still reads a retired generation's bytes — they were never deleted — but a span that no longer fits its frame is refused rather than clamped (see a receipt). Call limma_generations on the receipt's source_id: is_current: false means the answer you gave from it needs limma_diff before it is re-used.

As-of

Every count on every public verb is over the current generation of each document; no verb takes a generation as-of. limma_search does take an as_of month, and it means something else: the month relation rows are tested at — a role is active in it, a screening had happened by it (search) — over the current generations. Pinning source generation ids in attestations and answering as of a generation are item 1.8 of the current build and are not described here.

A relation source has generations too: a re-sync through limma_connect with replaces retires the old pull, and limma_diff between the two names each changed slot of each changed row (corrections, generations and erasure).