fix(reminder): recover the background guard when it fails to start or wakes up mid-permission-grant - #372
Merged
yyy-router merged 2 commits intoAug 24, 2026
Conversation
… wakes up mid-permission-grant Two separate ways the reminder guard could stay off until a full app restart: - ReminderGuardCoordinator.ensureLocationUpdates() called Location.getForegroundPermissionsAsync() with no try/catch, unlike every other native call in the same function. A cold-start hiccup there rejected the whole reconcile() chain and propagated out of AppRuntime.start() — which AppProviders.tsx calls with a bare `void` and no .catch(), so the failure was a silent unhandled rejection that took the reminder engine down with it (AppRuntime only stops modules it recorded as started, and the guard's own start() never returned to be recorded). - Granting a permission mid-session only called reminder.rebuild(); nothing ever nudged the guard coordinator. If its one-shot reconcile() had already hit the "permission not granted" skip branch, there was no code path back to it short of a restart. AppProviders.tsx now catches runtime.start()/stop() failures instead of swallowing them silently, and AppRoot.tsx's handlePermissionsUpdated also calls schedules.refresh(), which the guard already subscribes to.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Contributor
There was a problem hiding this comment.
Reviewed the runtime startup, permission-refresh callback, guard reconciliation, and associated unit tests. The coordinator now contains the native permission-query failure, and the permission grant path does wake its schedule subscription. One failure path remains in the new refresh trigger. Focused Jest/typecheck validation was unavailable because this checkout has no executable node_modules tooling; git diff --check passed.
…g them go unhandled fennoai flagged the new refresh() call in handlePermissionsUpdated as another fire-and-forget promise that could silently fail. While adding a regression test for it, the sibling refresh() call in the SQLite-ready effect turned out to have the exact same gap — both now log instead of rejecting silently. Also covers the runtime.stop() failure branch in AppProviders.tsx that Codecov flagged as untested.
yyy-router
approved these changes
Aug 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #371
Summary
ReminderGuardCoordinator.ts的getForegroundPermissionsAsync()补上 try/catch,跟同函数里hasStartedLocationUpdatesAsync/startLocationUpdatesAsync的处理方式对齐——单次原生调用失败只跳过这次 reconcile,不再把start()变成 rejected promise。AppProviders.tsx的runtime.start()/stop()加.catch()打日志,不再是静默的 unhandled rejection——即便以后还有别的地方漏包 try/catch,至少能看见日志定位到是哪次启动失败的。AppRoot.tsx的handlePermissionsUpdated补一句services.schedules.refresh():权限中途被授予时,顺带触发协调器已经订阅的schedules.subscribe(),让它有机会重新 reconcile,不用等下次重启。Test plan
ReminderGuardCoordinator.test.ts新增用例:getForegroundPermissionsAsync抛错时start()仍然 resolve。AppProviders.test.tsx新增用例:runtime.start()reject 时走console.error('[app] runtime.start() failed', ...),而不是抛出。AppRoot.test.tsx补充断言:授予通知权限那次操作会带动一次schedules.refresh()。npm run lint/npm run format:check/npm run typecheck/npm run test(jest 716 + vitest 87)全绿。🤖 Generated with Claude Code