`DocumentConditionLink` and `ExtractedFact` were classified as backed up and came back from no restore. The register said a restored vault is unsorted and that the facts are re-derivable by re-running the extraction. The first is right; the second understates it. What a re-run cannot produce is the REVIEW DECISION on each fact, and every column holding one defaults to "nobody has looked at this yet". `POST /api/documents/inbound/[id]/confirm` acts only on a PENDING fact and commits it into the structured store, so a restore that let those columns default would offer an already-committed reading for approval a second time and write a second lab result behind it. Both ends live in `src/lib/export/document-filing-backup.ts`, beside each other like the Coach, reminder and vaccination sections. `status`, `needsReview`, `confidence` and the commitment pair are carried and restored verbatim, and the wire schema requires the first three rather than defaulting them. Ordering: the section runs after the documents, the condition episodes, the lab results and the medications. The first two are real foreign keys, unlike the Coach's bare id columns, so an unresolvable value there does not cost one edge, it aborts the transaction and the operator gets none of the account back. The last two are what `committedRecordId` resolves against. The builder therefore carries a filing or a fact only when both of its ends are carried, and the restore still checks and drops what a hand-edited file leaves unresolvable. `committedRecordId` is nulled together with its type, because a type with no id claims a commitment the row cannot name. The round trip seeds a second condition and files the page under it, so a restore that kept the count and lost the pairing fails rather than handing back a lab report filed under a head cold. A third case breaks both references in a saved file to prove the drop path runs and still answers 200. Three new skip catalogues arrive with it, and with them labels for the three that had none: a dropped Coach attachment had been reaching the operator's screen labelled "mood factor" since it was added. The label chain is exhaustive now, so the next catalogue without one stops the build.
src/components/
React components. The UI primitives are shadcn/ui (new-york style, zinc base) over Radix + Tailwind 4 + Lucide; everything above the primitives is feature-grouped by surface. Components are server components unless they need a hook, state, or browser API, in which case they open with "use client". Charts use Recharts 3 — replacing the chart library requires explicit approval.
Primitives and shell
ui/— shadcn/ui primitives (button, dialog, sheet, table, …). Generated by the shadcn CLI; keep edits minimal and on-style.layout/— the app shell: sidebar, topbar, bottom nav.providers.tsx,version-poller.tsx— top-level context providers and the self-healing stale-shell poller.i18n/— translation context + locale switcher.
Feature groups
insights/— insight status, assessment cards, Coach surface, advisor cards (the largest group).medications/— medication cards, the schedule wizard, intake timeline, detail history.settings/— the section components behind each settings page.measurements/,mood/,dashboard/— entry forms, lists, dashboard tiles.charts/— Recharts wrappers.monitoring/— Umami / GlitchTip client bootstrap.gamification/,targets/,comparison/,doctor-report/,clinician/,onboarding/,admin/— the remaining per-surface groups.
Conventions
Kebab-case filenames; React component exports stay PascalCase regardless. (A handful of PascalCase-filename outliers under medications/ + onboarding/ are pre-existing drift, noted in ../../CLAUDE.md.) Every t() call needs a matching key in messages/en.json — guarded by i18n-call-site-coverage.test.ts. Data reads go through TanStack Query with keys from @/lib/query-keys and unwrap (await res.json()).data. Tests live in __tests__/.