Files
pulse/docs/PATROL_WEEKLY_DIGEST.md
T
rcourtman 8ea94d792b Add the Patrol weekly digest endpoint
Nothing in Pulse showed a paying customer what Patrol had done for them:
about 164 runs a month per install, findings raised, investigations and
fixes, and none of it summarised. GET /api/ai/patrol/digest rolls the last
N days (default 7, max 30) up from records Pulse already keeps: run
history, the findings store, Patrol-origin action audits, and the usage
cost store. It adds no telemetry and persists nothing. The payload
reports when the bounded run history no longer covers the window and when
model pricing is unknown, rather than quietly under-counting.

This is the first slice of the "Patrol weekly digest" named bet in the
pulse-pro demand ledger; the in-app "This week" card follows once its
browser pass is recorded. docs/PATROL_WEEKLY_DIGEST.md holds the design
note and the honest limits of each line.

status.json registers the patrol-value-visibility coverage gap, the
candidate lane, and its work claim. It also drops the second, identical
copy of the ai-provider-guided-setup coverage gap that landed with #1853;
the duplicate id fails the status audit on main for every pull request.
2026-09-02 08:53:50 +01:00

5.9 KiB

Patrol Weekly Digest

Status: building — first slice (aggregation endpoint and in-app "This week" card) landing on main 2026-09-01; weekly email is the unscheduled second slice. Demand ledger: pulse-pro/FEATURE_REQUESTS.md, "Patrol weekly digest (what Patrol did for you)", a named bet under the Patrol operations loop.

The job, in the customer's words

"Show me what I am paying for." A Pro customer turns Patrol on, it runs about five times a day, and nothing in Pulse ever adds that up. They see individual findings when they open the page and individual emails when a finding fires, but never the week: how often Patrol looked, what it caught, what it fixed, what it cost. The 2026-09-01 assessment found 33 of 175 Pro subscriptions past due and 80 percent of paying installs never seeing the paid loop fire. A customer who cannot see the work stops paying for it.

The least-expert plausible reader is a homelab operator who set Patrol up once and opens Pulse when an alert email arrives. They do not know what an "investigation", a "verdict", or an "evidence class" is. The digest must read as a plain weekly account, and every number must lead to something they can click or a decision they can make.

What the digest reports

Everything below is computed from data Pulse already records. No new telemetry fields; the readout task grades this bet from support and issue mentions.

Line Source What the reader does with it
Runs: how many times Patrol checked, how many checks in total, how many resources it covered, when it last ran PatrolRunRecord history (internal/ai/patrol.go, capped at 100 runs) Confirms Patrol is alive. If runs are zero or failed, the card says so and points at Patrol setup.
Mode: monitor, approval, assisted, or full Service.GetEffectivePatrolAutonomyLevel Explains why actions were or were not taken. Monitor mode with fixable findings is the upgrade prompt, stated plainly.
New issues this week, and how many are still open by severity run new_findings totals plus the findings store for still-tracked findings detected in the window Open critical or warning issues are the thing to look at; the count links to the findings list.
Issues resolved and how many Patrol resolved on its own run resolved_findings totals plus manual resolutions still in the findings store This is the "it did something" line.
Issues you dismissed or muted finding lifecycle events (dismissed, suppressed) in the window Reminds the reader what they told Patrol to ignore.
Investigations and their outcomes finding lifecycle investigation_outcome events in the window Shows the paid loop firing; outcomes that need attention are named.
Actions proposed, approved, executed, verified, failed, and still waiting canonical action audit records with origin.surface = patrol (internal/unifiedresources) "Waiting" links straight to Actions and approvals.
Alerts Patrol looked into alert-triggered runs (alert_fired, alert_flapping) and their distinct alert identifiers Shows Patrol responding to the reader's own alerts, not just its schedule.
Estimated model spend, tokens, and calls internal/ai/cost usage events with use_case = patrol Answers "what does this cost me" without opening the AI cost dashboard. Marked as an estimate; unknown pricing is stated, never zeroed.

Deliberately left out: per-run tool call traces, evidence classes, verdict strings, model names, and anything the reader cannot act on. Those stay in run history and the Actions audit, one click away.

Honest limits of the data

  • Run history keeps the last 100 runs. At five or six runs a day a week fits, but a busy install with event-triggered runs can overflow. The payload carries window.history_complete and window.history_since; the card says "since " instead of "this week" when the window is cut short.
  • Resolved findings are purged from the findings store 24 hours after resolution, and dismissed findings after 30 days. Resolution totals therefore come from run records, and severity is only reported for findings still tracked. Investigation outcomes are read from lifecycle events on tracked findings, so a finding that was investigated, fixed, and purged more than a day ago no longer contributes. Executed and verified actions do not have this gap: action audit records are durable.
  • Spend is cost.EstimateUSD over recorded usage events. When the model has no known price the digest says pricing is unknown rather than reporting a smaller number.

Surface

In-app card first. A "This week" card at the top of the Patrol page's Activity tab, above Verified outcomes (frontend-modern/src/features/patrol/PatrolWeeklyDigestCard.tsx, backed by GET /api/ai/patrol/digest?days=7). It costs nothing to deliver, every paying install can see it, and the Activity tab is already where "what happened" questions are answered. The Inbox stays a decision surface and does not gain a summary card.

Weekly email second. The past-due population is the population that has stopped opening Pulse, so the email is the slice that reaches them. It should render the same digest through the existing enhanced email manager (internal/notifications/email_enhanced.go, SendEmailWithRetry) on a weekly schedule, respecting the existing notification enable state and email destination. Webhook and Apprise channels are not in scope for the first email slice. Not scheduled; the ledger entry's decline condition governs it.

API

GET /api/ai/patrol/digest — requires ai:execute scope like the other Patrol read endpoints. Query days (1 to 30, default 7). Returns the window, current mode, and the run, finding, investigation, action, alert, and spend rollups described above. Empty history returns zero counts with window.history_complete = true; a missing AI or Patrol service returns the same zero shape so the card can render its "Patrol has not run" state.