From 533862dadb4e91bf5945554569909839a95267f7 Mon Sep 17 00:00:00 2001 From: tmonga2208 Date: Sat, 10 Oct 2026 12:29:50 +0530 Subject: [PATCH 1/2] fix: unblock release pipeline (Android Gradle cache, detector history window) The Release workflow has never published: the Android job died in setup-java because `cache: gradle` hashes Gradle files that do not exist until prebuild, and since August the shared test step fails on a detector test. - android.yml: cache ~/.gradle with actions/cache keyed on the lockfile and app.json instead of setup-java's built-in Gradle cache. - detectors: anchor the 21-day history window on the localDate being evaluated rather than Date.now(), so a run for a past day still sees its own history. This is what made 'raises confidence when the pattern recurs' start failing once the wall clock moved 21 days past the fixture dates. Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/android.yml | 15 +++++++++++++-- apps/api/src/context/detectors.ts | 6 ++++-- 2 files changed, 17 insertions(+), 4 deletions(-) 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(), ]); From 2a7293319e6a70f22b459f944838caa41234e770 Mon Sep 17 00:00:00 2001 From: tmonga2208 Date: Sat, 10 Oct 2026 12:53:29 +0530 Subject: [PATCH 2/2] test(mobile): stop an unawaited theme write leaking into the next test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'applies the choice to memory immediately' fired setPreference and returned while its storage write was still behind an in-flight dynamic import. On a loaded runner the one-macrotask drain in beforeEach is not enough, the import overlaps the next test's, and Vitest resolves that second import to the real async-storage module ('window is not defined', swallowed by the store) — so 'persists the choice under the documented key' never sees its write. The test now waits for its own write before finishing. Reproduced at ~7% under CPU load before, 0/40 after. Co-Authored-By: Claude Opus 5.5 (1M context) --- apps/mobile/src/theme/ThemeProvider.persistence.test.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) 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 () => {