Skip to content

fix(reminder): recover the background guard when it fails to start or wakes up mid-permission-grant - #372

Merged
yyy-router merged 2 commits into
1024XEngineer:mainfrom
LUPENGHAN:fix/reminder-guard-permission-refresh
Aug 24, 2026
Merged

fix(reminder): recover the background guard when it fails to start or wakes up mid-permission-grant#372
yyy-router merged 2 commits into
1024XEngineer:mainfrom
LUPENGHAN:fix/reminder-guard-permission-refresh

Conversation

@LUPENGHAN

Copy link
Copy Markdown
Contributor

Fixes #371

Summary

  • ReminderGuardCoordinator.tsgetForegroundPermissionsAsync() 补上 try/catch,跟同函数里 hasStartedLocationUpdatesAsync/startLocationUpdatesAsync 的处理方式对齐——单次原生调用失败只跳过这次 reconcile,不再把 start() 变成 rejected promise。
  • AppProviders.tsxruntime.start()/stop().catch() 打日志,不再是静默的 unhandled rejection——即便以后还有别的地方漏包 try/catch,至少能看见日志定位到是哪次启动失败的。
  • AppRoot.tsxhandlePermissionsUpdated 补一句 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

… 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

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread frontend/src/app/AppRoot.tsx Outdated
…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
yyy-router merged commit d036aeb into 1024XEngineer:main Aug 24, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(reminder): background guard sometimes stays off until a full app restart

2 participants