fix(review): preserve typed terminal facts and routes

Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
This commit is contained in:
Ouroboros 2026-08-27 11:26:40 +03:00
parent 8e20368cca
commit 89e32db656
7 changed files with 74 additions and 3 deletions

View file

@ -880,6 +880,8 @@ def _collect_chat_rows(
rec["mime"] = str(entry.get("mime") or "")
rec["download_url"] = str(entry["download_url"])
rec["caption"] = str(entry.get("caption") or "")
if "task_terminal_status" in entry:
rec["task_terminal_status"] = str(entry.get("task_terminal_status") or "")
_copy_task_summary_metadata(rec, entry)
for field in SUBAGENT_MESSAGE_FIELDS:
if field in entry:

View file

@ -43,9 +43,13 @@ def _emit_plan_review_reference(
serialized = json.dumps(state, ensure_ascii=False, sort_keys=True, default=str)
revision = sha256(serialized.encode("utf-8")).hexdigest()
try:
chat_id = int(getattr(ctx, "current_chat_id", 0))
from supervisor.message_bus import notification_chat_route
chat_id = notification_chat_route(getattr(ctx, "current_chat_id", None), 1)
if chat_id is None:
chat_id = 1
except (TypeError, ValueError):
chat_id = 0
chat_id = 1
ts = utc_now_iso()
payload = {
"type": "review_reference", "surface": "plan_review",

View file

@ -915,6 +915,8 @@ def log_chat(
for key in ("project_id", "project_name", "target_label", "status"):
if key in meta:
record[key] = meta[key]
if "task_terminal_status" in meta:
record["task_terminal_status"] = str(meta.get("task_terminal_status") or "")
if filename:
record["filename"] = filename
if mime:

View file

@ -1479,7 +1479,12 @@ def _run_chat_task(
)
except Exception:
log.debug("Failed-turn typing announce failed", exc_info=True)
send_with_budget(chat_id, err_msg, task_id=failed_task_id)
send_with_budget(
chat_id,
err_msg,
task_id=failed_task_id,
progress_meta={"task_terminal_status": "failed"},
)
except Exception:
log.debug("Suppressed exception", exc_info=True)

View file

@ -41,6 +41,31 @@ def test_chat_history_preserves_subagent_lane_group_metadata(tmp_path):
assert rec["task_group_id"] == "group1"
def test_chat_history_replays_typed_direct_error_terminal_status(tmp_path):
logs = tmp_path / "logs"
logs.mkdir()
(logs / "chat.jsonl").write_text(
json.dumps({
"ts": "2026-08-27T00:00:00Z",
"direction": "out",
"chat_id": 1,
"user_id": 7,
"text": "error",
"task_id": "failed-task",
"task_terminal_status": "failed",
}) + "\n",
encoding="utf-8",
)
(logs / "progress.jsonl").write_text("", encoding="utf-8")
endpoint = make_chat_history_endpoint(tmp_path)
response = asyncio.run(endpoint(SimpleNamespace(query_params={"limit": "10"})))
payload = json.loads(response.body.decode("utf-8"))["messages"]
rec = next(item for item in payload if item.get("task_id") == "failed-task")
assert rec["task_terminal_status"] == "failed"
def test_chat_history_replays_delivered_document_row(tmp_path):
"""A persisted document chat row is replayed as a msg_type=document record so
the frontend rebuilds the file bubble on reload from the durable URL."""

View file

@ -158,6 +158,7 @@ def test_run_chat_task_unregisters_and_keys_error_final_when_agent_raises(tmp_pa
args, kwargs = sent[0]
assert args[0] == 5
assert kwargs.get("task_id") == str(agent.task["id"])
assert kwargs.get("progress_meta") == {"task_terminal_status": "failed"}
# The failed turn re-announces the activity<->client_message_id link right
# before the keyed final, so the client retires the `Sending...` submission
# even when the start announce never reached it.

View file

@ -55,6 +55,38 @@ def test_plan_reference_uses_shared_best_effort_log_event_seam(monkeypatch):
}
def test_plan_reference_defaults_unbound_context_to_main_chat(monkeypatch):
events: queue.Queue = queue.Queue()
ctx = SimpleNamespace(event_queue=events)
state = {"current_attempt": {"fingerprint": "review-fingerprint"}, "waves": []}
calls = []
monkeypatch.setattr(
plan_review_references,
"emit_log_event",
lambda _queue, payload, **_kwargs: calls.append(payload),
)
plan_review_references._emit_plan_review_reference(ctx, "task-1", state)
assert calls[0]["chat_id"] == 1
def test_plan_reference_preserves_explicit_panel_chat_zero(monkeypatch):
events: queue.Queue = queue.Queue()
ctx = SimpleNamespace(event_queue=events, current_chat_id=0)
state = {"current_attempt": {"fingerprint": "review-fingerprint"}, "waves": []}
calls = []
monkeypatch.setattr(
plan_review_references,
"emit_log_event",
lambda _queue, payload, **_kwargs: calls.append(payload),
)
plan_review_references._emit_plan_review_reference(ctx, "task-1", state)
assert calls[0]["chat_id"] == 0
def test_attempt_helper_publishes_immediately_after_the_canonical_write(monkeypatch):
ctx = SimpleNamespace(event_queue=queue.Queue())
calls = []