Add proactive nudges: recurring-charge-due-soon reminders - #51
Merged
Conversation
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
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.
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.
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 anext_expected_datefrom each pattern's last occurrence and classified frequency (weekly/biweekly/monthly/yearly canonical day-count, not the group's own possibly-off measured average).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.typeand akeyso 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./insightsendpoint (the 80% threshold filter) moved intoagent/db.pyalongsideget_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 fornext_expected_dateprojection andget_insights()'s window/boundary/combining logic)uv run ruff check .— cleannpm run build/npm run lint— cleannpx 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 incashflow.spec.jsthat the new seed expenses shifted)🤖 Generated with Claude Code
https://claude.ai/code/session_01Qz6JBNFwhgem5GY3i2BEMY
Generated by Claude Code