Calendar Day
Tuesday, June 16, 2026 (PR 3 of 3)
Planned Effort
3 story points — sprint item #7 (Medium)
Continues: Week 2 PR #72 TypedDict boundary work. Independent of: Tuesday PR 1 (CSP #2).
Problem
MessageDict.role is typed str, so typos like "assitant" pass mypy silently. The parser only emits user, assistant, system, result, and progress. A Literal union catches errors statically and documents the vocabulary (mirrors ToolNameLiteral in models/tool_results.py).
Goal
One merged PR that narrows MessageDict.role to RoleLiteral, guards unknown roles at parse time (fallback + warning, never raise), and adds a test for unknown-role handling.
Scope
Touch points
models/session.py — RoleLiteral, MessageDict.role
utils/jsonl_parser.py — _coerce_role guard at assignment
models/search.py, api/search.py — propagate narrowed type if needed
tests/test_jsonl_parser.py — unknown-role test
Type definition
RoleLiteral = Literal["user", "assistant", "system", "result", "progress"]
Unknown JSONL roles → map to "system" with logging.warning (never raise).
Acceptance Criteria
Verification
cd C:\Users\Jasen\CppAliance\claude-code-chat-browser
.\.venv\Scripts\Activate.ps1
mypy -p api -p utils -p models
pytest tests/test_jsonl_parser.py -q
pytest -q
ruff check .
Out of Scope
__all__ cleanup (Tuesday PR 2 — #3)
- Frontend TypeScript role types
Calendar Day
Tuesday, June 16, 2026 (PR 3 of 3)
Planned Effort
3 story points — sprint item #7 (Medium)
Continues: Week 2 PR #72 TypedDict boundary work. Independent of: Tuesday PR 1 (CSP #2).
Problem
MessageDict.roleis typedstr, so typos like"assitant"pass mypy silently. The parser only emitsuser,assistant,system,result, andprogress. ALiteralunion catches errors statically and documents the vocabulary (mirrorsToolNameLiteralinmodels/tool_results.py).Goal
One merged PR that narrows
MessageDict.roletoRoleLiteral, guards unknown roles at parse time (fallback + warning, never raise), and adds a test for unknown-role handling.Scope
Touch points
models/session.py—RoleLiteral,MessageDict.roleutils/jsonl_parser.py—_coerce_roleguard at assignmentmodels/search.py,api/search.py— propagate narrowed type if neededtests/test_jsonl_parser.py— unknown-role testType definition
Unknown JSONL roles → map to
"system"withlogging.warning(never raise).Acceptance Criteria
RoleLiteraldefined;MessageDict.role: RoleLiteralmypy -p api -p utils -p modelsstrict, no new errorsapi/search.py("role": msg["role"]) andmodels/search.py(SearchHitDict.role) auditedMessageDictwith string roles updated to valid literal valuestests/test_jsonl_parser.py: unknown role in JSONL handled gracefully (mapped/skipped, not raised)ruff check .passesVerification
Out of Scope
__all__cleanup (Tuesday PR 2 — #3)