Skip to content

Add proactive nudges: recurring-charge-due-soon reminders - #51

Merged
derekl-beep merged 1 commit into
mainfrom
claude/proactive-nudges
Jul 16, 2026
Merged

Add proactive nudges: recurring-charge-due-soon reminders#51
derekl-beep merged 1 commit into
mainfrom
claude/proactive-nudges

Conversation

@derekl-beep

Copy link
Copy Markdown
Owner

Summary

Ships the in-app half of "proactive ambient insights" from the roadmap — actual push notifications are a separate infra project (VAPID keys, a subscription table, a scheduled worker) and stay out of scope here; this reuses data already being computed.

  • get_recurring_expenses() now projects a next_expected_date from each pattern's last occurrence and classified frequency (weekly/biweekly/monthly/yearly canonical day-count, not the group's own possibly-off measured average).
  • New get_insights() combines that with the existing budget-threshold check into one proactive signal list, so a recurring charge due in the next 3 days ("Gym Membership ($45.00) renews in 2 days") surfaces in the same dismissible banner as budget warnings, without waiting to be asked.
  • Both insight types now carry a type and a key so the frontend can dismiss them independently even when a category collision would otherwise make that ambiguous — the existing budget-insight shape is unchanged, just extended.
  • Business logic that was living in the /insights endpoint (the 80% threshold filter) moved into agent/db.py alongside get_insights(), per this repo's stated architecture: no business logic in the API layer beyond auth/rate-limiting.

Test plan

  • uv run pytest tests/ — 177 passed (new coverage for next_expected_date projection and get_insights()'s window/boundary/combining logic)
  • uv run ruff check . — clean
  • npm run build / npm run lint — clean
  • Full npx playwright test — 80 passed, mobile + desktop, including a new recurring-reminder e2e spec (seed data changed, so the full suite was run — this also caught and fixed a hardcoded net-cash-flow total in cashflow.spec.js that the new seed expenses shifted)

🤖 Generated with Claude Code

https://claude.ai/code/session_01Qz6JBNFwhgem5GY3i2BEMY


Generated by Claude Code

Ships the in-app half of the "proactive ambient insights" roadmap item
(#29) — actual push notifications (#30) are a separate infra project
(VAPID keys, a subscription table, a scheduled worker) and stay out of
scope here; this reuses data already being computed.

get_recurring_expenses() now projects a next_expected_date from each
pattern's last occurrence and classified frequency. A new get_insights()
combines that with the existing budget-threshold check into one
proactive signal list, so a recurring charge due in the next 3 days
("Gym Membership ($45.00) renews in 2 days") surfaces in the same
dismissible banner as budget warnings, without waiting to be asked.

Both insight types now carry a `type` and a `key` so the frontend can
dismiss them independently even when a category collision would
otherwise make that ambiguous — the existing budget-insight shape is
unchanged, just extended, so no other client of it needed to change.

Business logic that was living in the /insights endpoint (the 80%
threshold filter) moved into agent/db.py alongside get_insights(), per
this repo's stated architecture: no business logic in the API layer
beyond auth/rate-limiting.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qz6JBNFwhgem5GY3i2BEMY
@derekl-beep
derekl-beep merged commit 46f3c0f into main Jul 16, 2026
3 checks passed
derekl-beep pushed a commit that referenced this pull request Jul 16, 2026
main moved forward with PRs #50 (usage instrumentation + session
persistence) and #51 (proactive nudges) since this branch was cut.
Conflicts were both cases of two features adding adjacent schema/config
blocks — resolved by keeping both sides, not choosing one:

- agent/db.py: usage_events + chat_sessions table creation (from main)
  alongside savings_goals (from this branch).
- tests/conftest.py and scripts/seed_e2e_data.py: TRUNCATE lists needed
  every new table name from both sides, not just one.

No logic changes beyond the merge itself.
derekl-beep pushed a commit that referenced this pull request Jul 16, 2026
main moved forward with PRs #50 (usage instrumentation + session
persistence) and #51 (proactive nudges) since this branch was cut.
The one real conflict was tests/test_agent.py, where both this branch
(a Sentry capture_exception test) and main (serialize_block/session_lock
tests) added new tests at the same location — kept both, no logic
changes beyond the merge itself.
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.

2 participants