TOMK / src /python /tom_world /indexes.py
c0mbatduckzz's picture
Publish WQK 0.6 formal specification, use cases and verified release (part 2)
767a051 verified
Raw History Blame Contribute Delete
12 kB
"""Immutable secondary indexes for TOM World & Query Kernel 0.2.
The index is a pure, content-addressed projection of an immutable snapshot's
record map. It contains no timestamps, host paths, object addresses, or
backend-specific ordering. Deleting an index file cannot destroy world data:
``WorldStore.rebuild_indexes`` deterministically reconstructs the exact bytes
from the snapshot's immutable records.
"""
from __future__ import annotations
from bisect import bisect_right
from collections import defaultdict
from collections.abc import Callable, Mapping, Sequence
from typing import Any
from .canonical import attach_hash, canonical_bytes, digest_bytes, verify_hash
from .records import validate_record
INDEX_SCHEMA = "TOM-WORLD-INDEXES-0.2"
INDEX_VERSION = "0.2.0"
def _sorted_map_of_sets(source: Mapping[str, set[str]]) -> dict[str, list[str]]:
return {key: sorted(source[key]) for key in sorted(source)}
def _require_int(value: Any, name: str, *, minimum: int | None = None) -> int:
if isinstance(value, bool) or not isinstance(value, int):
raise ValueError(f"{name} must be an integer")
if minimum is not None and value < minimum:
raise ValueError(f"{name} must be >= {minimum}")
return value
def canonical_index_key(value: Any) -> tuple[str, Any | None]:
"""Return a stable string key plus optional canonical value metadata.
Scalar values remain human-readable. Compound JSON values are keyed by
their canonical SHA-256 and retained once in ``generative_address_values``.
"""
if value is None:
return "null", None
if isinstance(value, bool):
return "bool:true" if value else "bool:false", None
if isinstance(value, int):
return f"int:{value}", None
if isinstance(value, str):
return "str:" + value, None
data = canonical_bytes(value)
return "json:" + digest_bytes(data, prefix=False), value
def record_time_interval(record: Mapping[str, Any]) -> tuple[int, int] | None:
payload = record["payload"]
interval = payload.get("active_interval", payload.get("time_interval"))
if isinstance(interval, Mapping):
start = _require_int(interval.get("start"), f"{record['id']}.interval.start", minimum=0)
end = _require_int(interval.get("end"), f"{record['id']}.interval.end", minimum=0)
if end < start:
raise ValueError(f"{record['id']} interval end precedes start")
return start, end
if record["record_type"] == "event" and isinstance(payload.get("event_tick"), int):
tick = _require_int(payload["event_tick"], f"{record['id']}.event_tick", minimum=0)
return tick, tick
if record["record_type"] == "checkpoint" and isinstance(payload.get("tick"), int):
tick = _require_int(payload["tick"], f"{record['id']}.tick", minimum=0)
return tick, tick
return None
def record_topology_sheet(record: Mapping[str, Any]) -> int | None:
payload = record["payload"]
if "topology_sheet" in payload:
return _require_int(payload["topology_sheet"], f"{record['id']}.topology_sheet", minimum=0)
if record["record_type"] == "instance":
state = payload.get("initial_state")
if isinstance(state, Mapping) and "sheet" in state:
return _require_int(state["sheet"], f"{record['id']}.initial_state.sheet", minimum=0)
return None
def build_index_record(
records: Mapping[str, str],
loader: Callable[[str, str], Mapping[str, Any]],
*,
seed_sha256: str,
) -> dict[str, Any]:
"""Build the exact immutable index projection for one snapshot record map."""
by_type: dict[str, set[str]] = defaultdict(set)
by_dependency: dict[str, set[str]] = defaultdict(set)
relation_by_instance: dict[str, set[str]] = defaultdict(set)
relation_by_support: dict[str, set[str]] = defaultdict(set)
relation_by_compatibility: dict[str, set[str]] = defaultdict(set)
event_spec_by_relation: dict[str, set[str]] = defaultdict(set)
by_generative_address: dict[str, set[str]] = defaultdict(set)
generative_address_values: dict[str, Any] = {}
by_topology_sheet: dict[str, set[str]] = defaultdict(set)
definition_by_hash: dict[str, set[str]] = defaultdict(set)
by_content_hash: dict[str, set[str]] = defaultdict(set)
checkpoint_by_instance: dict[str, list[dict[str, Any]]] = defaultdict(list)
time_intervals: list[dict[str, Any]] = []
for record_id in sorted(records):
object_hash = str(records[record_id])
record = loader(record_id, object_hash)
validate_record(record)
if record["id"] != record_id or record["content_hash"] != object_hash:
raise ValueError(f"record map/object mismatch while indexing: {record_id}")
record_type = str(record["record_type"])
payload = record["payload"]
by_type[record_type].add(record_id)
by_content_hash[object_hash].add(record_id)
if record_type == "definition":
definition_by_hash[object_hash].add(record_id)
for dependency in record["dependencies"]:
by_dependency[str(dependency)].add(record_id)
if record_type == "relation":
instance_id = str(payload["instance_id"])
relation_by_instance[instance_id].add(record_id)
for support_id in payload.get("support_ids", []):
relation_by_support[str(support_id)].add(record_id)
for compatibility_id in payload.get("compatibility_ids", []):
relation_by_compatibility[str(compatibility_id)].add(record_id)
elif record_type == "event_spec":
event_spec_by_relation[str(payload["relation_id"])].add(record_id)
elif record_type == "checkpoint":
checkpoint_by_instance[str(payload["instance_id"])].append({
"tick": int(payload["tick"]),
"id": record_id,
"content_hash": object_hash,
})
if "generative_address" in payload:
key, compound = canonical_index_key(payload["generative_address"])
by_generative_address[key].add(record_id)
if compound is not None:
existing = generative_address_values.get(key)
if existing is not None and canonical_bytes(existing) != canonical_bytes(compound):
raise ValueError(f"generative-address digest collision: {key}")
generative_address_values[key] = compound
sheet = record_topology_sheet(record)
if sheet is not None:
by_topology_sheet[str(sheet)].add(record_id)
interval = record_time_interval(record)
if interval is not None:
time_intervals.append({
"start": interval[0],
"end": interval[1],
"id": record_id,
"record_type": record_type,
})
time_intervals.sort(key=lambda item: (item["start"], item["end"], item["record_type"], item["id"]))
checkpoints = {
instance_id: sorted(items, key=lambda item: (item["tick"], item["id"]))
for instance_id, items in sorted(checkpoint_by_instance.items())
}
record_map = {key: str(records[key]) for key in sorted(records)}
index = attach_hash({
"schema": INDEX_SCHEMA,
"version": INDEX_VERSION,
"seed_sha256": seed_sha256,
"record_count": len(record_map),
"records_map_hash": digest_bytes(canonical_bytes(record_map)),
"indexes": {
"by_type": _sorted_map_of_sets(by_type),
"by_dependency": _sorted_map_of_sets(by_dependency),
"relation_by_instance": _sorted_map_of_sets(relation_by_instance),
"relation_by_support": _sorted_map_of_sets(relation_by_support),
"relation_by_compatibility": _sorted_map_of_sets(relation_by_compatibility),
"event_spec_by_relation": _sorted_map_of_sets(event_spec_by_relation),
"by_generative_address": _sorted_map_of_sets(by_generative_address),
"generative_address_values": {
key: generative_address_values[key] for key in sorted(generative_address_values)
},
"time_intervals": time_intervals,
"by_topology_sheet": _sorted_map_of_sets(by_topology_sheet),
"definition_by_hash": _sorted_map_of_sets(definition_by_hash),
"by_content_hash": _sorted_map_of_sets(by_content_hash),
"checkpoint_by_instance": checkpoints,
},
})
validate_index_record(index, records=record_map, seed_sha256=seed_sha256)
return index
def validate_index_record(
index: Mapping[str, Any],
*,
records: Mapping[str, str] | None = None,
seed_sha256: str | None = None,
) -> None:
if index.get("schema") != INDEX_SCHEMA:
raise ValueError(f"index schema must be {INDEX_SCHEMA}")
if index.get("version") != INDEX_VERSION:
raise ValueError(f"index version must be {INDEX_VERSION}")
if not verify_hash(index):
raise ValueError("index content hash mismatch")
if seed_sha256 is not None and index.get("seed_sha256") != seed_sha256:
raise ValueError("index seed binding mismatch")
indexes = index.get("indexes")
if not isinstance(indexes, Mapping):
raise ValueError("index indexes field must be an object")
required = {
"by_type", "by_dependency", "relation_by_instance", "relation_by_support",
"relation_by_compatibility", "event_spec_by_relation", "by_generative_address",
"generative_address_values", "time_intervals", "by_topology_sheet",
"definition_by_hash", "by_content_hash", "checkpoint_by_instance",
}
missing = sorted(required - set(indexes))
if missing:
raise ValueError("index is missing fields: " + ", ".join(missing))
if records is not None:
canonical_map = {key: str(records[key]) for key in sorted(records)}
if index.get("record_count") != len(canonical_map):
raise ValueError("index record_count mismatch")
if index.get("records_map_hash") != digest_bytes(canonical_bytes(canonical_map)):
raise ValueError("index records_map_hash mismatch")
def ids_for(index: Mapping[str, Any], name: str, key: str) -> list[str]:
values = index["indexes"].get(name, {})
if not isinstance(values, Mapping):
raise ValueError(f"index {name} is invalid")
result = values.get(key, [])
if not isinstance(result, list) or any(not isinstance(item, str) for item in result):
raise ValueError(f"index {name}[{key!r}] is invalid")
return list(result)
def interval_ids(
index: Mapping[str, Any],
start: int,
end: int,
*,
record_type: str | None = None,
) -> list[str]:
"""Return IDs whose indexed inclusive interval overlaps ``[start,end]``."""
_require_int(start, "interval start", minimum=0)
_require_int(end, "interval end", minimum=0)
if end < start:
raise ValueError("interval end precedes start")
entries = index["indexes"]["time_intervals"]
if not isinstance(entries, list):
raise ValueError("time_intervals index is invalid")
starts = [int(item["start"]) for item in entries]
limit = bisect_right(starts, end)
result = {
str(item["id"])
for item in entries[:limit]
if int(item["end"]) >= start and (record_type is None or item["record_type"] == record_type)
}
return sorted(result)
def checkpoint_entries(index: Mapping[str, Any], instance_id: str) -> list[dict[str, Any]]:
mapping = index["indexes"].get("checkpoint_by_instance", {})
if not isinstance(mapping, Mapping):
raise ValueError("checkpoint_by_instance index is invalid")
values = mapping.get(instance_id, [])
if not isinstance(values, list):
raise ValueError(f"checkpoint index for {instance_id} is invalid")
return [dict(item) for item in values]