Skip to content

feat: detect services that are running but no longer earning (CashPilot-kbs) - #149

Merged
GeiserX merged 5 commits into
mainfrom
feat/earnings-flatline
Aug 2, 2026
Merged

feat: detect services that are running but no longer earning (CashPilot-kbs)#149
GeiserX merged 5 commits into
mainfrom
feat/earnings-flatline

Conversation

@GeiserX

@GeiserX GeiserX commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Independent of the deploy stack — branched off main.

The gap

Catalog rot is the most common way a user silently stops earning, and the weekly liveness check cannot see the worst case: a service that is up but no longer paying.

The container runs. The collector authenticates. The balance never moves. Every other view of the system looks healthy, so nothing surfaces it — the user finds out by eventually noticing they stopped being paid.

The detection

get_flatlined_services() reports a balance that has not moved across 7 recorded days, raised as a distinct flatline alert kind and exposed at GET /api/earnings/flatlines.

The restraint matters as much

The bead is explicit that a report which cries wolf is a report nobody reads, so most of the logic is about what must not be reported:

Case Reported? Why
Balance moved at all, even once Slow earners are still earning
Fewer than 7 recorded days A new deployment has not had time to earn
Balance always exactly zero Never paid — a setup problem, not a service that stopped
Only 3 readings across 30 days The window counts distinct recorded days, so a collection outage cannot masquerade as a flat balance
7+ days at a non-zero, unchanging balance Running, authenticated, and not earning

record_alert's existing per-kind cooldown keeps this to one notification per service rather than one per collection cycle, and the whole check is wrapped so a diagnostic can never break the collection run itself.

Scope

This is the flatline half of the bead. The gone-vs-down streak tracking lives in the weekly liveness script and needs state that survives between CI runs, so it is deliberately not in this change.

Verification

  • ruff check . && ruff format --check . — clean
  • pytest --cov=app --cov-fail-under=901342 passed, coverage 93.83%
  • 7 new tests, six of which assert the non-reporting cases above.

Summary by CodeRabbit

  • New Features

    • Added detection for services whose nonzero earnings remain unchanged for seven or more recorded days.
    • Added an authenticated API endpoint to view flatline earnings alerts.
    • Added notifications for detected flatlines, with cooldowns to prevent duplicate alerts.
    • Handles insufficient history, zero balances, outages, and database errors safely.
  • Documentation

    • Documented flatline earnings detection and related release planning updates.

…ot-kbs)

Catalog rot is the most common way a user silently stops earning, and the
liveness check cannot see the worst case: a service that is UP but no
longer paying. The container runs, the collector authenticates, the
balance never moves - and every other view of the system looks healthy, so
nothing surfaces it. The user finds out by eventually noticing.

get_flatlined_services reports a balance that has not moved across 7
recorded days, raised as a distinct "flatline" alert kind and exposed at
GET /api/earnings/flatlines.

The restraint matters as much as the detection, since the bead is explicit
that a report which cries wolf is a report nobody reads:

- too little history is not a flatline; a new deployment has not had time
  to earn anything yet
- an always-zero balance is not reported: that is a service that never
  paid, usually a setup problem the user already knows about, not one that
  stopped
- the window counts DISTINCT RECORDED DAYS, so a collection outage (which
  records nothing) cannot masquerade as a flat balance

record_alert's existing per-kind cooldown keeps this to one notification
per service rather than one per collection cycle, and the check is wrapped
so a diagnostic can never break the collection run itself.

Scope: this is the flatline half. Gone-vs-down streak tracking lives in
the weekly liveness script and needs state that survives between CI runs,
so it is not in this change.
@GeiserX

GeiserX commented Aug 1, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Flatline earnings detection

Layer / File(s) Summary
Flatline detection query and validation
app/database.py, tests/test_earnings_flatline.py, CHANGELOG.md
The database reports nonzero balances that remain unchanged across at least seven recorded days. Tests cover valid and excluded histories. The changelog documents the behavior and endpoint.
Alerting and API integration
app/main.py, tests/test_earnings_flatline.py
Collection runs the flatline check after earnings processing. The check records cooldown-limited alerts, sends notifications, and isolates failures. An authenticated endpoint returns detected services. Tests cover API, notification, cooldown, and failure behavior.

Project release documentation

Layer / File(s) Summary
Release follow-up instructions
docs/GOAL.md
The document adds release verification, follow-up work, review handling, and decision instructions.

Estimated code review effort: 3 (Moderate) | ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: detecting active services whose earnings have stopped increasing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/earnings-flatline

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@codecov

codecov Bot commented Aug 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.23%. Comparing base (cb00a8b) to head (9b23ead).
⚠️ Report is 9 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #149      +/-   ##
==========================================
+ Coverage   93.91%   94.23%   +0.32%     
==========================================
  Files          35       36       +1     
  Lines        4123     4475     +352     
==========================================
+ Hits         3872     4217     +345     
- Misses        251      258       +7     
Files with missing lines Coverage Δ
app/database.py 93.39% <100.00%> (+1.44%) ⬆️
app/main.py 96.85% <100.00%> (+0.07%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

GeiserX added 4 commits August 2, 2026 00:32
codecov flagged the patch: the detection query was tested but the part
that actually reaches the user - the alert and the endpoint - was not.

Extracted the inline block from the collection loop into _flatline_check
so it can be exercised directly, and covered the behaviour that matters:
the endpoint returns the flatlined services; a newly detected flatline
notifies and the message says how many days; a service already in cooldown
does NOT notify again (one per service, not one per collection cycle); and
a failing check never raises, because a diagnostic must not be able to
take down the collection run it is diagnosing.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/database.py`:
- Around line 1543-1552: Update the flatline detection flow around the earnings
aggregation query and its direct GET /api/earnings/flatlines caller to filter
results by platforms confirmed active and successfully collected in the current
run, excluding failed or removed services. Propagate that current collection
state into the detection/API path (or reuse persisted equivalent state) rather
than changing only _flatline_check, and add coverage for a service with seven
unchanged historical readings whose current collection fails.

In `@app/main.py`:
- Around line 264-269: The flatline collection must clear the stored cooldown
when a service’s balance has increased. In app/main.py lines 264-269, update the
flow around database.get_flatlined_services and record_alert to detect recovery
from a changed balance and call clear_alerts("flatline", flat["platform"]) only
for that recovery; in tests/test_earnings_flatline.py lines 173-194, add a
regression test covering flatline, balance increase, then flatline again before
cooldown expiry.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3df2267f-7425-4837-b778-cab039594a44

📥 Commits

Reviewing files that changed from the base of the PR and between 2e139d8 and 9b23ead.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • app/database.py
  • app/main.py
  • docs/GOAL.md
  • tests/test_earnings_flatline.py

Comment thread app/database.py
Comment thread app/main.py
@GeiserX
GeiserX merged commit 1fd9735 into main Aug 2, 2026
8 checks passed
@GeiserX
GeiserX deleted the feat/earnings-flatline branch August 2, 2026 11:32
@GeiserX

GeiserX commented Aug 2, 2026

Copy link
Copy Markdown
Owner Author

Closing the review threads on this PR: every finding is fixed on main, verified by reading the code there. Fixed in #158 — the flatline query now requires a reading from today (a failing collector no longer reads as flat), and _flatline_check clears the cooldown for services that started earning again.

They stayed open only because the fixes landed in a later PR — CodeRabbit auto-resolves a thread when the fix is pushed to the same branch, and these were not.

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.

1 participant