# Todo 14 Message Role Validation Plan **Status:** done ## Goal Reject provider-invalid API message roles and malformed tool replies before runtime work or network calls. ## Why The UI no longer exposes `tool` pre-messages, and the OpenAI client rejects tool messages without `tool_call_id`. API callers can still send arbitrary `role` values in `pre_messages`, which can fail later at the provider boundary. The request contract should fail fast. ## Scope This todo covers: - Constrain `ChatMessage.role` to Chat Completions-compatible roles. - Reject `pre_messages` with `role="tool"` in `DebugRunRequest`, because request pre-messages cannot provide a matching assistant `tool_calls` context safely. - Preserve internally generated EventAgent tool replies. - Keep OpenAI client payload serialization tests passing. This todo does not cover: - Full arbitrary conversation transcript validation. - UI changes. - Tool handler exception isolation. ## Files - Modify: `src/agent_lab/domain/messages.py` - Modify: `src/agent_lab/application/contracts.py` - Modify: `tests/test_websocket_api.py` or add focused contract tests. ## Steps 1. Add failing tests: - invalid pre-message role is rejected by `DebugRunRequest.model_validate()`; - `pre_messages` with `role="tool"` is rejected before runtime; - internally created `ChatMessage(role="tool", tool_call_id="...")` remains valid. 2. Implement a `Literal` role type in `ChatMessage`. 3. Add a `DebugRunRequest` validator that rejects tool pre-messages. 4. Run: ```bash uv run pytest tests/test_websocket_api.py tests/test_event_agent.py uv run pytest ``` 5. Update this plan and `docs/plans/todos.md`, then commit. ## Verification Result: - Red run passed as expected: invalid role and tool pre-message tests failed before implementation. - `uv run pytest tests/test_websocket_api.py tests/test_event_agent.py` passed: 21 tests, 1 existing Starlette deprecation warning. - `uv run pytest` passed: 34 tests, 1 existing Starlette deprecation warning. ## Evaluation - `ChatMessage.role` now accepts only `system`, `user`, `assistant`, or `tool`. - `DebugRunRequest` rejects `tool` entries in `pre_messages`, so API callers cannot send orphan tool replies as front-loaded context. - Internally generated EventAgent tool replies still validate and serialize correctly.