mirror of
https://github.com/razzant/ouroboros.git
synced 2026-10-03 12:18:39 +00:00
NOT_REVIEWED integration checkpoint for READY 02-COGNITION. Contains linked knowledge, book composition, source retention, Nano view primitives and native multiwindow review. Full phase review, shared model/loop wiring, book migration and live acceptance remain pending. No version bump, tag or public delivery.
159 lines
8.8 KiB
Python
159 lines
8.8 KiB
Python
"""Cancellation observations describe sources without claiming caller prose is fact."""
|
|
|
|
import json
|
|
import queue
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
from starlette.applications import Starlette
|
|
from starlette.routing import Route
|
|
from starlette.testclient import TestClient
|
|
|
|
from ouroboros import cancel_intents, delegate_custody
|
|
from ouroboros.task_results import load_task_result, write_task_result
|
|
from ouroboros.task_status import observe_cancellation_target
|
|
from ouroboros.tools.join_ledger import _cancel_task
|
|
from ouroboros.utils import atomic_write_json, utc_now_iso
|
|
|
|
|
|
def _queue(root, task_id, *, stale=False):
|
|
atomic_write_json(root / "state" / "queue_snapshot.json", {
|
|
"ts": "2000-01-01T00:00:00Z" if stale else utc_now_iso(),
|
|
"running": [{"id": task_id}], "pending": [],
|
|
})
|
|
|
|
|
|
def _caller(root):
|
|
return SimpleNamespace(task_id="parent", drive_root=root, pending_events=[], event_queue=queue.Queue(),
|
|
task_metadata={"root_task_id": "parent", "delegation_role": "root"})
|
|
|
|
|
|
def test_agent_cancel_preserves_recorded_start_and_caller_reason_separately(tmp_path):
|
|
write_task_result(tmp_path, "child", "running", parent_task_id="parent", root_task_id="parent", delegation_role="subagent",
|
|
started_at="2026-09-06T01:00:00+00:00", accounted_upper_bound_usd=3.25, cost_final=False)
|
|
_queue(tmp_path, "child")
|
|
delegate_custody.emit(tmp_path, delegate_custody.STARTED, {"run_id": "r1", "task_id": "child"})
|
|
reason = "I think it never started and cost nothing"
|
|
response = _cancel_task(_caller(tmp_path), "child", reason)
|
|
assert response.startswith("Cancel requested:") and "cancel_state=pending" in response
|
|
intent = cancel_intents.active_intent(tmp_path, "child")
|
|
observation = intent["observation"]
|
|
assert intent["reason"] == reason
|
|
assert observation["observed_task_id"] == "child"
|
|
assert observation["matches_cancel_target"] is True
|
|
assert observation["queue_snapshot"]["status"] == "running"
|
|
assert observation["queue_snapshot"]["fresh"] is True
|
|
assert observation["task_result"]["started_at"] == "2026-09-06T01:00:00+00:00"
|
|
assert observation["task_result"]["cost"]["accounted_upper_bound_usd"] == 3.25
|
|
assert observation["delegated_execution"]["delegated_runs_started"] == 1
|
|
assert observation["request_origin"] == {"kind": "agent_task", "task_id": "parent"}
|
|
assert "separate from the caller's reason" in response
|
|
assert '"delegated_runs_started": 1' in response
|
|
assert load_task_result(tmp_path, "child")["status"] == "running"
|
|
events = [json.loads(line) for line in (tmp_path / "logs" / "supervisor.jsonl").read_text().splitlines()]
|
|
assert next(row for row in events if row.get("event") == "requested")["observation"] == observation
|
|
from supervisor.cancel_publication import _intent_outcome_fields
|
|
fields = _intent_outcome_fields(intent)
|
|
assert fields["parent_decision_reason"] == reason and fields["cancel_observation"] == observation
|
|
|
|
|
|
def test_stale_snapshot_is_unknown_not_the_fail_open_live_hint(tmp_path):
|
|
write_task_result(tmp_path, "child", "running")
|
|
_queue(tmp_path, "child", stale=True)
|
|
from ouroboros.task_status import task_has_live_queue_ownership
|
|
assert task_has_live_queue_ownership(tmp_path, "child") is True
|
|
observation = observe_cancellation_target(tmp_path, "child")
|
|
queue_fact = observation["queue_snapshot"]
|
|
assert {key: queue_fact[key] for key in ("status", "ts", "fresh")} == {
|
|
"status": "unknown", "ts": "2000-01-01T00:00:00Z", "fresh": False,
|
|
}
|
|
assert queue_fact["freshness"] == "stale" and queue_fact["age_sec"] > 10
|
|
assert queue_fact["source"] == "state/queue_snapshot.json"
|
|
assert observation["task_result"]["started_at"] is None
|
|
|
|
|
|
def _retry(root):
|
|
write_task_result(root, "original", "interrupted", root_task_id="original", delegation_role="root",
|
|
retry_task_id="retry", superseded_by="retry")
|
|
write_task_result(root, "retry", "running", root_task_id="original", delegation_role="root",
|
|
original_task_id="original", timeout_retry_from="original", supersedes_task_id="original")
|
|
_queue(root, "retry")
|
|
|
|
|
|
def test_observation_names_resolved_retry_and_discloses_a_later_target_change(tmp_path):
|
|
_retry(tmp_path)
|
|
observation = observe_cancellation_target(tmp_path, "original")
|
|
assert observation["requested_task_id"] == "original" and observation["observed_task_id"] == "retry"
|
|
intent = cancel_intents.request_cancel(tmp_path, "original", observation=observation)
|
|
assert intent["task_id"] == "retry" and intent["observation"]["matches_cancel_target"] is True
|
|
# Widening an existing leaf intent rekeys it to the logical cascade root;
|
|
# it keeps the observed physical identity instead of rewriting that fact.
|
|
widened = cancel_intents.request_cancel(tmp_path, "original", scope="cascade", observation=observation)
|
|
assert widened["task_id"] == "original"
|
|
assert widened["observation"]["observed_task_id"] == "retry"
|
|
assert widened["observation"]["matches_cancel_target"] is False
|
|
|
|
|
|
def test_agent_cancel_of_a_foreign_task_records_origin_without_a_parent_decision(tmp_path):
|
|
"""A cancel outside the caller's own lineage must not discharge the REAL
|
|
parent's disposition duty (D#7). ``requested_by`` is the parent-decision
|
|
trigger, so the initiator fact rides ``source`` + ``request_origin``."""
|
|
write_task_result(tmp_path, "stranger", "running",
|
|
parent_task_id="someone-else", root_task_id="someone-else",
|
|
delegation_role="subagent")
|
|
_queue(tmp_path, "stranger")
|
|
|
|
response = _cancel_task(_caller(tmp_path), "stranger", "not mine but stop it")
|
|
assert response.startswith("Cancel requested:")
|
|
intent = cancel_intents.active_intent(tmp_path, "stranger")
|
|
assert intent["requested_by"] == ""
|
|
assert intent["source"] == "agent_tool"
|
|
assert intent["observation"]["request_origin"] == {
|
|
"kind": "agent_task", "task_id": "parent"}
|
|
from supervisor.cancel_publication import _intent_outcome_fields
|
|
fields = _intent_outcome_fields(intent)
|
|
assert "parent_decision" not in fields
|
|
assert fields["cancel_observation"] == intent["observation"]
|
|
|
|
|
|
def test_completed_child_keeps_its_result(tmp_path):
|
|
write_task_result(tmp_path, "child", "completed", parent_task_id="parent", root_task_id="parent", delegation_role="subagent", result="finished work")
|
|
atomic_write_json(tmp_path / "state" / "queue_snapshot.json", {"ts": utc_now_iso(), "running": [], "pending": []})
|
|
response = _cancel_task(_caller(tmp_path), "child", "unneeded")
|
|
assert response.startswith("Nothing to cancel:")
|
|
assert cancel_intents.active_intent(tmp_path, "child") is None
|
|
assert load_task_result(tmp_path, "child")["result"] == "finished work"
|
|
|
|
|
|
@pytest.mark.parametrize("cascade", [False, True])
|
|
def test_http_observation_records_transport_without_inventing_owner_identity(tmp_path, monkeypatch, cascade):
|
|
from ouroboros.gateway import tasks
|
|
from supervisor import queue as task_queue
|
|
|
|
write_task_result(tmp_path, "task", "running")
|
|
_queue(tmp_path, "task")
|
|
monkeypatch.setattr(task_queue, "DRIVE_ROOT", tmp_path)
|
|
monkeypatch.setattr(task_queue, "task_has_live_ownership", lambda task_id: True)
|
|
monkeypatch.setattr(task_queue, "task_subtree_is_live", lambda task_id: True)
|
|
monkeypatch.setattr(task_queue, "cancel_task_custody", lambda task_id: task_queue.CANCEL_CANCELLED)
|
|
monkeypatch.setattr(tasks, "_run_cascade_cancel", lambda task_id: True)
|
|
monkeypatch.setattr("ouroboros.delegate_evidence.task_execution_evidence", lambda *_a: pytest.fail("HTTP must not scan delegated descendants"))
|
|
app = Starlette(routes=[Route("/api/tasks/{task_id}/cancel", tasks.api_task_cancel, methods=["POST"])])
|
|
app.state.drive_root = tmp_path
|
|
with TestClient(app) as client:
|
|
response = client.post("/api/tasks/task/cancel", json={"cascade": cascade})
|
|
assert response.status_code == 200
|
|
intent = cancel_intents.active_intent(tmp_path, "task")
|
|
assert intent["requested_by"] == ""
|
|
assert intent["observation"]["request_origin"] == {
|
|
"kind": "http_client", "source": "http_cascade" if cascade else "http_single"}
|
|
assert "delegated_execution" not in intent["observation"]
|
|
# The durable origin fact rides ``source`` + ``observation.request_origin``.
|
|
# ``requested_by`` is NOT a display label: any non-empty value is the
|
|
# PARENT-DECISION trigger, so naming the owner here would stamp
|
|
# ``parent_decision=cancelled`` on a child whose real parent decided
|
|
# nothing, and the parent's forced-finalization would treat that child
|
|
# result as already dispositioned (D#7).
|
|
from supervisor.cancel_publication import _intent_outcome_fields
|
|
assert "parent_decision" not in _intent_outcome_fields(intent)
|
|
assert intent["source"] == ("http_cascade" if cascade else "http_single")
|