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_ofon 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>onPOST /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 carriesreplacestoday;limma_ingestand 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
removedis the one that matters: a coordinate that stopped being populated. A re-read lost a fact you may have answered from.changedcarriesrelative_move, because a rounding correction and a restatement are the same boolean and different facts.counts.unchangedis the denominator: 4 changed of 12 is a re-parse, 4 of 4,000 is a correction.linked: falsemeans 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"]])
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).