mirror of
https://github.com/razzant/ouroboros.git
synced 2026-10-03 04:07:04 +00:00
The gate decision records why a plan review stayed open as a closed class on the typed execution axis (only some reviewers answered, none answered, answered but never closed), beside the existing awaiting case; the normal delivery rail stamps terminal_plan_review_open on the result exactly as the forced rails do. The owner cause table, in Python and in its browser twin, gains the sentences for those classes, for a task held by a blocking review, and for the standing limitations of an answer (a plan review still open at delivery, deferred sub-task results, an unrecovered tool failure, an internal error); phrases that named internal actors are reworded. The completion verdict becomes an assembler on both twins: one clause per fact, joined, never one fact chosen over another; the parity fixture carries the new production-shaped cases. The durable task_summary row carries the same clause as a reason_detail field for transports without a card. Forced prompts hand their one model call the same limitations as typed facts, priced by the existing reservation probe; a hold the owner's hurry released reaches the model once before its last word. Delivery no longer splits a terminal into an answer and a System notice: the terminal_host_notice field keeps its bytes on the result for every machine reader, and only the custody audit rides the send event as its own typed row. A Telegram-only owner, who has no task card, receives one short line on a non-clean root finish built from the stamped phase and the reason sentence; clean finishes stay opt-in. In the Reviews group a settled plan wave whose reviewers were too few for a verdict reads no verdict with its counts in the neutral tone instead of the stored DEGRADED word; reviewer rows name the model and quote the engine's reported sentence, and the slot id and failure code stay in the task detail and Logs. DESIGN.md and the architecture chapters state the same; two chapter byte budgets rise with their reasons.
185 lines
9.4 KiB
Python
185 lines
9.4 KiB
Python
"""The facts reach the mind before its last word (chat-voice C2, perception).
|
|
|
|
A forced rail hands its ONE model call the limitations it will record beside the
|
|
answer as typed ``key=value`` facts, never the owner-facing ``⚠️`` notice; and a
|
|
plan-review hold the mind was told about that the owner's hurry then released
|
|
reaches it exactly once. Every other gate transition already arrives by its own
|
|
route (the mind's own ``plan_task`` result, the settled-wave task message, the
|
|
forced prompt), so it buys no round.
|
|
|
|
New module rather than ``tests/test_owner_hurry_s3.py`` (933 lines, would cross the
|
|
1000-line target) or ``tests/test_delivery_forced_finalization.py`` (a frozen giant).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
# The plan-review engine is imported BEFORE the s3 helpers on purpose: importing
|
|
# ``tests.test_owner_hurry_s3`` (its gateway imports) ahead of the engine leaves a
|
|
# later ``tests/test_owner_hurry_custody.py`` collection in the same process reading
|
|
# a wave with an empty roster as resumable (a pre-existing import-order hazard,
|
|
# reproducible on the base with ``test_owner_hurry_s3 -> test_owner_hurry_custody``
|
|
# in one process); this module sorts before both, so it keeps that order safe.
|
|
import ouroboros.tools.plan_review # noqa: F401 (import order, see above)
|
|
from tests._delivery_candidate_shared import write_child, write_confirmed_disposition_fixture
|
|
from tests.test_delivery_forced_finalization import _forced_test_context
|
|
from tests.test_owner_hurry_s3 import _acceptance_ctx, _plan_state
|
|
|
|
OWNER_NOTICE = (
|
|
"\n\n⚠️ Plan review is still open (DEGRADED); work proceeded under the owner-selected "
|
|
"advisory enforcement."
|
|
)
|
|
|
|
|
|
def _held(ctx, *, content="answer"):
|
|
import ouroboros.loop as loop_mod
|
|
|
|
trace = {"reasoning_notes": []}
|
|
messages: list = []
|
|
held = loop_mod._enforce_swarm_actions(content, messages, SimpleNamespace(_ctx=ctx), trace, lambda _m: None)
|
|
return held, messages, trace
|
|
|
|
|
|
@pytest.fixture
|
|
def open_blocking_install(monkeypatch):
|
|
import ouroboros.loop as loop_mod
|
|
import ouroboros.task_results as tr
|
|
|
|
monkeypatch.setattr(loop_mod, "get_review_enforcement", lambda: "blocking")
|
|
monkeypatch.setattr(tr, "load_plan_review_state", lambda _root, _tid: _plan_state("open"))
|
|
|
|
|
|
def test_an_open_advisory_gate_costs_no_extra_round_on_first_sight(tmp_path, monkeypatch):
|
|
"""Q4=A: no reminder before every final. The marker is not seeded by an observation."""
|
|
import ouroboros.loop as loop_mod
|
|
import ouroboros.task_results as tr
|
|
|
|
monkeypatch.setattr(loop_mod, "get_review_enforcement", lambda: "advisory")
|
|
monkeypatch.setattr(tr, "load_plan_review_state", lambda _root, _tid: _plan_state("open"))
|
|
ctx = _acceptance_ctx(tmp_path, latched=False)
|
|
ctx.task_metadata = {"force_plan": True}
|
|
held, messages, trace = _held(ctx)
|
|
assert held is False and messages == []
|
|
assert trace["force_plan_decision"]["allow"] is True
|
|
assert not hasattr(ctx, "_plan_gate_told_identity")
|
|
|
|
|
|
def test_a_hold_released_by_owner_hurry_reaches_the_mind_once(tmp_path, open_blocking_install):
|
|
"""Fires only on a change the mind cannot see otherwise, one shot, typed not rendered."""
|
|
ctx = _acceptance_ctx(tmp_path, latched=False)
|
|
ctx.task_metadata = {"force_plan": True}
|
|
held, messages, _trace = _held(ctx)
|
|
assert held is True and messages[-1]["content"].startswith("[PLAN_REVIEW_HOLD]")
|
|
told = ctx._plan_gate_told_identity
|
|
assert told
|
|
|
|
ctx._owner_hurry_latch = _acceptance_ctx(tmp_path, latched=True)._owner_hurry_latch
|
|
held, messages, trace = _held(ctx, content="the draft")
|
|
assert held is True and [m["role"] for m in messages] == ["assistant", "user"]
|
|
released = messages[-1]["content"]
|
|
assert released.startswith("[PLAN_REVIEW_RELEASED]")
|
|
for fact in ("plan_review_open=true", "owner_hurry_local_advisory=true", "configured_enforcement=blocking"):
|
|
assert fact in released
|
|
assert "⚠️" not in released and "advisory enforcement" not in released.split("\n")[1]
|
|
assert ctx._plan_gate_told_identity != told and ctx._plan_gate_release_told is True
|
|
assert trace["reasoning_notes"] == ["Released plan-review gate reported before final response."]
|
|
|
|
held, messages, _trace = _held(ctx)
|
|
assert held is False and messages == []
|
|
|
|
|
|
def test_a_gate_the_mind_closed_itself_buys_no_round(tmp_path, monkeypatch, open_blocking_install):
|
|
"""The latch never bills the mind for its own act: a closed wave, or an author
|
|
stop, after a delivered hold appends nothing."""
|
|
import ouroboros.task_results as tr
|
|
|
|
projection = {"status": "open", "allow": False, "closed": False, "outcome": "REVIEW_REQUIRED"}
|
|
monkeypatch.setattr(tr, "plan_review_gate_projection", lambda *_a, **_k: dict(projection))
|
|
ctx = _acceptance_ctx(tmp_path, latched=False)
|
|
ctx.task_metadata = {"force_plan": True}
|
|
assert _held(ctx)[0] is True
|
|
|
|
ctx._owner_hurry_latch = _acceptance_ctx(tmp_path, latched=True)._owner_hurry_latch
|
|
projection = {"status": "closed", "allow": True, "closed": True, "outcome": "GREEN"}
|
|
held, messages, _trace = _held(ctx)
|
|
assert held is False and messages == []
|
|
|
|
projection = {"status": "author_stopped", "allow": True, "closed": False, "outcome": "REVIEW_REQUIRED"}
|
|
held, messages, _trace = _held(ctx)
|
|
assert held is False and messages == []
|
|
assert not getattr(ctx, "_plan_gate_release_told", False)
|
|
|
|
|
|
ADVISORY_OPEN = {
|
|
"required": True, "self_opened": True, "enforcement": "advisory", "status": "advisory_open",
|
|
"allow": True, "closed": False, "outcome": "DEGRADED", "reviewer_slots_degraded": True,
|
|
"custody_pending": True, "review_late_result_pending": True, "cycles_paid": 1,
|
|
}
|
|
|
|
|
|
def _round_limit_prompt(tmp_path, monkeypatch, decision, *, deferred_child):
|
|
if deferred_child:
|
|
write_child(tmp_path)
|
|
write_confirmed_disposition_fixture(tmp_path, disposition="deferred", rationale="defer it")
|
|
loop, _registry, limit_ctx, _trace = _forced_test_context(tmp_path)
|
|
captured = []
|
|
monkeypatch.setattr(loop, "call_llm_with_retry", lambda _llm, messages, *_a, **_k: (
|
|
captured.append([dict(m) for m in messages]) or {"content": "Forced answer."}, 0.0))
|
|
monkeypatch.setattr(loop, "_force_plan_decision", lambda *_a, **_k: dict(decision))
|
|
monkeypatch.setattr(loop, "_force_plan_disclosure",
|
|
lambda *_a, **_k: OWNER_NOTICE if not decision.get("closed") else "")
|
|
text, usage, _returned = loop._handle_round_limit(limit_ctx)
|
|
assert text == "Forced answer." and len(captured) == 1
|
|
last = next(m for m in reversed(captured[0]) if m["role"] == "user")
|
|
content = last["content"]
|
|
if isinstance(content, list):
|
|
content = "".join(str(block.get("text") or "") for block in content if isinstance(block, dict))
|
|
return str(content), usage
|
|
|
|
|
|
def test_the_forced_prompt_carries_typed_facts_not_the_owner_notice(tmp_path, monkeypatch):
|
|
prompt, usage = _round_limit_prompt(tmp_path / "facts", monkeypatch, ADVISORY_OPEN, deferred_child=True)
|
|
assert "[TASK_STATE_FACTS]" in prompt
|
|
for fact in ("plan_review_open=true", "plan_review_enforcement=advisory", "cycles_paid=1",
|
|
"children_deferred=1: child1 [completed] sha256="):
|
|
assert fact in prompt
|
|
assert "⚠️ Plan review is still open" not in prompt and "⚠️ DEFERRED CHILD RESULTS" not in prompt
|
|
if "[ACCEPTANCE_SUBJECT_OBSERVATION]" in prompt:
|
|
assert prompt.index("[ACCEPTANCE_SUBJECT_OBSERVATION]") > prompt.index("[TASK_STATE_FACTS]")
|
|
# The owner-facing bytes did not move into or out of the prompt.
|
|
assert "⚠️ Plan review is still open (DEGRADED)" in usage["terminal_host_notice"]
|
|
assert "⚠️ DEFERRED CHILD RESULTS: child1" in usage["terminal_host_notice"]
|
|
|
|
# Nothing to state: a closed review and no child add no block at all.
|
|
quiet, usage = _round_limit_prompt(tmp_path / "quiet", monkeypatch, {
|
|
**ADVISORY_OPEN, "status": "closed", "closed": True, "outcome": "GREEN"}, deferred_child=False)
|
|
assert "[TASK_STATE_FACTS]" not in quiet and "plan_review_open" not in quiet
|
|
assert "terminal_host_notice" not in usage
|
|
|
|
|
|
def test_the_facts_block_is_data_from_typed_fields_only():
|
|
from ouroboros.loop_forced_finalization import _plan_gate_facts
|
|
|
|
assert _plan_gate_facts({}) == "" and _plan_gate_facts({"required": True, "closed": True}) == ""
|
|
line = _plan_gate_facts({**ADVISORY_OPEN, "owner_hurry_local_advisory": True,
|
|
"configured_enforcement": "blocking", "decision_authority": "cyber_pro"})
|
|
assert line == (
|
|
"plan_review_open=true plan_review_outcome=DEGRADED plan_review_enforcement=advisory "
|
|
"decision_authority=cyber_pro owner_hurry_local_advisory=true configured_enforcement=blocking "
|
|
"reviewer_slots_degraded=true custody_pending=true review_late_result_pending=true cycles_paid=1"
|
|
)
|
|
assert "⚠️" not in line and "proceeded" not in line
|
|
|
|
|
|
def test_the_narrowed_latch_reads_nothing_but_the_registry_context(tmp_path):
|
|
"""The two attributes live on ``tools._ctx`` (loop-local, per worker): a resumed
|
|
task re-derives them and spends no round. The identity is typed fields only."""
|
|
from ouroboros.loop_forced_finalization import _plan_gate_identity
|
|
|
|
same = _plan_gate_identity({**ADVISORY_OPEN})
|
|
assert same == _plan_gate_identity({**ADVISORY_OPEN, "fingerprint": "different-wave"})
|
|
assert same != _plan_gate_identity({**ADVISORY_OPEN, "custody_pending": False})
|
|
assert _plan_gate_identity({}) == "|" * 10
|