From 07008baaf5b91c6d77e0ccdc50faccdd6a339074 Mon Sep 17 00:00:00 2001 From: Renn F Date: Tue, 23 Jun 2026 02:34:55 +0200 Subject: [PATCH] fix(api): serialize pr_reviewer_notes/doc_notes/notes_structured in the task response MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit task_to_response (the builder behind the task list + detail endpoints the panel reads) set dev_notes/qa_notes/quick_context but dropped pr_reviewer_notes, doc_notes, and notes_structured, and TaskResponse didn't declare notes_structured at all — so the PR-reviewer notes, documenter notes, and the structured PR-review verdict were always blank in the panel regardless of what agents persisted. The structured-content write-path + obligation gates were fine; the data just wasn't serialized. Now returns every note section + the structured source of truth. Regression test asserts all three round-trip. --- CHANGELOG.md | 4 ++++ roboco/api/schemas/tasks.py | 7 +++++++ tests/unit/api/test_schemas_tasks.py | 20 ++++++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07599d85..150b3c73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ## [Unreleased] +### Fixed + +- **Task notes were invisible in the panel — the API response dropped them.** The `task_to_response` serializer (used by the task list and detail endpoints the panel reads) set `dev_notes` / `qa_notes` / `quick_context` but **omitted `pr_reviewer_notes`, `doc_notes`, and `notes_structured`**, and `TaskResponse` didn't even declare `notes_structured` — so the PR-reviewer's notes, the documenter's notes, and the structured PR-review verdict were always blank in the UI no matter what the agents wrote to the DB (the structured-content write-path and obligation gates work; the data simply wasn't being serialized). The builder now returns all note sections plus the structured source of truth. (`dev_notes`/`qa_notes` on an in-flight task are still legitimately empty until the developer submits / QA reviews.) + ## [0.9.0] - 2026-06-23 ### Added diff --git a/roboco/api/schemas/tasks.py b/roboco/api/schemas/tasks.py index 6338cd09..65ebd35d 100644 --- a/roboco/api/schemas/tasks.py +++ b/roboco/api/schemas/tasks.py @@ -238,6 +238,10 @@ class TaskUpdate(BaseModel): pr_reviewer_notes: str | None = None doc_notes: str | None = None quick_context: str | None = None + # The structured source of truth (panel renders the verdict pill + sections + # from this). Must be serialized or the PR-review verdict + any structured + # rendering are blank even when the DB has them. + notes_structured: dict | None = None # Lifecycle override — privileged/admin only. Applied by the route as an # audited force-transition (so an operator can recover a task wedged in a @@ -705,7 +709,10 @@ def task_to_response(task: "TaskTable") -> TaskResponse: dev_notes=task.dev_notes, qa_notes=task.qa_notes, auditor_notes=task.auditor_notes, + pr_reviewer_notes=task.pr_reviewer_notes, + doc_notes=task.doc_notes, quick_context=task.quick_context, + notes_structured=task.notes_structured, self_verified=task.self_verified, qa_verified=task.qa_verified, branch_name=getattr(task, "branch_name", None), diff --git a/tests/unit/api/test_schemas_tasks.py b/tests/unit/api/test_schemas_tasks.py index c9823a79..497704fa 100644 --- a/tests/unit/api/test_schemas_tasks.py +++ b/tests/unit/api/test_schemas_tasks.py @@ -300,7 +300,10 @@ def _stub_task(*, with_project: bool = False) -> SimpleNamespace: dev_notes=None, qa_notes=None, auditor_notes=None, + pr_reviewer_notes=None, + doc_notes=None, quick_context=None, + notes_structured=None, self_verified=False, qa_verified=None, branch_name=None, @@ -327,6 +330,23 @@ def test_task_to_response_includes_slug_when_project_loaded() -> None: assert resp.project_slug == "proj-1" +def test_task_to_response_serializes_all_note_sections() -> None: + """Regression: pr_reviewer_notes / doc_notes / notes_structured MUST be in the + response. The builder previously omitted them, so the panel showed them blank + even when the DB had them (the recurring "notes invisible" bug).""" + stub = _stub_task() + stub.pr_reviewer_notes = "## Findings\n- looks good" + stub.doc_notes = "Updated the README" + stub.notes_structured = {"pr_review": {"verdict": "passed"}} + fake_inspector = MagicMock() + fake_inspector.unloaded = {"project"} + with patch("roboco.api.schemas.tasks.sa_inspect", return_value=fake_inspector): + resp = task_to_response(stub) # type: ignore[arg-type] + assert resp.pr_reviewer_notes == "## Findings\n- looks good" + assert resp.doc_notes == "Updated the README" + assert resp.notes_structured == {"pr_review": {"verdict": "passed"}} + + def test_task_list_to_response_returns_list() -> None: stubs = [_stub_task(), _stub_task()] fake_inspector = MagicMock()