Skip to content

feat(collectors): failure taxonomy — a 401 and a timeout are different problems (CashPilot-5bdm) - #313

Merged
GeiserX merged 1 commit into
mainfrom
feat/collector-auth-taxonomy
Aug 8, 2026
Merged

feat(collectors): failure taxonomy — a 401 and a timeout are different problems (CashPilot-5bdm)#313
GeiserX merged 1 commit into
mainfrom
feat/collector-auth-taxonomy

Conversation

@GeiserX

@GeiserX GeiserX commented Aug 8, 2026

Copy link
Copy Markdown
Owner

An expired credential earns $0 until a human acts; a provider outage fixes itself. They were the same free-text error string end to end, so the bell could only ever say "collection failed".

Taxonomy: EarningsResult.error_kindauth / transient / shape, None = unknown and never guessed (an unknown labelled "self-heals" hides a real fault). Explicit kinds in honeygain, bytelixir, packetstream, salad, grass; classify_exception for unclassified escapes. Ten remaining collectors' shape-errors tracked as CashPilot-dqaf.

Attribution fix: a collector that RAISES is converted to an attributed result inside _collect_bounded — the exception used to reach the gather() where the platform name was unrecoverable: no alert, no bell, no metric.

Persistence: alerts.category column (guarded migration; closed enum whitelisted at the boundary so error_kind can never smuggle unredacted exception text — which for several providers IS the credential — into a durable any-role table). A dedupe hit inside the 24h window now refreshes the stored kind+message: grass alternates auth/transient with no success in between, and the pinned first kind meant a restart could render a dead credential as a muted self-healing blip (review HIGH). Push titles carry the cause — the only channel an unattended install has.

Bell: auth → "credential expired" + Fix credential; transient → muted, no button (never point a user at their credential for a network blip); shape → no button (our bug); unknown → button stays. Credentials ≥75% of known lifetime (or past it — the forecast must not vanish when it comes true) join as expiring — a distinct category, because a prediction is not an observed 401 — and the badge goes muted when forecasts are all there is.

27 new tests (negative controls: timeout-never-auth, unknown-never-transient, no history backfill, stale-kind refresh incl. the warm-rebuild path, closed-enum smuggling control, semaphore release). Full suite 4503 passed. Salad mutation (auth→transient) verified to fail the suite. Fresh review completed; all findings addressed or filed (dqaf).

…e taxonomy end to end (CashPilot-5bdm)

An expired credential earns $0 until a human acts; a provider outage
fixes itself. They used to be the same free-text string, so the bell
could only say 'collection failed' — teaching the user to ignore the
one alert that needs them.

EarningsResult.error_kind (auth/transient/shape, None = unknown, never
guessed) with classify_exception for unclassified escapes; explicit
kinds in honeygain, bytelixir, packetstream, salad and grass. A raised
collector exception is now converted while the collector is still in
hand, so its platform survives — it used to reach the gather() where
attribution was unrecoverable and the failure was invisible.

alerts.category column (closed enum, whitelisted at the boundary so a
future error_kind=str(exc) cannot smuggle credential text into a
durable any-role table) with a guarded migration; a dedupe hit inside
the 24h window now refreshes the stored kind and message, because grass
alternates auth and transient errors with no success in between and the
first kind was pinned — a restart could render a dead credential as a
muted self-healing blip. The push notification title carries the cause.

The bell renders auth as 'credential expired' with a Fix-credential
button, transient muted with NO button (never point a user at their
credential for a network blip), shape without a button (our bug, their
credential is fine), unknown keeps the button. Credentials past 75% of
their known lifetime (or past it) join the bell as 'expiring'
forecasts — their own category, because a prediction is not an observed
rejection — with a muted badge when forecasts are all there is.

27 new tests including the negative controls that matter: a timeout is
never auth, an unknown is never transient, history is never backfilled,
the stored kind cannot go stale, the semaphore survives the new
exception path.
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@GeiserX, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 18 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: dd90b4e2-5def-48b6-b7d7-b8aa7f001848

📥 Commits

Reviewing files that changed from the base of the PR and between 6ebd285 and 9fd591b.

📒 Files selected for processing (11)
  • app/collectors/base.py
  • app/collectors/bytelixir.py
  • app/collectors/grass.py
  • app/collectors/honeygain.py
  • app/collectors/packetstream.py
  • app/collectors/salad.py
  • app/database.py
  • app/main.py
  • app/static/js/app.js
  • tests/test_beads_batch_25.py
  • tests/test_collector_error_taxonomy.py

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.

@codecov

codecov Bot commented Aug 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.91667% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 95.57%. Comparing base (a60b947) to head (9fd591b).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
app/main.py 95.23% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #313      +/-   ##
==========================================
+ Coverage   95.56%   95.57%   +0.01%     
==========================================
  Files          51       51              
  Lines        6646     6685      +39     
==========================================
+ Hits         6351     6389      +38     
- Misses        295      296       +1     
Files with missing lines Coverage Δ
app/collectors/base.py 100.00% <100.00%> (ø)
app/collectors/bytelixir.py 94.66% <ø> (ø)
app/collectors/grass.py 94.25% <ø> (ø)
app/collectors/honeygain.py 100.00% <100.00%> (ø)
app/collectors/packetstream.py 96.42% <ø> (ø)
app/collectors/salad.py 100.00% <100.00%> (ø)
app/database.py 95.05% <100.00%> (+0.05%) ⬆️
app/main.py 97.69% <95.23%> (-0.04%) ⬇️
🚀 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
GeiserX merged commit 2297a77 into main Aug 8, 2026
8 checks passed
@GeiserX
GeiserX deleted the feat/collector-auth-taxonomy branch August 8, 2026 00:06
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