diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index db390d8..291a58a 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -52,8 +52,19 @@ jobs: with: distribution: temurin java-version: 17 - # Caches ~/.gradle so the toolchain and dependencies are not re-downloaded every run. - cache: gradle + + # Caches ~/.gradle so the toolchain and dependencies are not re-downloaded every run. + # Not setup-java's `cache: gradle`: that hashes *.gradle files at setup time, and + # android/ does not exist until prebuild, so it fails the job before anything builds. + # The lockfile and app.json are what actually decide the native dependency set. + - uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: gradle-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml', 'apps/mobile/app.json') }} + restore-keys: | + gradle-${{ runner.os }}- - name: Install dependencies run: pnpm install --frozen-lockfile diff --git a/apps/api/src/context/detectors.ts b/apps/api/src/context/detectors.ts index 51871ea..a8cdcda 100644 --- a/apps/api/src/context/detectors.ts +++ b/apps/api/src/context/detectors.ts @@ -1,6 +1,7 @@ import { Types } from 'mongoose'; import { SUGGEST_THRESHOLD, + addLocalDays, localMinutesOfDay, todayLocalDate, type Evidence, @@ -217,8 +218,9 @@ export async function runDetectors( PersonalEventModel.find({ userId: uid, localDate }).lean(), PersonalEventModel.find({ userId: uid, - localDate: { $lt: localDate }, - timestamp: { $gte: new Date(Date.now() - 21 * 86_400_000) }, + // Anchored on the day being evaluated, not the wall clock: a run for a past + // localDate must see the three weeks before THAT day, or its history is empty. + localDate: { $gte: addLocalDays(localDate, -21), $lt: localDate }, }).lean(), UserPreferenceModel.findOne({ userId: uid }).lean(), ]); diff --git a/apps/mobile/src/theme/ThemeProvider.persistence.test.ts b/apps/mobile/src/theme/ThemeProvider.persistence.test.ts index 56cb634..42c2631 100644 --- a/apps/mobile/src/theme/ThemeProvider.persistence.test.ts +++ b/apps/mobile/src/theme/ThemeProvider.persistence.test.ts @@ -131,11 +131,18 @@ describe('hydrate', () => { }); describe('setPreference', () => { - it('applies the choice to memory immediately', () => { + it('applies the choice to memory immediately', async () => { usePreferenceStore.getState().setPreference('dark'); // Synchronous on purpose: the theme must flip on the very next render, not once a // device write has come back. expect(usePreferenceStore.getState().preference).toBe('dark'); + + // Not part of the assertion — it keeps the unawaited write from outliving this test. + // The `beforeEach` drain is one macrotask, and on a loaded CI runner the store's + // `await import()` takes longer than that. Left in flight it overlaps the next test's + // import, and Vitest then resolves that second import to the REAL async-storage + // module (`window is not defined`, swallowed by the store), so its write never happens. + await vi.waitFor(() => expect(setItem).toHaveBeenCalledWith(STORAGE_KEY, 'dark')); }); it('persists the choice under the documented key', async () => {