Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 87 additions & 61 deletions scripts/build_series_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -1249,12 +1249,9 @@ def _succeeds_problem(
reserved = _reserved_segment_problem(succeeds.get("concept"))
if reserved:
return f"line {lineno}: succeeds {reserved}"
if self.entry_key(succeeds) == key:
return (
f"line {lineno}: {key} succeeds ITSELF — self-handover "
"would forge consumption and unlock a ceremony-free "
"reclaim of a fresh UUID"
)
# Dimension types must be valid BEFORE any key derivation: a
# string geography would otherwise crash _geo_key instead of
# producing a schema finding.
for what, allowed in (
("geography", ("level", "id", "vintage")),
("entity", ("name", "role")),
Expand All @@ -1264,6 +1261,12 @@ def _succeeds_problem(
)
if domain:
return f"line {lineno}: {domain}"
if self.entry_key(succeeds) == key:
return (
f"line {lineno}: {key} succeeds ITSELF — self-handover "
"would forge consumption and unlock a ceremony-free "
"reclaim of a fresh UUID"
)
predecessor_key = self.entry_key(succeeds)
predecessor = self.latest.get(predecessor_key)
if predecessor is None:
Expand Down Expand Up @@ -1611,6 +1614,71 @@ def claim_uuid(row_uuid: str, key: tuple) -> str:
used_uuids[parsed] = key
return row_uuid

def _plan_enrichment(
prior: dict,
canon_key: tuple[str, str, str],
geography: dict | None,
entity: dict | None,
row_uuid: str,
) -> bool:
"""Plan a retire + succeeds pair when ``prior`` is a genuine
placeholder whose live binding owns ``row_uuid``; True if planned.
"""
prior_own_key = UuidRegistry.entry_key(prior)
owner_key = registry.uuid_owner.get(uuid_module.UUID(row_uuid).int)
prior_geo = prior.get("geography") or None
geo = geography or {}
enrichment_shaped = (
prior.get("status") == "docket-only"
and prior.get("entity") is None
and (
prior_geo is None
or (
(prior_geo.get("level"), prior_geo.get("id"))
== (geo.get("level"), geo.get("id"))
and prior_geo.get("vintage")
in (None, geo.get("vintage"))
)
)
)
if not (
enrichment_shaped
and owner_key == prior_own_key
and registry.is_live(prior_own_key)
):
return False
# Docket-placeholder enrichment: the binding MOVES to the observed
# identity via an explicit retire + succeeds pair. UUID continuity
# is preserved, so no ceremony flag is needed — this holds for the
# first enrichment and for a re-enrichment after a withdraw/reclaim
# cycle (the grammar accepts succeeds on a retired successor key).
plan["enrich_retires"].append(
dict(
_registry_event(
prior["concept"],
prior.get("geography"),
prior.get("entity"),
row_uuid,
),
retired=True,
note=(
"docket placeholder enriched by first observed "
"identity"
),
)
)
plan["mints"].append(
dict(
_registry_event(canon_key[0], geography, entity, row_uuid),
succeeds={
"concept": prior["concept"],
"geography": _identity_geography(prior.get("geography")),
"entity": _identity_entity(prior.get("entity")),
},
)
)
return True

def resolve_uuid(
canon_key: tuple[str, str, str],
prior: dict | None,
Expand All @@ -1620,6 +1688,17 @@ def resolve_uuid(
binding = registry.binding(canon_key)
prior_uuid = prior["uuid"] if prior else None
if prior_uuid and binding and prior_uuid != binding:
# A RETIRED successor returning on the CURRENT placeholder
# lineage is a re-enrichment, not a remint: plan the retire +
# succeeds pair instead of dead-ending on UUID ownership.
if (
prior is not None
and not registry.is_live(canon_key)
and _plan_enrichment(
prior, canon_key, geography, entity, prior_uuid
)
):
return prior_uuid
# The catalog row disagrees with the registry: an explicit,
# gated remint (the curator edited the row's uuid on purpose).
# The replacement must be new to the registry outright.
Expand Down Expand Up @@ -1686,62 +1765,9 @@ def resolve_uuid(
row_uuid = prior_uuid if prior_uuid else str(uuid_module.uuid4())
owner_key = registry.uuid_owner.get(uuid_module.UUID(row_uuid).int)
if owner_key is not None:
prior_own_key = (
UuidRegistry.entry_key(prior) if prior is not None else None
)
prior_geo = (prior or {}).get("geography") or None
geo = geography or {}
enrichment_shaped = (
prior is not None
and prior.get("status") == "docket-only"
and prior.get("entity") is None
and (
prior_geo is None
or (
(prior_geo.get("level"), prior_geo.get("id"))
== (geo.get("level"), geo.get("id"))
and prior_geo.get("vintage")
in (None, geo.get("vintage"))
)
)
)
if (
enrichment_shaped
and owner_key == prior_own_key
and registry.is_live(owner_key)
if prior is not None and _plan_enrichment(
prior, canon_key, geography, entity, row_uuid
):
# Docket-placeholder enrichment: the binding MOVES to the
# observed identity via an explicit retire + succeeds pair.
# UUID continuity is preserved, so no ceremony flag needed.
plan["enrich_retires"].append(
dict(
_registry_event(
prior["concept"],
prior.get("geography"),
prior.get("entity"),
row_uuid,
),
retired=True,
note=(
"docket placeholder enriched by first observed "
"identity"
),
)
)
plan["mints"].append(
dict(
_registry_event(
canon_key[0], geography, entity, row_uuid
),
succeeds={
"concept": prior["concept"],
"geography": _identity_geography(
prior.get("geography")
),
"entity": _identity_entity(prior.get("entity")),
},
)
)
return row_uuid
raise SystemExit(
f"identity {canon_key} would mint uuid {row_uuid}, which "
Expand Down
82 changes: 82 additions & 0 deletions tests/test_build_series_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -2221,3 +2221,85 @@ def test_check_compares_bytes_not_text(tmp_path: pathlib.Path) -> None:
crlf = catalog_path.read_bytes().replace(b"\n", b"\r\n")
catalog_path.write_bytes(crlf)
assert bsc.main(argv + ["--check"]) == 1 # byte drift is drift


def test_builder_plans_reenrichment_of_retired_successor(
tmp_path: pathlib.Path,
) -> None:
# Fourteenth-review follow-up: placeholder A/U1 -> enriched B/U1 ->
# B retired (observation withdrawn) + A reclaimed as U2 -> B returns.
# The builder must plan retire(A/U2) + succeeds(B/U2) instead of
# aborting on UUID ownership before enrichment planning.
fresh = "dddddddd-4444-4444-8444-444444444444"
a_key = {"concept": "census.m3.new_orders", "geography": None,
"entity": None}
b_geo = {"level": "country", "id": "0100000US", "vintage": "current"}
b_ent = {"name": "economy", "role": "aggregate"}
entries = [
_mint("census.m3.new_orders", U1),
dict(_mint("census.m3.new_orders", U1), retired=True,
note="placeholder enriched"),
dict(_mint("census.m3.new_orders", U1, geography=b_geo,
entity=b_ent), succeeds=a_key),
dict(_mint("census.m3.new_orders", U1, geography=b_geo,
entity=b_ent), retired=True,
note="observation withdrawn upstream"),
dict(_mint("census.m3.new_orders", fresh), reclaimed=True,
note="placeholder re-established after handover"),
]
existing = {
"series": [
{
"uuid": fresh,
"concept": "census.m3.new_orders",
"geography": None,
"entity": None,
"aliases": [],
"status": "docket-only",
}
]
}
catalog, plan = _build(
tmp_path,
[_row("census.m3.new_orders", geography=dict(b_geo), entity=b_ent)],
existing=existing,
registry_entries=entries,
)
row = catalog["series"][0]
assert row["uuid"] == fresh and row["status"] == "observed"
retire = next(e for e in plan["enrich_retires"] if e.get("retired"))
succeed = next(e for e in plan["mints"] if e.get("succeeds"))
assert retire["uuid"] == succeed["uuid"] == fresh
# The staged whole must reload: succeeds lands on B's RETIRED key.
registry = _registry(tmp_path, entries)
staged = registry.stage(
plan["enrich_retires"] + plan["mints"] + plan["revives"]
+ plan["supersedes"] + plan["retire_pending"]
)
b_full_key = ("census.m3.new_orders", bsc._geo_key(b_geo),
bsc._entity_key(b_ent))
assert staged.binding(b_full_key) == fresh
assert staged.is_live(b_full_key)


def test_malformed_succeeds_geography_is_a_schema_finding(
tmp_path: pathlib.Path,
) -> None:
# Fourteenth-review follow-up: a non-object succeeds.geography raised
# a raw AttributeError inside key derivation.
path = tmp_path / "registry.jsonl"
base = [
_mint("a.one", U1),
dict(_mint("a.one", U1), retired=True, note="placeholder done"),
]
forged = dict(
_mint("a.one", U1, entity={"name": "economy", "role": "aggregate"}),
succeeds={"concept": "a.one", "geography": "not-an-object",
"entity": None},
)
path.write_text(
"".join(json.dumps(e) + "\n" for e in base + [forged]),
encoding="utf-8",
)
with pytest.raises(SystemExit, match="must be an object or null"):
bsc.UuidRegistry.load(path)
Loading