fix(backup): carry how hard a symptom hit through export and restore

A cycle symptom can be recorded with a 1-4 intensity. The log-day sheet
writes it, the day-log reads it back, and the backup never carried it:
the file held the symptom keys and nothing else. Export a day recorded
as "cramps, and they were a 4", restore it, and you get "cramps". The
symptom is there, the number is gone, and nothing fails, which is why
nobody noticed.

The file now carries `symptomSeverities` alongside `symptomKeys` — one
entry per symptom that was actually rated. `symptomKeys` still decides
which symptoms a day had, so a release that only knows that field reads
every symptom out of a new file unchanged, and the two lists can never
disagree about presence. A link nobody rated is simply not listed: no
entry means never rated, which is not the same as rated zero.

A file written before this restores exactly as it did, with every
intensity absent rather than invented. An intensity outside 1-4, from a
hand-edited file or some later scale, costs that one number and leaves
the symptom and the rest of the restore alone.
This commit is contained in:
Marc-André Bombeck committed 2026-07-31 07:37:48 +02:00
1 parent 340d1db6d9
commit 92da02f824
4 files changed
+407 -12

No files matched your search

+20 -1
View File
@@ -267,8 +267,26 @@ const cycleSpanSchema = z
* ciphertext envelope verbatim — the backup never decrypts it, so the
* owner's free-text note round-trips encrypted (and a wrong-surface leak is
* impossible). `symptomKeys` carries the seeded catalogue keys so the
* restore can re-link without exporting internal join ids.
* restore can re-link without exporting internal join ids, and
* `symptomSeverities` carries how hard each of them hit.
*/
/**
* The intensity recorded against one of a day's symptoms.
*
* A sparse annotation over `symptomKeys`, not a replacement for it: the keys
* still say which symptoms the day had, this says how hard the ones that were
* rated hit. Only rated links appear, so a missing entry means the person never
* put a number on it. `severity` is left as a plain integer here on purpose —
* the 1-4 range is checked when the row is written, so a file carrying a value
* outside it loses that one intensity instead of failing to restore at all.
*/
const cycleSymptomSeveritySchema = z
.object({
key: z.string().min(1),
severity: z.number().int().nullable().optional(),
})
.passthrough();
const cycleDayLogSchema = z
.object({
id: z.string().min(1).optional(),
@@ -298,6 +316,7 @@ const cycleDayLogSchema = z
createdAt: isoDateTime.optional(),
updatedAt: isoDateTime.optional(),
symptomKeys: z.array(z.string()).default([]),
symptomSeverities: z.array(cycleSymptomSeveritySchema).default([]),
})
.passthrough();