Files
rune/scripts/gen_abstraction_truth.py
T
Nick 180d2bb8b4 feat(abstraction): port to FULL Go — dichotomy wave
Full name-preserving port of src/rune/abstraction.py to src/go/abstraction
(GO verdict, #2479 dichotomy tranche — 17/17 fns, dichotomy audit now FULL):
- ExtractFacts: Hindsight what/when/where/who/why prompt byte-parity
  (incl. existing-facts context), Mem0 ADD/NOOP via sha256[:80-lower][:16]
  content-hash dedup, entity upsert/linking with fact_count, HippoRAG
  who+first-3-words triples, LLM-array extraction (first '[' to last ']',
  JSONDecodeError -> []), deterministic kernel fallback seam
  (cc_abstraction_extract_facts with sorted stopword table + min_len=10,
  blank items skipped, text/words/content_words -> experience fact with
  kernel:true), silent kernel-failure degrade, raw[:200] error row
- SynthesizeObservations: >=3-fact gate, facts_text [type] lines, DESC
  created_at source_fact_ids, confidence default 0.7, observation_count
  update; ListBeliefs/Rules/Skills (confidence/success_count DESC + limit);
  RunLifecycle (30-day/access_count/confidence<0.5 gates + superseded
  observations, changes() accumulation); PipelineStats (7 tables,
  active<=state tables); HandleHook
- CtxBudget over frozen DEFAULT_PROFILES snapshot; Now() Python
  isoformat semantics (microseconds only when nonzero); cc_loader_pin env
  pin + kernel probe seam; connect() schema 1:1 (WAL, 8 tables, 8 indexes)
- LLM payload byte-parity (temperature 0.3, num_predict, num_ctx via
  CtxBudget); OLLAMA_URL seam; transport failure -> logged + ""
- RunCLI: main() exact stdout incl. argparse usage/help/error text frozen
  from the oracle (COLUMNS=80), --json via Python json.dumps(indent=2)
  byte-parity encoder (pyjson.go, ensure_ascii=True, compact + indented)
- truth.json: 77 frozen rows from the live oracle covering EVERY public
  function and every branch (LLM valid/prose-wrapped/unterminated/empty/
  dedup/short/hash-80-truncation, kernel fallback/raise/none, prompt-bytes
  captures, payload captures, lifecycle gate matrix, CLI branches incl.
  help/bad-choice/usage) — replay 77/77 exact-equal, live-regenerated and
  re-verified (frozen == live); go test green; gofmt+vet clean
- IO (sqlite via modernc.org/sqlite, LLM transport, clock, env, kernel)
  behind injectable seams; scripts/gen_abstraction_truth.py committed
- CI: HARD abstraction FULL-port parity gate (replay + test, not
  swallowable)
- abstraction.py untouched (Python oracle remains the parity authority);
  stale batch-28 extractor skeleton superseded by the full port

Dichotomy audit: abstraction 17/17 → FULL (27%→28% function-level).

Contract: BUILDER-2026-10-11T13:36:04Z-c0db01-factory-wave (phase A of
factory protocol v2; improvement commit follows before closure).
2026-10-11 15:43:11 +01:00

661 lines
34 KiB
Python

#!/usr/bin/env python3
"""gen_abstraction_truth.py — freeze truth from the live Python oracle
(src/rune/abstraction.py) for the Go dichotomy port.
Exercises EVERY public function with real inputs across ALL branches:
ctx_budget, stopwords table, cc_loader_pin, kernel probe, _now format,
connect schema, llm_call transport+payload, extract_facts (LLM valid /
dedup / short / kernel fallback / no-kernel / empty-array),
synthesize_observations (missing / <3 / ok), list_beliefs/rules/skills
(ordering + limit), run_lifecycle (all archive gates), pipeline_stats,
handle_hook, and the full CLI surface (byte-exact stdout/stderr/exit).
"""
import contextlib
import io
import json
import os
import sqlite3
import sys
import tempfile
import types
import urllib.request
from datetime import datetime as _real_dt
os.environ["COLUMNS"] = "80" # argparse usage wrapping must be stable across re-runs
sys.path.insert(0, "/mnt/workdrive/rune/src")
FIXED_TS = "2026-10-01T00:00:00+00:00"
UNREACHABLE = "http://127.0.0.1:9"
import rune.abstraction as ab # noqa: E402
import rune.cc_loader as rcl # noqa: E402
# freeze the clock for every insert the oracle performs
_ORIG_NOW = ab._now
ab._now = lambda: FIXED_TS
os.environ["RUNE_COGNITION_CORE"] = "python" # pin kernels OFF by default
rows = []
def row(name, op, setup, want):
rows.append({"name": name, "op": op, "setup": setup, "want": want})
# ── ctx_budget ──────────────────────────────────────────────────────────────
for mid in ("qwen2.5:3b", "mimo-v2.5-pro", "unknown-model:7b", "deterministic", ""):
row(f"ctx_budget:{mid or 'default'}", "ctx_budget", {"model": mid}, ab._ctx_budget(mid))
# ── stopwords table ────────────────────────────────────────────────────────
row("stopwords", "stopwords", {}, ab._ABSTRACTION_STOPWORDS())
# ── cc_loader_pin ──────────────────────────────────────────────────────────
for envval in ("python", "auto", "", None):
key = envval if envval is not None else "unset"
if envval is None:
os.environ.pop("RUNE_COGNITION_CORE", None)
else:
os.environ["RUNE_COGNITION_CORE"] = envval
row(f"cc_loader_pin:{key}", "cc_loader_pin", {"env": {"RUNE_COGNITION_CORE": envval}}, ab.cc_loader_pin())
os.environ["RUNE_COGNITION_CORE"] = "python"
# ── _core / _cc_probe (kernel probe) ───────────────────────────────────────
def core_token():
c = ab._core()
return None if c is None else "kernel"
os.environ["RUNE_COGNITION_CORE"] = "python"
row("core:pinned", "core", {"pinned": True, "kernel": False}, core_token())
_old_probe = ab._cc_probe
os.environ.pop("RUNE_COGNITION_CORE", None)
ab._cc_probe = lambda: None # kernel absent
row("core:auto_no_kernel", "core", {"pinned": False, "kernel": False}, core_token())
ab._cc_probe = lambda: object() # kernel present
row("core:auto_with_kernel", "core", {"pinned": False, "kernel": True}, core_token())
ab._cc_probe = _old_probe
os.environ["RUNE_COGNITION_CORE"] = "python"
# ── _now format (isoformat, microseconds only when nonzero) ────────────────
class _FakeDT(_real_dt):
_fixed = None
@classmethod
def now(cls, tz=None):
return cls._fixed
_orig_dt = ab.datetime
for label, args in (("zero", (2026, 1, 1, 12, 30, 45, 0)), ("micros", (2026, 1, 1, 12, 30, 45, 123456))):
import datetime as _dtmod
_FakeDT._fixed = _real_dt(*args, tzinfo=_dtmod.timezone.utc)
ab.datetime = _FakeDT
ab._now = _ORIG_NOW # the real _now, against the frozen datetime
row(f"now:{label}", "now", {"usec": args[6]}, ab._now())
ab.datetime = _orig_dt
ab._now = lambda: FIXED_TS
# ── connect: schema on a fresh database ────────────────────────────────────
def fresh_db():
d = tempfile.mkdtemp(prefix="abstr-truth-")
path = os.path.join(d, "abstraction.db")
ab.DB_PATH = path
return path
def dump_db(path, tables=("facts", "entities", "fact_entities", "observations", "beliefs", "rules", "skills", "triples")):
out = {}
con = sqlite3.connect(path)
con.row_factory = sqlite3.Row
for t in tables:
if t == "fact_entities":
order = "fact_id, entity_id"
else:
order = "id"
out[t] = [dict(r) for r in con.execute(f"SELECT * FROM {t} ORDER BY {order}").fetchall()]
con.close()
return out
path = fresh_db()
db = ab.connect()
tables = sorted(r[0] for r in db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"))
db.close()
row("connect:schema", "connect_schema", {}, tables)
# ── llm_call: transport + parse branches (patched urlopen) ─────────────────
_orig_urlopen = urllib.request.urlopen
class _FakeResp:
def __init__(self, body):
self._body = body
def read(self):
return self._body
def __enter__(self):
return self
def __exit__(self, *a):
return False
def with_urlopen(fake, fn):
urllib.request.urlopen = fake
try:
return fn()
finally:
urllib.request.urlopen = _orig_urlopen
captured = {}
def urlopen_ok(body, capture=False):
def fake(req, timeout=30):
if capture:
captured["url"] = req.full_url
captured["payload"] = req.data.decode()
return _FakeResp(body)
return fake
# payload capture: prompt/model/max_tokens → exact request body (byte parity)
ab.OLLAMA_URL = "http://127.0.0.1:11434"
got_payload = with_urlopen(
urlopen_ok(b'{"response": "x"}', capture=True),
lambda: ab.llm_call("Say hi", model=None, max_tokens=800),
)
row("llm_call:payload_default_model", "llm_payload", {"prompt": "Say hi", "model": "", "max_tokens": 800, "url": ab.OLLAMA_URL},
{"url": captured["url"], "payload": captured["payload"], "returned": got_payload})
got_payload = with_urlopen(
urlopen_ok(b'{"response": "x"}', capture=True),
lambda: ab.llm_call("Say hi", model="mimo-v2.5-pro", max_tokens=64),
)
row("llm_call:payload_cloud_model", "llm_payload", {"prompt": "Say hi", "model": "mimo-v2.5-pro", "max_tokens": 64, "url": ab.OLLAMA_URL},
{"url": captured["url"], "payload": captured["payload"], "returned": got_payload})
# response parsing branches
for label, body in (
("plain", b'{"response": " hello world "}'),
("missing_key", b'{"other": 1}'),
("whitespace_only", b'{"response": " \\n\\t "}'),
("non_dict", b'[1, 2]'),
("bad_json", b'not json'),
):
got = with_urlopen(urlopen_ok(body), lambda: ab.llm_call("p"))
row(f"llm_call:{label}", "llm_call", {"body": body.decode()}, got)
def urlopen_raise(req, timeout=30):
raise urllib.error.URLError("connection refused")
got = with_urlopen(urlopen_raise, lambda: ab.llm_call("p"))
row("llm_call:transport_error", "llm_call", {"raise": True}, got)
# ── extract_facts ──────────────────────────────────────────────────────────
def seed_db(path, seed):
ab.connect().close() # ensure the schema exists before seeding
if not seed:
return
con = sqlite3.connect(path)
for table, cols, rws in (
("facts", ["id", "episode_id", "what", "content_hash", "when_text", "where_text", "who_text", "why_text",
"fact_type", "entities_json", "temporal_start", "temporal_end", "confidence", "source", "state",
"access_count", "last_accessed", "created_at", "superseded_by"], seed.get("facts", [])),
("entities", ["id", "name", "entity_type", "summary", "fact_count", "observation_count", "created_at"],
seed.get("entities", [])),
("fact_entities", ["fact_id", "entity_id"], seed.get("fact_entities", [])),
("observations", ["id", "entity_id", "observation", "source_fact_ids", "confidence", "state", "created_at",
"superseded_by"], seed.get("observations", [])),
("beliefs", ["id", "claim", "confidence", "reasoning", "supporting_fact_ids", "counterevidence", "scope",
"state", "formed_at", "last_reinforced", "last_tested"], seed.get("beliefs", [])),
("rules", ["id", "pattern", "description", "confidence", "source_belief_ids", "trigger_count",
"success_count", "failure_count", "scope", "state", "created_at", "last_triggered"],
seed.get("rules", [])),
("skills", ["id", "name", "description", "procedure_json", "source_rule_ids", "success_count",
"failure_count", "state", "created_at", "last_used"], seed.get("skills", [])),
("triples", ["id", "subject", "predicate", "object", "source_fact_id", "confidence", "created_at"],
seed.get("triples", [])),
):
for r in rws:
vals = [r.get(c) for c in cols]
con.execute(
f"INSERT INTO {table} ({', '.join(cols)}) VALUES ({', '.join('?' * len(cols))})", vals)
con.commit()
con.close()
class FakeKernel:
"""Stands in for the Rust cc kernel: records args, scripted items."""
def __init__(self, items=None, raise_exc=False):
self.items = items or []
self.raise_exc = raise_exc
self.calls = []
def abstraction_extract_facts(self, text, stopwords, min_len=10):
self.calls.append({"text": text, "stopwords": list(stopwords), "min_len": min_len})
if self.raise_exc:
raise RuntimeError("kernel exploded")
return [dict(i) for i in self.items]
def run_steps(setup):
"""Execute the setup's steps against the live oracle; return results."""
results = []
path = fresh_db()
seed_db(path, setup.get("seed"))
llm_queue = list(setup.get("llm", []))
kernel = None
if "kernel" in setup:
kernel = FakeKernel(setup["kernel"], setup.get("kernel_raise", False))
old_llm = ab.llm_call
old_probe = ab._cc_probe
old_loader_fn = rcl.abstraction_extract_facts
old_core = ab._core
llm_captures = []
def scripted_llm(prompt, model=None, max_tokens=500):
llm_captures.append({"prompt": prompt, "model": model, "max_tokens": max_tokens})
return llm_queue.pop(0) if llm_queue else ""
ab.llm_call = scripted_llm
if kernel is not None:
# kernel path: env unpinned + probe returns non-None + loader faked
os.environ.pop("RUNE_COGNITION_CORE", None)
ab._cc_probe = lambda: object()
rcl.abstraction_extract_facts = kernel.abstraction_extract_facts
try:
for step in setup.get("steps", []):
fn = step["fn"]
if fn == "extract_facts":
results.append(ab.extract_facts(
step["text"], episode_id=step.get("episode_id"), source=step.get("source", "session")))
elif fn == "synthesize_observations":
results.append(ab.synthesize_observations(step["entity"]))
elif fn == "list_beliefs":
results.append(ab.list_beliefs(limit=step.get("limit", 20)))
elif fn == "list_rules":
results.append(ab.list_rules(limit=step.get("limit", 20)))
elif fn == "list_skills":
results.append(ab.list_skills(limit=step.get("limit", 20)))
elif fn == "run_lifecycle":
results.append(ab.run_lifecycle())
elif fn == "pipeline_stats":
results.append(ab.pipeline_stats())
elif fn == "handle_hook":
results.append(ab.handle_hook(step.get("event"), step.get("data")))
finally:
ab.llm_call = old_llm
ab._cc_probe = old_probe
rcl.abstraction_extract_facts = old_loader_fn
ab._core = old_core
os.environ["RUNE_COGNITION_CORE"] = "python"
want = {"results": results}
if setup.get("capture_llm"):
want["llm_calls"] = llm_captures
if setup.get("dump"):
want["db"] = dump_db(path, tuple(setup["dump"]))
if kernel is not None:
want["kernel_calls"] = kernel.calls
return want
FACT_A = ("The deployment pipeline failed because the staging database "
"was locked by a long running migration this morning")
FACT_A_DICT = {"what": FACT_A, "when": "this morning", "where": "staging",
"who": "deploy-bot", "why": "database lock", "type": "experience",
"entities": ["deploy-bot", "staging"]}
LLM_ARRAY = json.dumps([FACT_A_DICT])
# LLM valid JSON: entities linked, triple created (who + >3 words)
row("extract:llm_valid_entities_triple", "steps",
{"llm": [LLM_ARRAY], "dump": ["facts", "entities", "fact_entities", "triples"],
"steps": [{"fn": "extract_facts", "text": "deploy text", "episode_id": "ep-1", "source": "test"}]},
run_steps({"llm": [LLM_ARRAY], "dump": ["facts", "entities", "fact_entities", "triples"],
"steps": [{"fn": "extract_facts", "text": "deploy text", "episode_id": "ep-1", "source": "test"}]}))
# dedup: second identical fact is a NOOP; distinct facts both stored
row("extract:dedup_noop", "steps",
{"llm": [LLM_ARRAY, LLM_ARRAY, json.dumps([{"what": FACT_A + " and then recovered fully"}])],
"dump": ["facts"], "steps": [
{"fn": "extract_facts", "text": "t1", "episode_id": "e1"},
{"fn": "extract_facts", "text": "t2", "episode_id": "e2"},
{"fn": "extract_facts", "text": "t3", "episode_id": "e3"}]},
run_steps({"llm": [LLM_ARRAY, LLM_ARRAY, json.dumps([{"what": FACT_A + " and then recovered fully"}])],
"dump": ["facts"], "steps": [
{"fn": "extract_facts", "text": "t1", "episode_id": "e1"},
{"fn": "extract_facts", "text": "t2", "episode_id": "e2"},
{"fn": "extract_facts", "text": "t3", "episode_id": "e3"}]}))
# short / empty what skipped; missing type defaults to world; missing dims empty
SHORT_FACTS = json.dumps([
{"what": "tiny"}, # <10 chars → skip
{"what": ""}, # empty → skip
{"what": "A completely unpunctuated plain fact sentence"}, # defaults
])
row("extract:short_empty_defaults", "steps",
{"llm": [SHORT_FACTS], "dump": ["facts"], "steps": [{"fn": "extract_facts", "text": "t"}]},
run_steps({"llm": [SHORT_FACTS], "dump": ["facts"], "steps": [{"fn": "extract_facts", "text": "t"}]}))
# who + <=3 words → no triple; who empty → no triple; entity strip/empty handling
NO_TRIPLE = json.dumps([
{"what": "Deploy bot ran", "who": "deploy-bot", "entities": [" deploy-bot ", ""]},
{"what": "Nobody was around here at all", "who": "", "entities": []},
])
row("extract:no_triple_entity_strip", "steps",
{"llm": [NO_TRIPLE], "dump": ["facts", "entities", "fact_entities", "triples"],
"steps": [{"fn": "extract_facts", "text": "t"}]},
run_steps({"llm": [NO_TRIPLE], "dump": ["facts", "entities", "fact_entities", "triples"],
"steps": [{"fn": "extract_facts", "text": "t"}]}))
# hash truncation: two facts identical in first 80 chars dedup; different at 81+ don't
BASE80 = "x" * 79
DUP80 = json.dumps([{"what": BASE80 + "a"}, {"what": BASE80 + "b"}])
row("extract:hash_80_truncation", "steps",
{"llm": [DUP80], "dump": ["facts"], "steps": [{"fn": "extract_facts", "text": "t"}]},
run_steps({"llm": [DUP80], "dump": ["facts"], "steps": [{"fn": "extract_facts", "text": "t"}]}))
# prose-wrapped array extraction (first [ to last ])
WRAPPED = "Sure! Here are the facts:\n" + LLM_ARRAY + "\nHope that helps."
row("extract:prose_wrapped_array", "steps",
{"llm": [WRAPPED], "dump": ["facts"], "steps": [{"fn": "extract_facts", "text": "t"}]},
run_steps({"llm": [WRAPPED], "dump": ["facts"], "steps": [{"fn": "extract_facts", "text": "t"}]}))
# unparseable + kernel fallback (items mapped; blank item skipped; args recorded)
KERNEL_ITEMS = [{"text": " kernel derived fact sentence one ", "content_words": ["kernel", "derived"]},
{"text": " "},
{"text": "kernel derived fact sentence two", "content_words": None}]
row("extract:kernel_fallback", "steps",
{"llm": ["no json here"], "kernel": KERNEL_ITEMS, "dump": ["facts"],
"steps": [{"fn": "extract_facts", "text": "kernel episode text", "episode_id": "ek"}]},
run_steps({"llm": ["no json here"], "kernel": KERNEL_ITEMS, "dump": ["facts"],
"steps": [{"fn": "extract_facts", "text": "kernel episode text", "episode_id": "ek"}]}))
# kernel raises → silent degrade to the error row
row("extract:kernel_raises", "steps",
{"llm": [], "kernel": [], "kernel_raise": True, "steps": [{"fn": "extract_facts", "text": "t"}]},
run_steps({"llm": [], "kernel": [], "kernel_raise": True, "steps": [{"fn": "extract_facts", "text": "t"}]}))
# no LLM, no kernel → deterministic error row (raw "")
row("extract:no_llm_no_kernel", "steps",
{"llm": [], "steps": [{"fn": "extract_facts", "text": "t", "episode_id": None}]},
run_steps({"llm": [], "steps": [{"fn": "extract_facts", "text": "t", "episode_id": None}]}))
# LLM returns "[]" → falls through kernel (pinned off) → error with raw "[]"
row("extract:empty_array_no_kernel", "steps",
{"llm": ["[]"], "steps": [{"fn": "extract_facts", "text": "t"}]},
run_steps({"llm": ["[]"], "steps": [{"fn": "extract_facts", "text": "t"}]}))
# unterminated array (truncation) → JSONDecodeError → error path
row("extract:unterminated_array", "steps",
{"llm": ['[{"what": "truncated fact sentence here"}'], "steps": [{"fn": "extract_facts", "text": "t"}]},
run_steps({"llm": ['[{"what": "truncated fact sentence here"}'], "steps": [{"fn": "extract_facts", "text": "t"}]}))
# episode_id omitted → stored "" and returned null
row("extract:episode_id_null", "steps",
{"llm": [LLM_ARRAY], "dump": ["facts"], "steps": [{"fn": "extract_facts", "text": "t"}]},
run_steps({"llm": [LLM_ARRAY], "dump": ["facts"], "steps": [{"fn": "extract_facts", "text": "t"}]}))
# prompt bytes: the exact LLM prompt built for an episode (with existing-facts context)
row("extract:prompt_bytes", "steps",
{"llm": [LLM_ARRAY, LLM_ARRAY], "capture_llm": True, "steps": [
{"fn": "extract_facts", "text": "cold episode", "episode_id": "p1"},
{"fn": "extract_facts", "text": "warm episode", "episode_id": "p2"}]},
run_steps({"llm": [LLM_ARRAY, LLM_ARRAY], "capture_llm": True, "steps": [
{"fn": "extract_facts", "text": "cold episode", "episode_id": "p1"},
{"fn": "extract_facts", "text": "warm episode", "episode_id": "p2"}]}))
# ── synthesize_observations ────────────────────────────────────────────────
SEED_ENTITY = {"entities": [{"id": 1, "name": "deploy-bot", "entity_type": "concept", "summary": "",
"fact_count": 3, "observation_count": 0, "created_at": FIXED_TS}]}
SEED_3_FACTS = {"facts": [
{"id": i + 1, "episode_id": "e", "what": f"fact number {i} about the deploy bot system",
"content_hash": f"h{i}", "when_text": "", "where_text": "", "who_text": "", "why_text": "",
"fact_type": "world" if i % 2 else "experience", "entities_json": "[]", "temporal_start": "",
"temporal_end": "", "confidence": 0.7, "source": "s", "state": "active", "access_count": 0,
"last_accessed": None, "created_at": FIXED_TS, "superseded_by": None} for i in range(3)],
"fact_entities": [{"fact_id": i + 1, "entity_id": 1} for i in range(3)]}
row("observe:entity_missing", "steps",
{"steps": [{"fn": "synthesize_observations", "entity": "ghost"}]},
run_steps({"steps": [{"fn": "synthesize_observations", "entity": "ghost"}]}))
row("observe:too_few_facts", "steps",
{"seed": {"entities": [SEED_ENTITY["entities"][0]],
"facts": SEED_3_FACTS["facts"][:2], "fact_entities": [{"fact_id": 1, "entity_id": 1}]},
"steps": [{"fn": "synthesize_observations", "entity": "deploy-bot"}]},
run_steps({"seed": {"entities": [SEED_ENTITY["entities"][0]],
"facts": SEED_3_FACTS["facts"][:2], "fact_entities": [{"fact_id": 1, "entity_id": 1}]},
"steps": [{"fn": "synthesize_observations", "entity": "deploy-bot"}]}))
OBS_SCRIPT = json.dumps([
{"observation": "Deploy bot is unreliable under database contention", "confidence": 0.9},
{"observation": "short"}, # <10 → skipped
{"observation": "Deploy bot failures cluster around migrations"}, # default confidence
])
row("observe:ok", "steps",
{**SEED_ENTITY, **SEED_3_FACTS, "llm": [OBS_SCRIPT], "dump": ["observations", "entities"],
"steps": [{"fn": "synthesize_observations", "entity": "deploy-bot"}]},
run_steps({**SEED_ENTITY, **SEED_3_FACTS, "llm": [OBS_SCRIPT], "dump": ["observations", "entities"],
"steps": [{"fn": "synthesize_observations", "entity": "deploy-bot"}]}))
# observe prompt bytes
row("observe:prompt_bytes", "steps",
{**SEED_ENTITY, **SEED_3_FACTS, "fact_entities": SEED_3_FACTS["fact_entities"], "llm": [OBS_SCRIPT],
"capture_llm": True, "steps": [{"fn": "synthesize_observations", "entity": "deploy-bot"}]},
run_steps({**SEED_ENTITY, **SEED_3_FACTS, "fact_entities": SEED_3_FACTS["fact_entities"], "llm": [OBS_SCRIPT],
"capture_llm": True, "steps": [{"fn": "synthesize_observations", "entity": "deploy-bot"}]}))
# ordering: facts created_at DESC controls source_fact_ids
SEED_3_FACTS_DESC = {"facts": [
{"id": 1, "episode_id": "e", "what": "oldest fact sentence about the bot", "content_hash": "h1",
"when_text": "", "where_text": "", "who_text": "", "why_text": "", "fact_type": "world",
"entities_json": "[]", "temporal_start": "", "temporal_end": "", "confidence": 0.7, "source": "s",
"state": "active", "access_count": 0, "last_accessed": None, "created_at": "2026-01-01T00:00:00+00:00",
"superseded_by": None},
{"id": 2, "episode_id": "e", "what": "newest fact sentence about the bot", "content_hash": "h2",
"when_text": "", "where_text": "", "who_text": "", "why_text": "", "fact_type": "world",
"entities_json": "[]", "temporal_start": "", "temporal_end": "", "confidence": 0.7, "source": "s",
"state": "active", "access_count": 0, "last_accessed": None, "created_at": "2026-09-01T00:00:00+00:00",
"superseded_by": None},
{"id": 3, "episode_id": "e", "what": "middle fact sentence about the bot", "content_hash": "h3",
"when_text": "", "where_text": "", "who_text": "", "why_text": "", "fact_type": "world",
"entities_json": "[]", "temporal_start": "", "temporal_end": "", "confidence": 0.7, "source": "s",
"state": "active", "access_count": 0, "last_accessed": None, "created_at": "2026-05-01T00:00:00+00:00",
"superseded_by": None},
]}
row("observe:source_ids_order", "steps",
{**SEED_ENTITY, **SEED_3_FACTS_DESC, "llm": [json.dumps([{"observation": "Bot facts span the whole year range"}])],
"dump": ["observations"], "steps": [{"fn": "synthesize_observations", "entity": "deploy-bot"}]},
run_steps({**SEED_ENTITY, **SEED_3_FACTS_DESC,
"llm": [json.dumps([{"observation": "Bot facts span the whole year range"}])],
"dump": ["observations"], "steps": [{"fn": "synthesize_observations", "entity": "deploy-bot"}]}))
# ── list_beliefs / list_rules / list_skills (ordering + limit + state) ─────
SEED_LISTS = {
"beliefs": [
{"id": 1, "claim": "low confidence belief", "confidence": 0.3, "reasoning": "r",
"supporting_fact_ids": "[]", "counterevidence": "[]", "scope": "", "state": "active",
"formed_at": FIXED_TS, "last_reinforced": None, "last_tested": None},
{"id": 2, "claim": "high confidence belief", "confidence": 0.9, "reasoning": "r",
"supporting_fact_ids": "[]", "counterevidence": "[]", "scope": "", "state": "active",
"formed_at": FIXED_TS, "last_reinforced": None, "last_tested": None},
{"id": 3, "claim": "mid confidence belief", "confidence": 0.5, "reasoning": "r",
"supporting_fact_ids": "[]", "counterevidence": "[]", "scope": "", "state": "archived",
"formed_at": FIXED_TS, "last_reinforced": None, "last_tested": None},
],
"rules": [
{"id": 1, "pattern": "rule a", "description": "d", "confidence": 0.5, "source_belief_ids": "[]",
"trigger_count": 2, "success_count": 2, "failure_count": 0, "scope": "", "state": "active",
"created_at": FIXED_TS, "last_triggered": None},
{"id": 2, "pattern": "rule b", "description": "d", "confidence": 0.5, "source_belief_ids": "[]",
"trigger_count": 5, "success_count": 4, "failure_count": 1, "scope": "", "state": "active",
"created_at": FIXED_TS, "last_triggered": None},
],
"skills": [
{"id": 1, "name": "skill-low", "description": "d", "procedure_json": "[]", "source_rule_ids": "[]",
"success_count": 1, "failure_count": 0, "state": "active", "created_at": FIXED_TS, "last_used": None},
{"id": 2, "name": "skill-high", "description": "d", "procedure_json": "[]", "source_rule_ids": "[]",
"success_count": 9, "failure_count": 0, "state": "active", "created_at": FIXED_TS, "last_used": None},
],
}
row("beliefs:order_and_state", "steps",
{"seed": SEED_LISTS, "dump": [], "steps": [{"fn": "list_beliefs"}]},
run_steps({"seed": SEED_LISTS, "steps": [{"fn": "list_beliefs"}]}))
row("beliefs:limit", "steps",
{"seed": SEED_LISTS, "steps": [{"fn": "list_beliefs", "limit": 1}]},
run_steps({"seed": SEED_LISTS, "steps": [{"fn": "list_beliefs", "limit": 1}]}))
row("rules:order_tiebreak_and_state", "steps",
{"seed": SEED_LISTS, "steps": [{"fn": "list_rules"}]},
run_steps({"seed": SEED_LISTS, "steps": [{"fn": "list_rules"}]}))
row("skills:order_and_state", "steps",
{"seed": SEED_LISTS, "steps": [{"fn": "list_skills"}]},
run_steps({"seed": SEED_LISTS, "steps": [{"fn": "list_skills"}]}))
row("lists:empty_db", "steps",
{"steps": [{"fn": "list_beliefs"}, {"fn": "list_rules"}, {"fn": "list_skills"}]},
run_steps({"steps": [{"fn": "list_beliefs"}, {"fn": "list_rules"}, {"fn": "list_skills"}]}))
# ── run_lifecycle (all archive gates) ──────────────────────────────────────
LIFECYCLE_SEED = {"facts": [
{"id": 1, "episode_id": "", "what": "old low confidence untouched fact sentence",
"content_hash": "l1", "when_text": "", "where_text": "", "who_text": "", "why_text": "",
"fact_type": "world", "entities_json": "[]", "temporal_start": "", "temporal_end": "",
"confidence": 0.4, "source": "", "state": "active", "access_count": 0, "last_accessed": None,
"created_at": "2020-01-01T00:00:00+00:00", "superseded_by": None}, # → archived
{"id": 2, "episode_id": "", "what": "old but accessed fact sentence here",
"content_hash": "l2", "when_text": "", "where_text": "", "who_text": "", "why_text": "",
"fact_type": "world", "entities_json": "[]", "temporal_start": "", "temporal_end": "",
"confidence": 0.4, "source": "", "state": "active", "access_count": 1, "last_accessed": None,
"created_at": "2020-01-01T00:00:00+00:00", "superseded_by": None}, # stays
{"id": 3, "episode_id": "", "what": "old but confident fact sentence",
"content_hash": "l3", "when_text": "", "where_text": "", "who_text": "", "why_text": "",
"fact_type": "world", "entities_json": "[]", "temporal_start": "", "temporal_end": "",
"confidence": 0.8, "source": "", "state": "active", "access_count": 0, "last_accessed": None,
"created_at": "2020-01-01T00:00:00+00:00", "superseded_by": None}, # stays
{"id": 4, "episode_id": "", "what": "recent low confidence fact sentence",
"content_hash": "l4", "when_text": "", "where_text": "", "who_text": "", "why_text": "",
"fact_type": "world", "entities_json": "[]", "temporal_start": "", "temporal_end": "",
"confidence": 0.4, "source": "", "state": "active", "access_count": 0, "last_accessed": None,
"created_at": "2099-01-01T00:00:00+00:00", "superseded_by": None}, # stays
{"id": 5, "episode_id": "", "what": "already archived fact sentence",
"content_hash": "l5", "when_text": "", "where_text": "", "who_text": "", "why_text": "",
"fact_type": "world", "entities_json": "[]", "temporal_start": "", "temporal_end": "",
"confidence": 0.4, "source": "", "state": "archived", "access_count": 0, "last_accessed": None,
"created_at": "2020-01-01T00:00:00+00:00", "superseded_by": None}, # untouched
], "observations": [
{"id": 1, "entity_id": 1, "observation": "superseded observation sentence", "source_fact_ids": "[]",
"confidence": 0.7, "state": "active", "created_at": FIXED_TS, "superseded_by": 2}, # → archived
{"id": 2, "entity_id": 1, "observation": "current observation sentence", "source_fact_ids": "[]",
"confidence": 0.7, "state": "active", "created_at": FIXED_TS, "superseded_by": None}, # stays
{"id": 3, "entity_id": 1, "observation": "archived observation sentence", "source_fact_ids": "[]",
"confidence": 0.7, "state": "archived", "created_at": FIXED_TS, "superseded_by": 2}, # stays
]}
row("lifecycle:archive_gates", "steps",
{"seed": LIFECYCLE_SEED, "dump": ["facts", "observations"],
"steps": [{"fn": "run_lifecycle"}]},
run_steps({"seed": LIFECYCLE_SEED, "dump": ["facts", "observations"], "steps": [{"fn": "run_lifecycle"}]}))
# ── pipeline_stats ─────────────────────────────────────────────────────────
row("stats:mixed", "steps",
{"seed": {**LIFECYCLE_SEED, **SEED_LISTS, **SEED_ENTITY, **SEED_3_FACTS,
"fact_entities": SEED_3_FACTS["fact_entities"], "triples": [
{"id": 1, "subject": "s", "predicate": "p", "object": "o", "source_fact_id": 1,
"confidence": 0.6, "created_at": FIXED_TS}]},
"steps": [{"fn": "run_lifecycle"}, {"fn": "pipeline_stats"}]},
run_steps({"seed": {**LIFECYCLE_SEED, **SEED_LISTS, **SEED_ENTITY, **SEED_3_FACTS,
"fact_entities": SEED_3_FACTS["fact_entities"], "triples": [
{"id": 1, "subject": "s", "predicate": "p", "object": "o", "source_fact_id": 1,
"confidence": 0.6, "created_at": FIXED_TS}]},
"steps": [{"fn": "run_lifecycle"}, {"fn": "pipeline_stats"}]}))
row("stats:empty", "steps", {"steps": [{"fn": "pipeline_stats"}]},
run_steps({"steps": [{"fn": "pipeline_stats"}]}))
# ── handle_hook ────────────────────────────────────────────────────────────
row("hook:session_end_with_data", "steps",
{"steps": [{"fn": "handle_hook", "event": "on_session_end", "data": {"k": 1}}]},
run_steps({"steps": [{"fn": "handle_hook", "event": "on_session_end", "data": {"k": 1}}]}))
row("hook:other_event_no_data", "steps",
{"steps": [{"fn": "handle_hook", "event": "on_tick", "data": None}]},
run_steps({"steps": [{"fn": "handle_hook", "event": "on_tick", "data": None}]}))
# ── CLI (byte-exact stdout/stderr/exit) ────────────────────────────────────
def run_cli(setup):
import contextlib as _cl
path = fresh_db()
seed_db(path, setup.get("seed"))
llm_queue = list(setup.get("llm", []))
old_llm, old_url = ab.llm_call, ab.OLLAMA_URL
ab.llm_call = lambda prompt, model=None, max_tokens=500: llm_queue.pop(0) if llm_queue else ""
ab.OLLAMA_URL = UNREACHABLE
old_argv, old_out, old_err = sys.argv, sys.stdout, sys.stderr
out, err = io.StringIO(), io.StringIO()
code = 0
sys.argv = ["abstraction.py"] + setup["argv"]
sys.stdout, sys.stderr = out, err
try:
ab.main()
except SystemExit as e:
code = e.code if isinstance(e.code, int) else 1
finally:
sys.stdout, sys.stderr = old_out, old_err
sys.argv = old_argv
ab.llm_call, ab.OLLAMA_URL = old_llm, old_url
return {"stdout": out.getvalue(), "stderr": err.getvalue(), "code": code}
CLI_SETS = {
"extract_nojson": ({"llm": [LLM_ARRAY]}, ["extract", "deploy text", "--episode-id", "cx1"]),
"extract_json": ({"llm": [LLM_ARRAY]}, ["extract", "deploy text", "--json"]),
"extract_usage": ({}, ["extract"]),
"extract_fails": ({}, ["extract", "some episode text here"]),
"observe_ok": ({"seed": {**SEED_ENTITY, **SEED_3_FACTS, "fact_entities": SEED_3_FACTS["fact_entities"]},
"llm": [OBS_SCRIPT]}, ["observe", "deploy-bot", "--json"]),
"observe_missing": ({}, ["observe", "ghost"]),
"observe_usage": ({}, ["observe"]),
"beliefs_text": ({"seed": SEED_LISTS}, ["beliefs"]),
"beliefs_json": ({"seed": SEED_LISTS}, ["beliefs", "--json"]),
"beliefs_empty": ({}, ["beliefs"]),
"rules_text": ({"seed": SEED_LISTS}, ["rules"]),
"skills_text": ({"seed": SEED_LISTS}, ["skills"]),
"skills_json": ({"seed": SEED_LISTS}, ["skills", "--json"]),
"lifecycle_text": ({"seed": LIFECYCLE_SEED}, ["lifecycle"]),
"lifecycle_json": ({"seed": LIFECYCLE_SEED}, ["lifecycle", "--json"]),
"stats_text": ({"seed": SEED_LISTS}, ["stats"]),
"stats_json": ({"seed": SEED_LISTS}, ["stats", "--json"]),
"stats_empty_text": ({}, ["stats"]),
"hook": ({}, ["hook", "--event", "on_session_end", "--data", '{"session": "s1"}']),
"hook_no_event": ({}, ["hook"]),
"hook_data_null": ({}, ["hook", "--event", "on_tick"]),
"evolve_noop": ({}, ["evolve", "mem-123"]),
"bad_choice": ({}, ["nope"]),
"no_cmd": ({}, []),
"help": ({}, ["--help"]),
}
for label, (setup, argv) in CLI_SETS.items():
s = dict(setup)
s["argv"] = argv
row(f"cli:{label}", "cli", s, run_cli(s))
# ── freeze ─────────────────────────────────────────────────────────────────
doc = {
"frozen_ts": "2026-10-11T13:45:00Z",
"frozen_id": "abstraction-full-go-#2479",
"generator": "scripts/gen_abstraction_truth.py equivalent (session temp)",
"rows": rows,
}
out = sys.argv[1] if len(sys.argv) > 1 else "/mnt/workdrive/rune/src/go/abstraction/truth.json"
with open(out, "w") as f:
json.dump(doc, f, indent=1)
print(f"frozen {len(rows)} rows -> {out}")