mirror of
https://github.com/razzant/ouroboros.git
synced 2026-10-02 19:58:46 +00:00
One Ouroboros across Main and project rooms: each root may publish a short authored focus (update_focus) that rides the durable task result and the [INDEPENDENT_ROOTS] tail, so concurrent foci can see one another without a shared chat, a new wake or any widened authority; explicit cross-room journal/workpad reads are honoured instead of silently redirected. Dialogue consolidation summarizes each source room of a logical chunk from its own bytes (Light draft + source-grounded Light correction), assembles the typed room sections deterministically into one shared block and carries them through era compression; a failed room withholds its whole chunk while earlier complete chunks stay published; legacy mixed blocks keep unknown provenance. Room labels resolve against the canonical registry root even on a forked task drive. BIBLE P1 states the principle in three sentences. Version-neutral contribution: release carriers untouched. Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
340 lines
12 KiB
Python
340 lines
12 KiB
Python
import json
|
|
import os
|
|
import pathlib
|
|
import re
|
|
from types import SimpleNamespace
|
|
|
|
from ouroboros.task_tree_ledger import tree_ledger_append
|
|
from ouroboros.tools.project_journal import (
|
|
_journal_read,
|
|
journal_tail_digest,
|
|
)
|
|
from ouroboros.tools.recent_tasks import _handle_recent_tasks
|
|
from ouroboros.tools.task_tree import _tree_read
|
|
from ouroboros.utils import append_jsonl
|
|
|
|
|
|
def _snapshot(text: str) -> str:
|
|
match = re.search(r"snapshot=['\"]?([0-9a-f]{64})", text)
|
|
assert match is not None, text
|
|
return match.group(1)
|
|
|
|
|
|
def _journal_rows(root, project_id: str, count: int) -> None:
|
|
path = root / "projects" / project_id / "journal.jsonl"
|
|
for index in range(count):
|
|
append_jsonl(path, {
|
|
"ts": f"2026-08-21T00:{index // 60:02d}:{index % 60:02d}Z",
|
|
"kind": "note",
|
|
"text": f"journal-{index:03d}",
|
|
"task_id": f"task-{index:03d}",
|
|
})
|
|
|
|
|
|
def test_journal_read_pages_205_rows_and_digest_pointer_is_executable(tmp_path, monkeypatch):
|
|
monkeypatch.setattr("ouroboros.config.DATA_DIR", tmp_path)
|
|
_journal_rows(tmp_path, "mine", 205)
|
|
_journal_rows(tmp_path, "other", 205)
|
|
ctx = SimpleNamespace(
|
|
drive_root=tmp_path / "fork",
|
|
budget_drive_root=str(tmp_path),
|
|
project_id="mine",
|
|
task_metadata={"budget_drive_root": str(tmp_path), "root_task_id": "root"},
|
|
)
|
|
|
|
first = _journal_read(ctx, "other", limit=200)
|
|
assert "total=205" in first and "remaining=5" in first
|
|
assert "journal-005" in first and "journal-204" in first
|
|
assert "journal-004" not in first
|
|
second = _journal_read(
|
|
ctx, "other", limit=200, offset=200, snapshot=_snapshot(first),
|
|
)
|
|
assert "total=205" in second and "remaining=0" in second
|
|
assert "journal-000" in second and "journal-004" in second
|
|
assert "journal-005" not in second
|
|
|
|
digest = journal_tail_digest("mine", limit=40)
|
|
assert "journal_read(project_id='mine', limit=40, offset=40, snapshot=" in digest
|
|
pointed = _journal_read(
|
|
ctx, "", limit=40, offset=40, snapshot=_snapshot(digest),
|
|
)
|
|
assert "journal-164" in pointed and "journal-165" not in pointed
|
|
|
|
|
|
def test_journal_read_refuses_append_between_pages(tmp_path, monkeypatch):
|
|
monkeypatch.setattr("ouroboros.config.DATA_DIR", tmp_path)
|
|
_journal_rows(tmp_path, "mine", 3)
|
|
ctx = SimpleNamespace(drive_root=tmp_path / "fork", project_id="mine")
|
|
first = _journal_read(ctx, limit=2)
|
|
_journal_rows(tmp_path, "mine", 1)
|
|
|
|
changed = _journal_read(ctx, limit=2, offset=2, snapshot=_snapshot(first))
|
|
assert changed.startswith("JOURNAL_READ_SNAPSHOT_CHANGED:")
|
|
assert "journal-000" not in changed
|
|
|
|
|
|
def test_journal_read_preserves_valid_rows_but_marks_malformed_coverage(tmp_path, monkeypatch):
|
|
monkeypatch.setattr("ouroboros.config.DATA_DIR", tmp_path)
|
|
_journal_rows(tmp_path, "mine", 3)
|
|
path = tmp_path / "projects" / "mine" / "journal.jsonl"
|
|
with path.open("ab") as handle:
|
|
handle.write(b'{"broken":\n')
|
|
handle.write(b'["legacy", "array"]\n')
|
|
ctx = SimpleNamespace(drive_root=tmp_path / "fork", project_id="mine")
|
|
|
|
first = _journal_read(ctx, limit=2)
|
|
assert "total=5" in first
|
|
assert "valid_total=3" in first
|
|
assert "unreadable=2" in first
|
|
assert "coverage=partial" in first
|
|
second = _journal_read(ctx, limit=2, offset=2, snapshot=_snapshot(first))
|
|
joined = first + second
|
|
assert all(f"journal-{index:03d}" in joined for index in range(3))
|
|
assert "remaining=0" in second and "coverage=partial" in second
|
|
|
|
digest = journal_tail_digest("mine", limit=40)
|
|
assert "coverage partial" in digest
|
|
assert "2 unreadable" in digest
|
|
|
|
|
|
def test_journal_read_preserves_valid_rows_with_invalid_utf8(tmp_path, monkeypatch):
|
|
monkeypatch.setattr("ouroboros.config.DATA_DIR", tmp_path)
|
|
_journal_rows(tmp_path, "mine", 1)
|
|
path = tmp_path / "projects" / "mine" / "journal.jsonl"
|
|
with path.open("ab") as handle:
|
|
handle.write(b"\xfflegacy-row\n")
|
|
ctx = SimpleNamespace(drive_root=tmp_path / "fork", project_id="mine")
|
|
|
|
page = _journal_read(ctx, limit=2)
|
|
|
|
assert "journal-000" in page
|
|
assert "unreadable=1" in page
|
|
assert "coverage=partial" in page
|
|
|
|
|
|
def _tree_rows(root, root_id: str, count: int) -> None:
|
|
for index in range(count):
|
|
result = tree_ledger_append(
|
|
root_id,
|
|
"note",
|
|
f"tree-{index:03d}",
|
|
task_id=f"child-{index:03d}",
|
|
data_root=root,
|
|
)
|
|
assert result.startswith("OK:")
|
|
|
|
|
|
def test_tree_read_pages_500_rows_from_fork_and_digest_pointer_is_executable(tmp_path, monkeypatch):
|
|
monkeypatch.setattr("ouroboros.config.DATA_DIR", tmp_path)
|
|
_tree_rows(tmp_path, "root-task", 500)
|
|
assert tree_ledger_append(
|
|
"other-root", "note", "foreign-tree", task_id="foreign", data_root=tmp_path,
|
|
).startswith("OK:")
|
|
ctx = SimpleNamespace(
|
|
task_id="child-task",
|
|
drive_root=tmp_path / "fork",
|
|
budget_drive_root=str(tmp_path),
|
|
task_metadata={
|
|
"root_task_id": "root-task",
|
|
"budget_drive_root": str(tmp_path),
|
|
},
|
|
)
|
|
|
|
pages = []
|
|
offset = 0
|
|
snapshot = ""
|
|
while True:
|
|
page = _tree_read(ctx, limit=200, offset=offset, snapshot=snapshot)
|
|
pages.append(page)
|
|
snapshot = snapshot or _snapshot(page)
|
|
remaining = int(re.search(r"remaining=(\d+)", page).group(1))
|
|
if remaining == 0:
|
|
break
|
|
offset += 200
|
|
joined = "\n".join(pages)
|
|
assert len(pages) == 3
|
|
assert "tree-300" in pages[0] and "tree-499" in pages[0]
|
|
assert "tree-299" not in pages[0]
|
|
assert "foreign-tree" not in joined
|
|
assert all(f"tree-{index:03d}" in joined for index in range(500))
|
|
|
|
from ouroboros.task_tree_ledger import tree_ledger_tail_digest
|
|
|
|
digest = tree_ledger_tail_digest("root-task", limit=40, data_root=tmp_path)
|
|
assert "tree_read(limit=40, offset=40, snapshot=" in digest
|
|
pointed = _tree_read(ctx, limit=40, offset=40, snapshot=_snapshot(digest))
|
|
assert "tree-420" in pointed and "tree-419" not in pointed
|
|
|
|
|
|
def test_tree_read_refuses_append_between_pages(tmp_path, monkeypatch):
|
|
monkeypatch.setattr("ouroboros.config.DATA_DIR", tmp_path)
|
|
_tree_rows(tmp_path, "root-task", 3)
|
|
ctx = SimpleNamespace(
|
|
task_id="child",
|
|
drive_root=tmp_path / "fork",
|
|
budget_drive_root=str(tmp_path),
|
|
task_metadata={"root_task_id": "root-task"},
|
|
)
|
|
first = _tree_read(ctx, limit=2)
|
|
_tree_rows(tmp_path, "root-task", 1)
|
|
|
|
changed = _tree_read(ctx, limit=2, offset=2, snapshot=_snapshot(first))
|
|
assert changed.startswith("TREE_READ_SNAPSHOT_CHANGED:")
|
|
assert "tree-000" not in changed
|
|
|
|
|
|
def test_tree_read_preserves_valid_rows_but_marks_malformed_coverage(tmp_path):
|
|
_tree_rows(tmp_path, "root-task", 3)
|
|
path = tmp_path / "task_trees" / "root-task" / "blackboard.jsonl"
|
|
with path.open("ab") as handle:
|
|
handle.write(b'{"broken":\n')
|
|
handle.write(b'"legacy scalar"\n')
|
|
ctx = SimpleNamespace(
|
|
task_id="child",
|
|
drive_root=tmp_path / "fork",
|
|
budget_drive_root=str(tmp_path),
|
|
task_metadata={"root_task_id": "root-task"},
|
|
)
|
|
|
|
first = _tree_read(ctx, limit=2)
|
|
assert "total=5" in first
|
|
assert "valid_total=3" in first
|
|
assert "unreadable=2" in first
|
|
assert "coverage=partial" in first
|
|
second = _tree_read(ctx, limit=2, offset=2, snapshot=_snapshot(first))
|
|
joined = first + second
|
|
assert all(f"tree-{index:03d}" in joined for index in range(3))
|
|
assert "remaining=0" in second and "coverage=partial" in second
|
|
|
|
|
|
def test_tree_read_preserves_valid_rows_with_invalid_utf8(tmp_path):
|
|
_tree_rows(tmp_path, "root-task", 1)
|
|
path = tmp_path / "task_trees" / "root-task" / "blackboard.jsonl"
|
|
with path.open("ab") as handle:
|
|
handle.write(b"\xfflegacy-row\n")
|
|
ctx = SimpleNamespace(
|
|
task_id="child",
|
|
drive_root=tmp_path / "fork",
|
|
budget_drive_root=str(tmp_path),
|
|
task_metadata={"root_task_id": "root-task"},
|
|
)
|
|
|
|
page = _tree_read(ctx, limit=2)
|
|
|
|
assert "tree-000" in page
|
|
assert "unreadable=1" in page
|
|
assert "coverage=partial" in page
|
|
|
|
|
|
def test_runtime_tree_digest_uses_same_canonical_root_as_tree_read(tmp_path):
|
|
from ouroboros.context import build_runtime_section
|
|
|
|
canonical = tmp_path / "canonical"
|
|
fork = tmp_path / "fork"
|
|
fork.mkdir()
|
|
assert tree_ledger_append(
|
|
"root-task", "note", "canonical-passive-row", task_id="child",
|
|
data_root=canonical,
|
|
).startswith("OK:")
|
|
path = canonical / "task_trees" / "root-task" / "blackboard.jsonl"
|
|
with path.open("ab") as handle:
|
|
handle.write(b"\xfflegacy-row\n")
|
|
env = SimpleNamespace(
|
|
repo_dir=pathlib.Path(__file__).parents[1],
|
|
drive_root=fork,
|
|
budget_drive_root=fork,
|
|
)
|
|
task = {
|
|
"id": "child",
|
|
"root_task_id": "root-task",
|
|
"drive_root": str(fork),
|
|
}
|
|
ctx = SimpleNamespace(
|
|
drive_root=fork,
|
|
budget_drive_root="",
|
|
task_metadata={"budget_drive_root": str(canonical)},
|
|
)
|
|
|
|
rendered = build_runtime_section(env, task, ctx=ctx)
|
|
|
|
assert "canonical-passive-row" in rendered
|
|
assert "coverage partial: 1 unreadable task-tree row" in rendered
|
|
|
|
|
|
def _write_task(root, index: int, *, project_id: str = "mine") -> None:
|
|
path = root / "task_results" / f"task-{index:03d}.json"
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(json.dumps({
|
|
"task_id": f"task-{index:03d}",
|
|
"ts": f"2026-08-21T00:{index // 60:02d}:{index % 60:02d}Z",
|
|
"status": "completed",
|
|
"description": f"task {index}",
|
|
"result": f"result {index}",
|
|
"project_id": project_id,
|
|
}), encoding="utf-8")
|
|
os.utime(path, ns=(index + 1, index + 1))
|
|
|
|
|
|
def test_recent_tasks_pages_364_and_finds_21st_without_known_id(tmp_path):
|
|
canonical = tmp_path / "canonical"
|
|
for index in range(364):
|
|
_write_task(canonical, index)
|
|
ctx = SimpleNamespace(
|
|
drive_root=tmp_path / "fork",
|
|
budget_drive_root=str(canonical),
|
|
project_id="mine",
|
|
task_metadata={"budget_drive_root": str(canonical)},
|
|
)
|
|
|
|
first = json.loads(_handle_recent_tasks(ctx, limit=20))
|
|
assert first["total"] == 364
|
|
assert first["remaining"] == 344
|
|
assert first["tasks"][0]["task_id"] == "task-363"
|
|
second = json.loads(_handle_recent_tasks(
|
|
ctx, limit=20, offset=20, snapshot=first["snapshot"],
|
|
))
|
|
assert second["tasks"][0]["task_id"] == "task-343"
|
|
assert second["remaining"] == 324
|
|
|
|
seen = first["tasks"] + second["tasks"]
|
|
assert seen[20]["task_id"] == "task-343"
|
|
def test_recent_tasks_refuses_append_between_pages(tmp_path):
|
|
canonical = tmp_path / "canonical"
|
|
for index in range(3):
|
|
_write_task(canonical, index)
|
|
ctx = SimpleNamespace(drive_root=tmp_path / "fork", budget_drive_root=str(canonical))
|
|
first = json.loads(_handle_recent_tasks(ctx, limit=2))
|
|
_write_task(canonical, 3)
|
|
|
|
changed = json.loads(_handle_recent_tasks(
|
|
ctx, limit=2, offset=2, snapshot=first["snapshot"],
|
|
))
|
|
assert changed["error"]["code"] == "RECENT_TASKS_SNAPSHOT_CHANGED"
|
|
assert changed["tasks"] == []
|
|
|
|
|
|
def test_recent_tasks_next_replays_result_and_trace_query_flags(tmp_path):
|
|
canonical = tmp_path / "canonical"
|
|
for index in range(3):
|
|
_write_task(canonical, index)
|
|
path = canonical / "task_results" / f"task-{index:03d}.json"
|
|
data = json.loads(path.read_text(encoding="utf-8"))
|
|
data["trace_summary"] = f"trace {index}"
|
|
path.write_text(json.dumps(data), encoding="utf-8")
|
|
os.utime(path, ns=(index + 1, index + 1))
|
|
ctx = SimpleNamespace(drive_root=tmp_path / "fork", budget_drive_root=str(canonical))
|
|
|
|
first = json.loads(_handle_recent_tasks(
|
|
ctx, limit=1, include_results=True, include_traces=True,
|
|
))
|
|
assert first["next"] == {
|
|
"limit": 1,
|
|
"offset": 1,
|
|
"snapshot": first["snapshot"],
|
|
"include_results": True,
|
|
"include_traces": True,
|
|
}
|
|
second = json.loads(_handle_recent_tasks(ctx, **first["next"]))
|
|
assert "error" not in second
|
|
assert second["tasks"][0]["result"] == "result 1"
|
|
assert second["tasks"][0]["trace_summary"] == "trace 1"
|