feat(sync): re-deliver Apple Health write-back changes

Measurement upserts in /api/sync/changes carry appleHealthMirror. A new
measurementMirror domain delivers { id, appleHealthMirror } for the live
readings of every token whose setting changed past the cursor, keyed on
(token.mirrorChangedAt, measurement.id). Switching the setting writes the
token row only, so the readings keep their updatedAt and syncVersion and
nothing keyed on them recomputes. The domain is additive inside the v2
cursor and is left out of the retention expiry, since its watermark is a
setting's age, not a deletion's.
This commit is contained in:
Marc-André Bombeck committed 2026-10-23 19:28:04 +02:00
1 parent 8d10e5ab94
commit aab2e0ae7d
13 files changed
+226 -56

No files matched your search

@@ -106,22 +106,25 @@ beforeEach(() => {
} as never);
// Non-cycle domains always return rows so a 403/empty-everything result
// cannot be mistaken for a passing "cycle omitted" assertion.
vi.mocked(prisma.measurement.findMany).mockResolvedValue([
{
id: "m-1",
externalId: "ext-m-1",
type: "WEIGHT",
value: 80,
unit: "kg",
measuredAt: NOW,
source: "MANUAL",
notes: null,
notesEncrypted: null,
syncVersion: 1,
deletedAt: null,
updatedAt: NOW,
},
] as never);
// The `measurementMirror` walk reads measurements too (the readings of
// tokens whose Apple Health setting changed); it finds none here.
const measurementRow = {
id: "m-1",
externalId: "ext-m-1",
type: "WEIGHT",
value: 80,
unit: "kg",
measuredAt: NOW,
source: "MANUAL",
notes: null,
notesEncrypted: null,
syncVersion: 1,
deletedAt: null,
updatedAt: NOW,
};
vi.mocked(prisma.measurement.findMany).mockImplementation((async (args: {
where?: { ingestToken?: unknown };
}) => (args.where?.ingestToken ? [] : [measurementRow])) as never);
vi.mocked(prisma.moodEntry.findMany).mockResolvedValue([] as never);
vi.mocked(prisma.medicationIntakeEvent.findMany).mockResolvedValue(
[] as never,