Untyped string vocabularies couple frontend and backend
The app is threaded with magic string vocabularies that must match exactly between the browser, the edge functions, and the database — but are enforced by nothing. A one-character drift fails silently (blank labels, empty filters, no-op queries) and only at runtime.
Examples:
event_type—useAuditLog.EVENT_LABELShardcodes 13 Portuguese labels keyed by event strings the backend must emit verbatim (phase_changed,gate_approved,dossier_export_created, …). Unknown types fall back to raw snake_case.- Drifting filter subset —
CaseAuditLog'sEVENT_TYPESdropdown lists only 9 of the 13, soportal_link_*anddossier_export_*events can never be filtered even though they render. entity_type— filter list isCASE, CHECKLIST_ITEM, DOCUMENT, EVIDENCE, PORTAL, but code writesDOSSIER_EXPORT(not in the list → unfilterable) and defaults toCASE. Casing must match exactly.metadatakeys —MetadataDisplayspecial-cases{from, to, reason, title, phase}; if the backend writesold/newinstead offrom/to, the "A → B" display vanishes into a raw JSON blob.- Dossier export status — UI polls only while some export is
"generating"and mapspending/generating/ready/failedto badges; different backend strings mean polling never stops/starts. - Checklist status — dossier offers only items with
status === "approved"; a different literal yields an empty checklist silently. - Edge-function names & body shape —
"generate-dossier","log-auth-event","invite-user","setup-company"are string-invoked; bodies are snake_case (case_id,include_audit_log, …) manually remapped from camelCase in the hooks. - Every case-status write casts
as any(e.g.status: "ARCHIVED" as any), bypassing the TS enum — so even the type system doesn't catch drift.
The tacit assumption: all these vocabularies stay in sync by discipline. There is no shared schema, codegen, or test.
Rebuild implication
Define these enums once (shared contract / generated types) and derive labels, filters, and validation from that single source. Server should reject unknown values, not the UI silently drop them.