ADR-0039: Finance Monitor heartbeat + morning-brief integration¶
Status: Accepted (2026-06-13)
Status pass (2026-10-18): the code this record describes is in the private domain tree, not in this repository. heartbeat entry and brief slots reference finance_workflows; no finance module in this repository
Context: Finance build F4 (final finance phase). Implements ADR-0006's "Finance Monitor heartbeat (daily) + morning-briefing integration". Reuses the heartbeat (ADR-0013) + brief-slot (ADR-0029) machinery.
Context¶
F0–F3 made the finance data reliable and queryable on demand. F4 makes it proactive: a daily check that surfaces bills due/overdue and unusual spend, delivered both as a background alert and in the morning brief.
Decision¶
-
Monitor core (
iris_harness.finance.monitor) — pure, deterministic (explicitas_of):bills_due(statementamount_due+due_datefor the latest statement per account, plus manual bills, within a window and recently overdue) andunusual_spend(a category's current-month spend ÷ its trailing-N-month average ≥ ratio threshold, excluding theuncategorizedbucket; a material first-time category also flags).run_finance_monitorbundles both + net worth. All per-currency (ADR-0007). -
Bills = three signals (ADR-0006). Statement-derived dues (primary, already extracted in F2) + a manual
finance_billstable (iris finance bill add). Recurring-vendor detection is the third signal, deferred. -
finance_monitorheartbeat (config/heartbeats.yaml, cron0 8 * * *) —FinanceMonitorHandlerruns the monitor, emitsfinance.alert(with pre-rendered one-line bills/anomalies) when there's something to say, and returns aHeartbeatRunsummary. Follows the email-sweep pattern: never crash the heartbeat; emit an event for channel subscribers. -
Morning-brief integration — a
finance-briefskill with two read-only slot tools (brief_bills_due,brief_unusual_spend); themorning-briefingmanifest gains a Bills due and Unusual spend section (placed right after the user's tasks). This is the guaranteed daily surface even if the user never sees a push notification. -
CLI —
iris finance monitor(run now),iris finance bills,iris finance bill add|list.
Alternatives considered¶
- Deliver alerts only via push/notification channels. Rejected as the primary surface — wiring per-channel delivery is heavier and the brief is the established daily touchpoint; the event is emitted for a channel subscriber to consume when one is wired.
- Baseline = average over months-with-data. Chose average over a fixed
lookback_monthswindow (sparse prior months legitimately lower the baseline → a sudden spend is genuinely unusual relative to the window). Simpler and explainable; tunable.
Consequences¶
Positive:
- The agent is proactive: bills due/overdue and spend spikes surface daily
without the user asking.
- Monitor logic is pure + deterministic (as_of), fully unit-tested
without the scheduler.
Negative / still open:
- ~~Recurring-vendor bill discovery (signal 3) not built.~~ Built (2026-06-29):
iris_harness.finance.recurring.detect_recurring_bills mines the debit history for payees that
recur on a regular cadence (subscriptions/EMIs/utilities) and predicts the next due date;
surfaced via monitor.recurring_bills (suppressible) + iris finance recurring. See the
decisions log entry (ADR-0087).
- Spend baseline is a simple window average; no seasonality.
- Channel delivery of finance.alert relies on a future subscriber; the
brief is the guaranteed surface today.
Related ADRs¶
- ADR-0006 (finance ingestion + monitor + bill signals), ADR-0007 (per-currency), ADR-0013 (subsystem event topics), ADR-0029 (morning-brief slot mechanism), ADR-0037 (CLI), ADR-0038 (categories feed unusual-spend).