You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
review: wire typed models load-bearing across read sites (#30)
Closes five blocking findings from Brad's review pass:
1. Bubble / Workspace / ExportEntry were defined and unit-tested but had
zero production callers — the PR's own "Used at" table didn't match
the code. Wired each at the cited sites:
* Bubble in api/workspaces.py (both bubble loaders, line 584 + 1023)
* Bubble in api/search.py:150
* Workspace in api/composers.py:44 and api/workspaces.py:54
* ExportEntry reader in scripts/export.py:66 — paired with writer
updates so the on-disk shape matches what the model accepts.
2. get_workspace_tabs() in api/workspaces.py bypassed Composer.from_dict
while list_workspaces() in the same file validated. The two primary
conversation-browsing paths now agree on what counts as a valid row.
3. get_composer() returned the raw dict from either per-workspace DB or
global storage with no validation. Now goes through
WorkspaceLocalComposer.from_dict (per-workspace, drift logged + skipped)
and Composer.from_dict (global fallback, drift -> 404 + log).
4. WorkspaceLocalComposer.from_dict result was discarded after the gate.
The typed object is now load-bearing: the sort key reads
local.last_updated_at and the JSON's composerId/lastUpdatedAt are
written from the validated fields, not the raw dict.
5. SchemaError message always read "missing required field" even on
type/shape mismatches. Now reads "invalid field" when hint is set
and "missing required field" only when hint is None, so log grepping
can tell the two drift modes apart.
Plus one forward-looking note: added a WHY comment at the numeric-only
createdAt check in models/conversation.py pointing at the 17/17 live
scan that justifies the strict isinstance(int, float) gate.
11 new regression tests:
- tests/test_models_wired_at_read_sites.py (new file, 10 tests)
pinning each from_dict call site with spy + side_effect patches so
a future refactor that drops the typed model fails loudly.
- tests/test_models.py
one new test asserting the SchemaError wording distinction holds.
Verified locally:
- python -m unittest discover tests: 220 passed, OK (was 209)
- python -m unittest tests.test_models_wired_at_read_sites: 10/10 pass
- python app.py boots clean, curl / returns 200
0 commit comments