Files
roboco/tests/unit/gateway/test_envelope.py
T
Renn F d47adfe027 fix(gateway): rejection envelopes always carry a human-readable message
tracing_gap and incomplete_input rejections set missing and remediate but
left message null. The audit log records message (not remediate) and agents
keyed on message, so a rejected agent saw a null reason and retried the same
verb until it burned out instead of reading remediate and self-correcting.

Both builders (and from_decision) now derive a non-null message that folds
the missing tokens and the actionable remediate into one line, so the agent
and the audit trail always see what was missing and how to fix it.
2026-06-04 06:01:06 +02:00

58 lines
2.2 KiB
Python

"""Tests for the standardized response envelope."""
from __future__ import annotations
from uuid import uuid4
from roboco.services.gateway.envelope import Envelope
class TestEnvelopeOk:
def test_ok_minimal_response(self) -> None:
env = Envelope.ok(
status="in_progress", task_id=str(uuid4()), next="edit + commit"
)
body = env.as_dict()
assert body["status"] == "in_progress"
assert body["next"] == "edit + commit"
assert body["error"] is None
assert body["evidence"] is None or body["evidence"] == {}
assert "context_briefing" in body
def test_ok_with_evidence(self) -> None:
evidence = {"pr_url": "https://github.com/x/y/pull/8", "commits": []}
env = Envelope.ok(
status="awaiting_qa", task_id=str(uuid4()), next="idle", evidence=evidence
)
assert env.as_dict()["evidence"] == evidence
class TestEnvelopeError:
def test_tracing_gap(self) -> None:
env = Envelope.tracing_gap(
missing=["progress>=1", "journal:reflect"],
remediate="call note(scope='reflect', task_id='...')",
)
body = env.as_dict()
assert body["error"] == "tracing_gap"
assert body["missing"] == ["progress>=1", "journal:reflect"]
assert "note(scope='reflect'" in body["remediate"]
# message must NOT be null — it carries the missing tokens + remediate so
# the agent and the audit log can see what to do (no more silent flailing).
assert body["message"] is not None
assert "progress>=1" in body["message"]
assert "journal:reflect" in body["message"]
assert "note(scope='reflect'" in body["message"]
def test_invalid_state(self) -> None:
env = Envelope.invalid_state(
message="task is blocked", remediate="wait for PM unblock"
)
body = env.as_dict()
assert body["error"] == "invalid_state"
assert body["message"] == "task is blocked"
def test_not_authorized(self) -> None:
env = Envelope.not_authorized(message="role mismatch", remediate="claim first")
assert env.as_dict()["error"] == "not_authorized"