diff --git a/ouroboros/routing_wait.py b/ouroboros/routing_wait.py index 1028aeb1c..18e2f61df 100644 --- a/ouroboros/routing_wait.py +++ b/ouroboros/routing_wait.py @@ -17,6 +17,13 @@ from typing import Any, Dict PROMOTE_CONFIRM_POLL_SEC = 0.05 +def is_own_admission_stub(result: Any, routing_token: str) -> bool: + """The admission gates' form: a stub is read around ONLY by the token that wrote + it. No token is no claim, so a tokenless admission meets any stub as a row that + owns the id (``is_emitted_admission_stub`` without a token is for readers).""" + token = str(routing_token or "").strip() + return bool(token) and is_emitted_admission_stub(result, token) + def _confirm_wait_sec(timeout_sec: float | None) -> float: """The one bound of both confirmation waits (``runtime_limits``), read at the wait itself so this routing leaf keeps no import-time edge into the limits.""" diff --git a/ouroboros/server_routing_context.py b/ouroboros/server_routing_context.py index a0edf584f..45a9f460a 100644 --- a/ouroboros/server_routing_context.py +++ b/ouroboros/server_routing_context.py @@ -270,6 +270,14 @@ def _project_routing_manifest(ctx: Any, project_id: str) -> Dict[str, Any]: } +def _not_a_root_result(row: Dict[str, Any]) -> bool: + """A row that is never "the project's last result": a child's result, or a + promote's emitted stub (an admission still pending, no result at all).""" + from ouroboros.routing_wait import is_emitted_admission_stub + + return _is_child_result(row) or is_emitted_admission_stub(row) + + def _latest_project_task_result(ctx: Any, project_id: str) -> Optional[Dict[str, Any]]: """Newest ROOT task result bound to ``project_id`` (a child's is never the room's continuation: the promote door refuses it, ``_is_child_result``) WITHOUT replaying the whole @@ -309,7 +317,7 @@ def _latest_project_task_result(ctx: Any, project_id: str) -> Optional[Dict[str, if pointer: pointed = load_task_result(ctx.DRIVE_ROOT, pointer) if isinstance(pointed, dict) and str(pointed.get("project_id") or "") == project_id: - if not _is_child_result(pointed): + if not _not_a_root_result(pointed): return pointed pointer = "" # a child-stamped pointer is provably wrong, not in flight: heal it log.debug( @@ -348,7 +356,7 @@ def _latest_project_task_result(ctx: Any, project_id: str) -> Optional[Dict[str, if candidate is None: uncertain = True continue - if str(candidate.get("project_id") or "") != project_id or _is_child_result(candidate): + if str(candidate.get("project_id") or "") != project_id or _not_a_root_result(candidate): continue row = candidate # The match's whole equal-mtime group is read to its end — across the @@ -360,7 +368,7 @@ def _latest_project_task_result(ctx: Any, project_id: str) -> Optional[Dict[str, other = read_json_dict(tied) if other is None: uncertain = True - elif (str(other.get("project_id") or "") == project_id and not _is_child_result(other) + elif (str(other.get("project_id") or "") == project_id and not _not_a_root_result(other) and _order(other) > _order(row)): row = other break diff --git a/ouroboros/tools/control_routing.py b/ouroboros/tools/control_routing.py index 847cbe763..fe16a729e 100644 --- a/ouroboros/tools/control_routing.py +++ b/ouroboros/tools/control_routing.py @@ -122,6 +122,7 @@ def _predecessor_door_refusal( project to compare against, and an absent project is not a match: there the host's own list still decides. """ + from ouroboros.routing_wait import is_emitted_admission_stub from ouroboros.server_routing_context import _is_child_result from ouroboros.task_status import SETTLED_STATUSES @@ -131,6 +132,9 @@ def _predecessor_door_refusal( "belongs to - a child's work is reachable through its root" ) status = str(result.get("status") or "") + if is_emitted_admission_stub(result): + return ("the selected predecessor is a promote whose admission is still pending, not a " + "result; read get_task_result on it, and name a finished root instead") if status not in SETTLED_STATUSES: return ( f"the selected predecessor is still live (status {status or 'unknown'}); " diff --git a/ouroboros/tools/control_task_results.py b/ouroboros/tools/control_task_results.py index e55a12eb2..faf88d977 100644 --- a/ouroboros/tools/control_task_results.py +++ b/ouroboros/tools/control_task_results.py @@ -26,7 +26,7 @@ from ouroboros.task_status import ( wait_for_effective_tasks, ) from ouroboros.tools.registry import ToolContext -from ouroboros.utils import truncate_review_artifact +from ouroboros.utils import truncate_review_artifact, utc_now_iso from ouroboros.tools.tool_result import ToolResult, _publish_tool_result @@ -208,10 +208,11 @@ def _get_task_result( return _publish_tool_result(ctx, ToolResult( status="unavailable", code="LEGACY_UNAVAILABLE", text=( - f"Task {task_id}: admission pending since {since} - the promote was emitted and " - "the supervisor has not confirmed or refused it yet. This id is durably reserved: " - f"call get_task_result({task_id}) again to read the outcome, and do not promote " - "the same work a second time." + f"Task {task_id}: admission pending since {since} (now {utc_now_iso()}) - the promote " + "was emitted and this row carries no supervisor receipt yet, neither scheduled nor " + f"refused. Read get_task_result({task_id}) again before promoting the same work: the " + "supervisor answers within seconds of draining its queue. If the row is still " + "pending minutes later, the event did not land and a new promote is the way forward." ), )) if bool(include_authority) or bool(include_work_order_source) or bool(include_completion_source): diff --git a/ouroboros/tools/registry_guards.py b/ouroboros/tools/registry_guards.py index 6b35bc81e..6d8036a37 100644 --- a/ouroboros/tools/registry_guards.py +++ b/ouroboros/tools/registry_guards.py @@ -677,7 +677,7 @@ def _direct_shell_write_block(self, raw_cmd: Any, work_dir: pathlib.Path, runtim writable = ", ".join(dict.fromkeys( f"{root}={pathlib.Path(base).resolve(strict=False)}" for root, base, _source, _skill in roots if not (light_internal and pathlib.Path(base).resolve(strict=False) == system_repo))) or "(none)" - reason = ("runtime_mode=light keeps the Ouroboros repository and runtime data read-only" + reason = ("runtime_mode=light keeps the Ouroboros repository and runtime control data read-only" if light_internal else "explicit write target is outside every root this task may write") return ToolResult( status="blocked", code="LIGHT_MODE_BLOCKED" if light_internal else "WORKSPACE_BLOCKED", diff --git a/prompts/SYSTEM.md b/prompts/SYSTEM.md index a99ed23b5..cfdb61f11 100644 --- a/prompts/SYSTEM.md +++ b/prompts/SYSTEM.md @@ -164,8 +164,8 @@ canonical deliverables, `skill_payload` for reviewed skill payloads, and `user_files` for user-visible files under the owner's home (a bare filename lands in the visible Deliverables folder, not the home root). `subagent_projects` and `deliverables` are read-only (never written or a -shell cwd); a subagent reads its lineage's task files, never a sibling's, and -a read-only one also reads `deliverables`. +shell cwd); a read-only subagent reads `deliverables`, every subagent its +parent's and root's task files. My cognitive memory has first-class tools — `update_identity`, `update_scratchpad`, `knowledge_write` — and I never reach for diff --git a/supervisor/events_project_routing.py b/supervisor/events_project_routing.py index 104136bcf..7e66c6b95 100644 --- a/supervisor/events_project_routing.py +++ b/supervisor/events_project_routing.py @@ -256,6 +256,20 @@ def _rollback_promoted_pending( return removed +def _own_emitted_stub(ctx: Any, task_id: str, routing_token: str) -> bool: + """Whether the row on disk is THIS promote's emitted pre-receipt (#1160). A + refusal that writes no result of its own must still replace it, or the + reconciliation read answers "admission pending" for ever.""" + try: + from ouroboros.routing_wait import is_own_admission_stub + from ouroboros.task_results import load_task_result + + return is_own_admission_stub(load_task_result(ctx.DRIVE_ROOT, task_id), routing_token) + except Exception: + log.warning("promote: emitted-stub lookup failed for %s", task_id, exc_info=True) + return False + + def _persist_promote_rejection( ctx: Any, evt: Dict[str, Any], @@ -407,7 +421,7 @@ def _promote_chat_to_task_outcome(evt: Dict[str, Any], ctx: Any) -> Dict[str, An "task_id": task_id, "reservation_owned": False, } - if blocked["reason"] != "duplicate_task_id": + if blocked["reason"] != "duplicate_task_id" or _own_emitted_stub(ctx, task_id, routing_token): _persist_promote_rejection(ctx, evt, blocked) _emit_routing_receipt( ctx, @@ -564,7 +578,8 @@ def _promote_chat_to_task_outcome(evt: Dict[str, Any], ctx: Any) -> Dict[str, An reason="promote_chat_to_task_rejected", ) supervisor_queue.release_task_admission(task_id, routing_token) - if str(outcome.get("reason") or "") != "attachment_admission_rejected": + if (str(outcome.get("reason") or "") != "attachment_admission_rejected" + or _own_emitted_stub(ctx, task_id, routing_token)): _persist_promote_rejection(ctx, evt, outcome) _emit_routing_receipt( ctx, diff --git a/supervisor/queue.py b/supervisor/queue.py index af8dbf9da..e97395f61 100644 --- a/supervisor/queue.py +++ b/supervisor/queue.py @@ -209,12 +209,12 @@ def enqueue_task( t["_admission_blocked"] = "duplicate_task_id" return t try: - from ouroboros.routing_wait import is_emitted_admission_stub + from ouroboros.routing_wait import is_own_admission_stub from ouroboros.task_results import load_task_result stored = load_task_result(DRIVE_ROOT, task_id, strict=True) # The emitted promote stub (#1160) belongs to THIS admission token: # its own enqueue reads around it, any other row still owns the id. - if stored and not is_emitted_admission_stub(stored, admission_token): + if stored and not is_own_admission_stub(stored, admission_token): if ADMISSION_RESERVATIONS.get(task_id) == admission_token: ADMISSION_RESERVATIONS.pop(task_id, None) t["_admission_blocked"] = "duplicate_task_id" diff --git a/supervisor/task_admission.py b/supervisor/task_admission.py index 4d4d04ecf..54ec23af0 100644 --- a/supervisor/task_admission.py +++ b/supervisor/task_admission.py @@ -624,9 +624,9 @@ def reserve_task_admission( ) or {} except Exception: return {"status": "blocked", "reason": "task_id_lookup_failed"} - from ouroboros.routing_wait import is_emitted_admission_stub + from ouroboros.routing_wait import is_own_admission_stub - if existing and not is_emitted_admission_stub(existing, token): + if existing and not is_own_admission_stub(existing, token): # The emitted stub of THIS admission is its own pre-receipt (#1160), not # another task owning the id: the request it belongs to still reserves. admission = existing.get("promotion_admission") diff --git a/supervisor/worker_promotion.py b/supervisor/worker_promotion.py index 90956f37f..de9abd2ed 100644 --- a/supervisor/worker_promotion.py +++ b/supervisor/worker_promotion.py @@ -147,7 +147,7 @@ def _canonical_promoted_repair_constraint(value: Any) -> tuple[Optional[dict], s }, "" -def _promote_duplicate_reason(task_id: str, ctx: Any, *, admission_token: str = "") -> str: +def _promote_duplicate_reason(task_id: str, ctx: Any, *, admission_token: str) -> str: """Fail closed if a promoted id is already live, durable, or uncheckable. A row that is only THIS admission's emitted stub (#1160) is its own @@ -161,13 +161,13 @@ def _promote_duplicate_reason(task_id: str, ctx: Any, *, admission_token: str = for row in list(pending or []) ) or task_id in (running or {}) try: - from ouroboros.routing_wait import is_emitted_admission_stub + from ouroboros.routing_wait import is_own_admission_stub from ouroboros.task_results import load_task_result stored = load_task_result( getattr(ctx, "DRIVE_ROOT", _pool().DRIVE_ROOT), task_id, strict=True, ) - stored_duplicate = bool(stored) and not is_emitted_admission_stub( + stored_duplicate = bool(stored) and not is_own_admission_stub( stored, admission_token, ) except Exception: diff --git a/tests/test_promote_admission_pending.py b/tests/test_promote_admission_pending.py index 8cd7c1b3c..7defc0ced 100644 --- a/tests/test_promote_admission_pending.py +++ b/tests/test_promote_admission_pending.py @@ -13,6 +13,10 @@ import types import pytest +from tests.test_swarm_host_admission import host as _swarm_host + +swarm_host = _swarm_host # the real supervisor handler needs a real host + class _DeadQueue: """A supervisor that accepted the event and has not answered yet.""" @@ -192,6 +196,44 @@ def test_the_emitted_stub_never_owns_its_own_admissions_id(tmp_path, monkeypatch ) == "duplicate_task_id" +def test_a_stub_is_read_around_only_by_the_token_that_wrote_it(tmp_path, monkeypatch, _short_confirmation_window): + """The other direction of the three gates: a promote's stub belongs to its own + routing token. Any OTHER admission meets it as a row that owns the id, and a + `requested` row that carries no emitted admission is never a stub at all.""" + import supervisor.queue as supervisor_queue + import supervisor.workers as workers + from ouroboros.routing_wait import is_emitted_admission_stub, is_own_admission_stub + from ouroboros.task_results import load_task_result + from ouroboros.tools.control_routing import _promote_chat_to_task + + ctx = _promote_ctx(tmp_path, _DeadQueue()) + _promote_chat_to_task(ctx, "Build the racer", workspace="none", predecessor_task_id="") + task_id = ctx.event_queue.events[0]["task_id"] + monkeypatch.setattr(supervisor_queue, "DRIVE_ROOT", tmp_path) + monkeypatch.setattr(supervisor_queue, "PENDING", []) + monkeypatch.setattr(supervisor_queue, "RUNNING", {}) + monkeypatch.setattr(supervisor_queue, "ADMISSION_RESERVATIONS", {}) + host = types.SimpleNamespace(DRIVE_ROOT=tmp_path, PENDING=[], RUNNING={}) + + assert supervisor_queue.reserve_task_admission( + task_id, "other-token", drive_root=tmp_path) == {"status": "blocked", "reason": "duplicate_task_id"} + assert workers._promote_duplicate_reason(task_id, host, admission_token="other-token") == "duplicate_task_id" + queued = supervisor_queue.enqueue_task({ + "id": task_id, "type": "task", "_require_unique_task_id": True, "_admission_token": "other-token"}) + assert queued.get("_admission_blocked") == "duplicate_task_id" + + stub = load_task_result(tmp_path, task_id) + assert is_emitted_admission_stub(stub) and not is_emitted_admission_stub(stub, "other-token") + # The gates' form: no token is no claim, so a tokenless admission never reads around it. + assert is_own_admission_stub(stub, stub["promotion_admission"]["routing_token"]) + assert not is_own_admission_stub(stub, "") and not is_own_admission_stub(stub, "other-token") + assert supervisor_queue.enqueue_task({ + "id": task_id, "type": "task", "_require_unique_task_id": True, + }).get("_admission_blocked") == "duplicate_task_id" + assert not is_emitted_admission_stub({**stub, "status": "completed"}) + assert not is_emitted_admission_stub({**stub, "promotion_admission": {"status": "scheduled"}}) + + def test_one_runtime_limit_bounds_both_promote_confirmation_waits(): """The two confirmation windows were byte-identical literals in two modules.""" from ouroboros import routing_wait, runtime_limits @@ -203,3 +245,80 @@ def test_one_runtime_limit_bounds_both_promote_confirmation_waits(): assert routing_wait._confirm_wait_sec(None) == runtime_limits.get_promote_confirm_wait_sec() assert routing_wait._confirm_wait_sec(0.05) == 0.05 and routing_wait._confirm_wait_sec(-1) == 0.0 assert control_events._PROMOTE_CONFIRM_TIMEOUT_SEC == runtime_limits.get_promote_confirm_wait_sec() + + +def _emit_into(root, monkeypatch): + """A promote emitted at a supervisor that has not answered: the stub is on disk.""" + from ouroboros.tools import control_events + from ouroboros.tools.control_routing import _promote_chat_to_task + + monkeypatch.setattr(control_events, "_PROMOTE_CONFIRM_TIMEOUT_SEC", 0.05) + monkeypatch.setattr(control_events, "_PROMOTE_CONFIRM_POLL_SEC", 0.005) + ctx = _promote_ctx(root, _DeadQueue()) + out = _promote_chat_to_task(ctx, "Build the racer", workspace="none", predecessor_task_id="") + assert out.startswith("⚠️ PROMOTE_UNCONFIRMED"), out + return ctx, ctx.event_queue.events[0] + + +def _attachments_rejected(task_id): + return {"status": "needs_manual_target", "reason": "attachment_admission_rejected", + "detail": "- a.png: rejected (reason=staging_unavailable, ordinal=0)", + "task_id": task_id, "attachment_manifest": []} + + +def test_a_refusal_that_writes_no_result_of_its_own_still_replaces_the_stub(swarm_host, monkeypatch): + """The attachment refusal persists no task result by design. Over an emitted + stub that silence left "admission pending" on disk for ever, so the model was + told not to promote again and the owner's work never started.""" + import supervisor.workers as workers + from ouroboros.task_results import load_task_result + from ouroboros.tools.control_task_results import _get_task_result + from supervisor.events_project_routing import _handle_promote_chat_to_task + + ctx, event = _emit_into(swarm_host.root, monkeypatch) + task_id = event["task_id"] + assert load_task_result(swarm_host.root, task_id)["promotion_admission"]["status"] == "emitted" + monkeypatch.setattr(workers, "promote_chat_to_task", lambda evt, c: _attachments_rejected(task_id)) + + _handle_promote_chat_to_task(event, swarm_host.ctx) + + stored = load_task_result(swarm_host.root, task_id) + assert stored["status"] == "failed" + assert stored["promotion_admission"]["status"] == "rejected" + assert stored["promotion_admission"]["reason"] == "attachment_admission_rejected" + read = _get_task_result(ctx, task_id) + assert "admission pending" not in read and "attachment_admission_rejected" in read + + +def test_the_attachment_refusal_without_a_stub_still_writes_no_result(swarm_host, monkeypatch): + """The quiet direction: that refusal's own contract is untouched. With no + emitted stub on disk (a host-issued promote) it persists nothing, as before.""" + import supervisor.workers as workers + from ouroboros.task_results import load_task_result + from supervisor.events_project_routing import _handle_promote_chat_to_task + + event = {"type": "promote_chat_to_task", "task_id": "hostpromote000001", "routing_token": "tok-host", + "objective": "Build the racer", "chat_id": 1, "workspace": "none"} + monkeypatch.setattr(workers, "promote_chat_to_task", + lambda evt, c: _attachments_rejected("hostpromote000001")) + + _handle_promote_chat_to_task(event, swarm_host.ctx) + + assert not load_task_result(swarm_host.root, "hostpromote000001") + + +def test_the_pending_sentence_never_forbids_a_new_promote_for_ever(tmp_path, _short_confirmation_window): + """The row proves one thing: emitted, no receipt yet. An event can be lost (a + supervisor restart drops its in-memory queue), so the sentence hands the + decision back with the facts instead of a standing ban.""" + from ouroboros.tools.control_routing import _promote_chat_to_task + from ouroboros.tools.control_task_results import _get_task_result + + ctx = _promote_ctx(tmp_path, _DeadQueue()) + _promote_chat_to_task(ctx, "Continue the racer", workspace="none", predecessor_task_id="") + task_id = ctx.event_queue.events[0]["task_id"] + + read = _get_task_result(ctx, task_id) + assert "admission pending since" in read and f"get_task_result({task_id})" in read + assert "do not promote the same work a second time" not in read + assert "did not land" in read and "new promote" in read diff --git a/tests/test_promote_event_transport.py b/tests/test_promote_event_transport.py index 0daf97cdd..6a2269c5d 100644 --- a/tests/test_promote_event_transport.py +++ b/tests/test_promote_event_transport.py @@ -786,7 +786,7 @@ def test_exact_id_ingress_fails_closed_on_unreadable_result(monkeypatch, tmp_pat duplicate_reason = workers._promote_duplicate_reason( "malformed-id", types.SimpleNamespace( DRIVE_ROOT=tmp_path, PENDING=[], RUNNING={}, - ), + ), admission_token="", ) assert reservation == {"status": "blocked", "reason": "task_id_lookup_failed"} diff --git a/tests/test_promote_predecessor_door.py b/tests/test_promote_predecessor_door.py index 9495de53a..67a25cd90 100644 --- a/tests/test_promote_predecessor_door.py +++ b/tests/test_promote_predecessor_door.py @@ -73,6 +73,33 @@ def test_a_root_the_room_manifest_lists_is_still_addressable(tmp_path): assert evt["predecessor_authority_source"]["tool"] == "get_task_result" +def test_a_listed_row_is_accepted_only_with_the_pointer_the_host_issued_for_it(tmp_path): + """A row the host showed carries its own host-issued authority source, and only + that one is accepted: rebuilding a source for a shown row would make a tampered + manifest row indistinguishable from a host-built one.""" + import copy + + import server + from ouroboros.projects_registry import create_project + from ouroboros.task_results import write_task_result + + project = create_project(tmp_path, "racer", name="Racer") + write_task_result(tmp_path, "racer-root", "completed", project_id="racer", + objective="the root", ts="2026-08-10T00:00:01Z") + metadata = server._decision_turn_metadata( + _host_ctx(tmp_path), int(project["chat_id"]), "room-2", {"project_id": "racer"}, + ) + tampered = copy.deepcopy(metadata) + tampered["project_last_task_result"]["authority_source"] = {"kind": "invented"} + for row in tampered["project_routing_manifest"]["final_results"]: + row["authority_source"] = {"kind": "invented"} + + evt: dict = {} + assert "no readable authority source" in _door(_room_ctx(tmp_path, tampered), "racer-root", evt) + assert evt == {} + assert _door(_room_ctx(tmp_path, metadata), "racer-root") == "" # the host's own row passes + + def test_a_room_root_older_than_the_list_is_addressable_all_the_same(tmp_path, monkeypatch): """The 16-row cap is a HINT window, never the door: the night's root was a finished root of this very project, and only the cap hid it.""" @@ -308,6 +335,34 @@ def test_a_pointer_a_child_stamped_before_the_rule_heals_onto_the_root(tmp_path) assert get_project(tmp_path, "racer")["last_task_result_id"] == "racer-root" +def test_a_pending_promote_is_neither_the_last_result_nor_a_predecessor(tmp_path): + """An emitted stub carries the project's id and is the newest file, but it is an + admission still pending, not a result: the lookup answers with the root and never + stamps the stub, and the door says what it is instead of "steer this live root".""" + import os + + import server + from ouroboros.projects_registry import create_project, get_project + from ouroboros.task_results import task_results_dir, write_task_result + + create_project(tmp_path, "racer", name="Racer") + write_task_result(tmp_path, "racer-root", "completed", project_id="racer", result="root answer") + write_task_result(tmp_path, "racer-promote", "requested", project_id="racer", promotion_admission={ + "status": "emitted", "routing_token": "tok-1", "emitted_at": "2026-09-21T10:00:00Z"}) + results = task_results_dir(tmp_path, create=False) + os.utime(results / "racer-root.json", (1_000, 1_000)) + os.utime(results / "racer-promote.json", (2_000, 2_000)) + + assert server._latest_project_task_result(_host_ctx(tmp_path), "racer")["task_id"] == "racer-root" + assert get_project(tmp_path, "racer")["last_task_result_id"] == "racer-root" + + refusal = _door(_room_ctx(tmp_path, {}), "racer-promote") + assert "admission is still pending" in refusal and "steer_task" not in refusal + # The quiet direction: a root that really is live keeps its own sentence. + write_task_result(tmp_path, "racer-live", "running", project_id="racer") + assert "steer_task" in _door(_room_ctx(tmp_path, {}), "racer-live") + + def test_the_room_manifest_carries_cancel_facts_and_an_honest_omission_count(tmp_path): import server from ouroboros.cancel_intents import request_cancel @@ -327,7 +382,11 @@ def test_the_room_manifest_carries_cancel_facts_and_an_honest_omission_count(tmp ts="2026-08-10T00:00:02Z") running = {"racer-live": {"task": {"id": "racer-live", "project_id": "racer", "title": "Live", "objective": "in flight"}, - "started_at": "2026-08-10T00:00:03Z"}} + "started_at": "2026-08-10T00:00:03Z"}, + # Another project's live root is none of this room's business. + "other-live": {"task": {"id": "other-live", "project_id": "other", + "title": "Elsewhere", "objective": "not this room"}, + "started_at": "2026-08-10T00:00:04Z"}} request_cancel(tmp_path, "racer-live", source="owner", reason="stop it") manifest = server._decision_turn_metadata( @@ -343,6 +402,15 @@ def test_the_room_manifest_carries_cancel_facts_and_an_honest_omission_count(tmp assert live["task_id"] == "racer-live" and live["cancel_state"] == "pending" assert manifest["omissions"]["children"] == 1 assert manifest["omissions"]["final_results"] == 0 + assert manifest["omissions"]["active_roots"] == 0 + + # The live list is bounded too, and says how many of this room's roots it left out. + crowd = {f"racer-live-{index:02d}": {"task": {"id": f"racer-live-{index:02d}", "project_id": "racer"}, + "started_at": "2026-08-10T00:00:05Z"} for index in range(43)} + crowded = server._decision_turn_metadata( + _host_ctx(tmp_path, running=crowd), int(project["chat_id"]), "room-8", {"project_id": "racer"}, + )["project_routing_manifest"] + assert len(crowded["active_roots"]) == 40 and crowded["omissions"]["active_roots"] == 3 def test_the_manifest_row_cap_is_one_runtime_limit(tmp_path, monkeypatch): diff --git a/tests/test_tool_roots_lineage_read.py b/tests/test_tool_roots_lineage_read.py index fc62f4c68..5bbc12eb9 100644 --- a/tests/test_tool_roots_lineage_read.py +++ b/tests/test_tool_roots_lineage_read.py @@ -184,6 +184,26 @@ def test_lineage_is_read_only_even_for_a_top_level_parent_drive(geometry): assert target.read_text(encoding="utf-8") == before +def test_the_lineage_redirect_is_a_read_redirect_whatever_the_matrix_says(geometry): + """The write above is refused by the matrix first, so it never reaches the + resolver. This one does: the redirect onto a lineage root exists for READ + operations alone, so a profile whose matrix allows the write (an acting child + in cyber_pro) still cannot be resolved into its parent's drive.""" + from ouroboros.tool_access import _resolve_target_in_selected_base + + _registry, ctx = child_registry(geometry, acting=True) + target = geometry.parent_drive / "triage-draft.json" + own_base = geometry.headless / "task_drives" / CHILD + + assert _resolve_target_in_selected_base( + ctx, root="task_drive", base_path=own_base, path=str(target), operation="read", + ) == target.resolve() + for operation in ("write", "edit"): + with pytest.raises(ValueError): + _resolve_target_in_selected_base( + ctx, root="task_drive", base_path=own_base, path=str(target), operation=operation) + + # --- owner 7A: an absolute path without a root runs under the root holding it -- def test_child_reads_its_parents_task_drive_by_absolute_path_and_no_root(geometry): @@ -345,6 +365,18 @@ def test_child_lists_the_parents_drive_with_secret_names_hidden(geometry): assert any("hidden from this subagent" in item for item in items), items +def test_child_lists_deliverables_with_secret_names_hidden(geometry): + """Deliverables is a new listing root for the child, so it gets the same + secret-name filter as every other root it lists; an ordinary file stays.""" + (geometry.deliverables / ".env").write_text("SECRET_TOKEN=sk-secret\n", encoding="utf-8") + registry, _ctx = child_registry(geometry) + + items = json.loads(registry.execute("list_files", {"root": "deliverables", "path": str(geometry.deliverables)})) + + assert "answer.txt" in items and ".env" not in items, items + assert any("hidden from this subagent" in item for item in items), items + + # --- the pure lineage function ------------------------------------------------ def test_lineage_task_ids_are_own_parent_and_root_and_nothing_else(geometry): diff --git a/tests/test_usage_compaction_fingerprint.py b/tests/test_usage_compaction_fingerprint.py index f0c88a5e2..92768957c 100644 --- a/tests/test_usage_compaction_fingerprint.py +++ b/tests/test_usage_compaction_fingerprint.py @@ -383,10 +383,16 @@ def test_a_peer_process_does_not_refold_what_another_just_folded(data_root, monk assert _folds(data_root) == 1 assert _skip_events(data_root) == [] # declined before the pass, not aborted inside it + # The floor is what that pass READ plus the retry window, not the bare size: a + # ledger that regrew just past the old size is still inside the window (at the + # measured 0.02 % gain that is a few KB, the very cascade this guard removes). + _grow_past(data_root, monkeypatch, before.st_size + 1, "peer-growth") + assert _trigger(data_root) is False + assert len(entered) == 1 + # Quiet on real growth: once the file passes what that pass READ plus the # retry window, the peer folds on its own. monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_RETRY_GROWTH_BYTES", 1) - _grow_past(data_root, monkeypatch, before.st_size + 1, "peer-growth") assert _trigger(data_root) is True assert len(entered) == 2 assert int(_ledger_rows(data_root)[0]["compaction_epoch"]) == 2