Skip to content

Carry the vault's filing and its staged facts through backup and restore - #819

Merged
MBombeck merged 2 commits into
mainfrom
work/backup-docs
Aug 22, 2026
Merged

Carry the vault's filing and its staged facts through backup and restore#819
MBombeck merged 2 commits into
mainfrom
work/backup-docs

Conversation

@MBombeck

Copy link
Copy Markdown
Owner

DocumentConditionLink and ExtractedFact come off the coverage register, and the second one turned out to be more than lost history.

The review decision is the part that cannot be re-derived

The register called the facts "re-derivable only by re-running the extraction against a provider, at the operator's cost". That understates it. What a re-run cannot produce is the review decision: status, needsReview, confidence, and the pair committedRecordId / committedRecordType. Every one of them has a schema default meaning "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 returns the right number of facts and puts an already committed reading back in the review queue. Approving it a second time writes a second lab result. The wire schema requires those fields rather than defaulting them.

Both references are real foreign keys

Unlike the Coach's bare id columns, an unresolvable value here aborts the whole transaction rather than dangling. The builder therefore carries a row only when both of its ends are carried, mirroring how records-backup.ts scopes documents and episodes, and the restore still checks against what it actually wrote for the hand-edited file. committedRecordId is the one bare id, and it is nulled together with its type: a type with no id claims a commitment it cannot name.

Ordering is stated at the call site: after documents and episodes for the keys, and after lab results and medications for what the commitment resolves against. Resolving earlier would null every commitment and still report success.

A reporting defect found on the way

catalogueLabel in the admin backups view ended in an unguarded fall-through, so reminderReference, coachAttachment and coachReference had been reaching the operator's screen labelled "Mood factor" ever since they were added. The skip report was reporting, and reporting wrong.

Fixed, with labels for all six unlabelled catalogues across the six locales, and the chain now ends in const unlabelled: never = catalogue so the next catalogue without a label is a compile error instead of a wrong label.

Also corrected

  • The round-trip file header claimed field-level fidelity had "one exception". That stopped being true several sections ago. It now lists what each section actually pins.
  • BackupSummary under-counted; both new models are in it, the same gap v1.37.19 closed for visits.
  • link-surface-guard.test.ts caught the new module. Added to the frozen exemptions under the same standing rule as the visits and vaccinations sections: a restore preserves the original filing instant, which the service signature cannot express.

Proof

Four mutations, each reverted and re-run green. Forcing status: "PENDING" fails on the reviewed fact coming back up for review. Filing the page under the wrong episode fails by condition name. Nulling committedRecordId fails by id. Writing the foreign key unchecked fails a third test with expected 500 to be 200, which is what makes the constraint claim empirical rather than theoretical.

Register: 9 models remaining. Refs #186.

`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.
The scoped-sharing spec routes `/api/auth/me`, and the account switch it
drives ends in a full reload. The reload cancels whatever call is in
flight, Playwright marks the route handled, and the handler's `fulfill`
then lands on it with "Route is already handled!". With
`failOnFlakyTests` on CI that turns the whole shard red over a teardown
race rather than over anything the test is checking.

Same guard the record-session-fence spec and this file's own
accessibility sibling already use. The `fetch` is guarded too, because a
request cancelled mid-flight rejects there rather than at the fulfill,
and the earlier of the two is the one that decides which error the run
reports.
@MBombeck
MBombeck merged commit 974a90f into main Aug 22, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant