from __future__ import annotations
import json
import os
import re
import subprocess
import tempfile
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
BIN = ROOT / "target" / "debug" / "asobi"
KNOWLEDGE_TYPES = ("project", "concept", "reference", "preference", "standard")
SKIPPED_TYPES = ("session", "task", "skill")
def doc_env(home: Path) -> dict[str, str]:
env = os.environ.copy()
env.pop("ASOBI_HOME", None)
env.pop("ASOBI_DATABASE_URL", None)
env.pop("ASOBI_TOPICS_DIR", None)
env["XDG_DATA_HOME"] = str(home)
return env
def topics_dir(home: Path) -> Path:
return home / "asobi" / "topics"
def run(
args: list[str], env: dict[str, str], cwd: Path
) -> subprocess.CompletedProcess[str]:
result = subprocess.run(
[str(BIN), *args],
cwd=cwd,
env=env,
text=True,
capture_output=True,
check=False,
)
if result.returncode != 0:
raise AssertionError(
f"command failed: asobi {' '.join(args)}\n"
f"stdout:\n{result.stdout}\n"
f"stderr:\n{result.stderr}"
)
return result
def slugify(name: str) -> str:
slug = re.sub(r"[^a-z0-9]+", "-", name.lower()).strip("-")
return slug
def main() -> None:
subprocess.run(["cargo", "build", "--features", "documents"], cwd=ROOT, check=True)
compact_persists_every_type()
compact_is_idempotent()
ingest_then_query_recalls()
synced_topic_is_recallable()
print("Document CLI integration checks passed")
def compact_persists_every_type() -> None:
with tempfile.TemporaryDirectory(prefix="asobi-doc-types-") as tmp:
home = Path(tmp)
env = doc_env(home)
for etype in (*KNOWLEDGE_TYPES, *SKIPPED_TYPES):
name = f"ent:{etype}"
run(["new", name, etype], env, home)
run(["obs", name, f"trail note for a {etype} entity"], env, home)
run(["compact"], env, home)
present = {p.name for p in topics_dir(home).glob("*.md")}
expected = {f"{slugify(f'ent:{t}')}.md" for t in KNOWLEDGE_TYPES}
skipped = {f"{slugify(f'ent:{t}')}.md" for t in SKIPPED_TYPES}
missing = expected - present
assert not missing, (
f"knowledge types not persisted by compact: {sorted(missing)}\n"
f"topics present: {sorted(present)}"
)
leaked = skipped & present
assert not leaked, f"graph-only state wrongly synced: {sorted(leaked)}"
assert present == expected, (
f"unexpected topic set.\nexpected: {sorted(expected)}\n"
f"present: {sorted(present)}"
)
pref = (topics_dir(home) / f"{slugify('ent:preference')}.md").read_text()
assert 'title: "ent:preference"' in pref, f"title not quoted:\n{pref}"
assert 'type: "preference"' in pref, f"type not quoted:\n{pref}"
assert "trail note for a preference entity" in pref
def compact_is_idempotent() -> None:
with tempfile.TemporaryDirectory(prefix="asobi-doc-idem-") as tmp:
home = Path(tmp)
env = doc_env(home)
topic = topics_dir(home) / f"{slugify('proj:demo')}.md"
run(["new", "proj:demo", "project"], env, home)
run(["obs", "proj:demo", "first observation"], env, home)
run(["compact"], env, home)
first = topic.read_text()
assert "first observation" in first
run(["compact"], env, home)
assert topic.read_text() == first, "no-op compact rewrote an unchanged topic"
run(["truth", "proj:demo", "status", "ACTIVE"], env, home)
run(["obs", "proj:demo", "second observation"], env, home)
run(["compact"], env, home)
second = topic.read_text()
assert second != first, "changed entity not rewritten"
assert "second observation" in second, "second compact did not override"
assert 'truth_status: "ACTIVE"' in second, "new truth not reflected"
assert {p.name for p in topics_dir(home).glob("*.md")} == {topic.name}
def ingest_then_query_recalls() -> None:
with tempfile.TemporaryDirectory(prefix="asobi-doc-ingest-") as tmp:
home = Path(tmp)
env = doc_env(home)
doc = home / "note.md"
doc.write_text(
"---\ntitle: Auth Design\n---\n\n"
"Session tokens are verified with the zephyrquux handshake.\n"
)
run(["ingest", str(doc)], env, home)
results = json.loads(
run(["query", "zephyrquux handshake", "--json"], env, home).stdout
)
assert results, "ingested document not recalled by query"
assert any("zephyrquux" in r["snippet"].lower() for r in results), (
f"distinctive term missing from recall snippets: {results}"
)
def synced_topic_is_recallable() -> None:
with tempfile.TemporaryDirectory(prefix="asobi-doc-synced-") as tmp:
home = Path(tmp)
env = doc_env(home)
run(["new", "UserPreferences", "preference"], env, home)
run(
["obs", "UserPreferences", "Prefers the qubefable recall workflow."],
env,
home,
)
run(["compact"], env, home)
results = json.loads(
run(["query", "qubefable recall", "--json"], env, home).stdout
)
assert results, "synced preference topic not recalled after compact"
assert any(
"userpreferences" in r["topicId"].lower()
or "qubefable" in r["snippet"].lower()
for r in results
), f"compacted preference not found in recall: {results}"
if __name__ == "__main__":
main()