fix(api): require substantive audit notes on human task-decision endpoints

Audit/tracing rule: every human decision must record its rationale. These
panel-facing routes accepted empty/absent notes, leaving no trail:
- docs-complete: now requires notes (>=20) — what was documented
- submit-pm-review: now requires notes (>=20) — what is ready for review
- complete: now requires justification (>=20) — why the task is done

Mirrors the existing pass-qa / ceo-approve notes gates (checked after the
404/403 so not-found and forbidden still take precedence). submit-qa is
left as-is: it already gates on commits + PR + progress_updates +
self_verified, and the panel collects no extra note there to drop.
Tests updated to send notes; added complete-without-justification reject.
This commit is contained in:
Renn F
2026-05-24 07:05:33 +02:00
parent c093996efc
commit 5120b5ce81
2 changed files with 52 additions and 6 deletions
+19 -1
View File
@@ -2209,6 +2209,7 @@ async def test_submit_pm_review_service_returns_none(task_client: dict) -> None:
mock_factory.return_value = instance
response = await task_client["client"].post(
f"/api/tasks/{task.id}/submit-pm-review",
json={"notes": "Ready for PM review — all criteria met."},
headers=_HDR,
)
assert response.status_code == HTTPStatus.BAD_REQUEST
@@ -2230,12 +2231,29 @@ async def test_complete_task_success(task_client: dict) -> None:
mock_factory.return_value = instance
response = await task_client["client"].post(
f"/api/tasks/{task.id}/complete",
json={"force_with_cancelled": False},
json={
"force_with_cancelled": False,
"justification": "All acceptance criteria met; merging.",
},
headers=_HDR,
)
assert response.status_code == HTTPStatus.OK
@pytest.mark.asyncio
async def test_complete_without_justification_rejected(task_client: dict) -> None:
"""Audit: completing a task must carry its rationale (>= 20 chars)."""
task = _seed_task(task_client)
await task_client["db"].flush()
response = await task_client["client"].post(
f"/api/tasks/{task.id}/complete",
json={"force_with_cancelled": False},
headers=_HDR,
)
assert response.status_code == HTTPStatus.BAD_REQUEST
assert "JUSTIFICATION_REQUIRED" in response.json()["detail"]
# ---------------------------------------------------------------------------
# cancel: service returns None branch (1162-1167)
# ---------------------------------------------------------------------------