The integration suite used to share a single module graph across
files (`pool: forks`, `fileParallelism: false`, `isolate: false`).
That kept the container boot amortised but meant a `vi.mock()`
declaration in one file lost to whichever earlier file had already
pulled the real module into the graph. `apns-dispatch.test.ts` and
`integration-status.test.ts` both fall through to
`@/lib/notifications/dispatcher` (and its `@parse/node-apn` /
telegram sender deps); when a sibling test mounted the real
dispatcher first, the mock factories registered later silently
no-op'd and the in-suite run reported the mock as called zero
times. Each file still passed in isolation, which is the
fingerprint the v1.4.35.1 closure already flagged as pre-existing.
Flip the integration config to `isolate: true`. Per-file module
isolation costs ~10 s of import rebuild across the 56-file run;
the Postgres testcontainer + migrations live in `globalSetup`, so
the slow part (container boot, ~5-15 s) is unaffected. The two
`withings-*-sync.test.ts` files were previously dependent on a
sibling setting `process.env.ENCRYPTION_KEY` before them — pin the
deterministic test key in each file so they remain self-contained
under per-file isolation. Locally `pnpm test:integration` now
shows the four target failures green; the remaining two
bp-in-target flakes are pre-existing local-only data drift that
passed on CI under the old isolation flag and continue to be
ordering-independent.
New `syncUserSleep` routine pulls per-segment sleep data from
POST /v2/sleep?action=get and writes one Measurement row per stage
segment, tagged with the appropriate SleepStage enum:
state 0 (awake) → AWAKE
state 1 (light sleep) → CORE (HealthKit-aligned NREM 1+2)
state 2 (deep sleep) → DEEP
state 3 (REM) → REM
state 4 (synthetic) → ignored
The legacy ASLEEP and IN_BED enum slots stay reserved for HealthKit
ingest (pre-iOS-16 `asleepUnspecified` and iOS-16+ `inBed`).
ScanWatch nights with no REM segment simply omit the row rather than
synthesising an all-zeros entry — Withings drops the field, the writer
never sees it.
Idempotency rides on Migration 0055's five-column composite — a re-sync
of the same night UPDATEs every segment row in place rather than
inserting duplicates, even when Withings re-aggregates the night with
adjusted segment boundaries.
Sleep durations stored as canonical minutes (seconds ÷ 60, rounded) so
re-syncs don't flip values on floating-point noise.