Skip to content

feat: organization credit system — metered checks, balance gate, runway projection#84

Open
rathorevaibhav wants to merge 20 commits into
mainfrom
feat/credit-system
Open

feat: organization credit system — metered checks, balance gate, runway projection#84
rathorevaibhav wants to merge 20 commits into
mainfrom
feat/credit-system

Conversation

@rathorevaibhav

Copy link
Copy Markdown
Member

Summary

Monetization foundation: organizations hold a credit balance; every executed check (uptime, certificate, domain) costs 1 credit, decremented atomically per check. At balance ≤ 0 all of the org's checks pause via a query-level gate (no monitor config is touched) and resume on the next scheduler tick after a grant. Runway is projected from live monitor configuration and surfaces everywhere: warning emails, an always-visible header chip, the org credits page, the super-admin panel, and live impact previews on the monitor forms.

Design spec: plans/2026-07-02-credit-system-design.md · Implementation plan: plans/2026-07-02-credit-system-implementation-plan.md

What's included

  • Ledger: credit_transactions (grants/adjustments/nightly usage-debit rollups) with row-locked writes and balance_after audit snapshots; credit_usage_daily powers the usage breakdown
  • Metering: CreditMeteringService — two writes per check, race-free zero-crossing (atomic conditional UPDATE gates the one-shot "monitoring paused" email), never throws into the check pipeline
  • Enforcement: Monitor::scopeEnabled override gates uptime + certificate selection through Spatie's repository; domain command gated incl. --force. Incidentally fixes a vendor OR-precedence bug that selected uptime-only monitors for certificate checks
  • Runway: config-derived projection (CreditRunwayService + mirrored JS util), used by daily warning escalation (lowcritical), shared Inertia props, header chip, /credits page, super-admin grant panel, and monitor-form previews
  • Ops: credits:grant {amount} --org=|--all (auditable rollout/top-up path), credits:rollup-usage (00:15, idempotent), credits:evaluate-warnings (00:30)

⚠️ Deploy runbook

Migrating adds credit_balance = 0 to every existing organization, which pauses all their checks. Immediately after php artisan migrate, run:

php artisan credits:grant <amount> --all

Env: CREDITS_DEFAULT_GRANT, CREDITS_WARNING_LOW_DAYS, CREDITS_WARNING_CRITICAL_DAYS (see .env.example). Details in the spec's Rollout section.

Billing policy decisions (documented in spec)

  • Failed checks are billed (same resources consumed); checks paused for lack of credits cost nothing
  • DOWN-monitor rechecks are billed: the vendor rechecks DOWN monitors every minute regardless of interval, so burn during an outage can exceed the projection (decided 2026-07-04)
  • Soft-deleted orgs: past usage still gets its audit rollup; no warnings/pause emails (their checks are gated off)

Testing

  • PHP: 226 passed / 969 assertions (66 new credit tests; baseline was 160) — MySQL, real vendor entry points, no mocked scopes
  • JS: 75 passed (7 new runway-util tests); vite build green; pint clean
  • Every task TDD'd + individually code-reviewed; final whole-branch review (all findings fixed and re-reviewed)

Accepted follow-ups (non-blocking)

Notification class dedup + body-line tests; scopeFunded() helper for the 3× balance-gate closure; DB unique on rollup idempotency key; render-or-drop unused usageByDay.byType; chip TONES dedup; suppress "(currently 0)" on the create-form preview.

🤖 Generated with Claude Code

rathorevaibhav and others added 20 commits July 4, 2026 13:42
Moved off the tenancy PR (#82) so that PR stays focused on multi-tenant
organizations. The credit system is deferred until issue #23 (tenancy)
merges to main; this branch holds the design spec and implementation
plan to be picked up then.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Covers schema/ledger/metering/enforcement, runway projection, scheduled
rollup + warnings, default grant, org credits page, header chip,
super-admin panel, and monitor-form impact preview. Execution deferred
until issue #23 tenancy merges to main.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ance gate

The credit-system enforcement gate (Monitor::scopeEnabled()) now requires
the owning organization to have credit_balance > 0. This test's organization
fixture had the DB-default balance of 0, causing even the kept monitor to
be excluded. The test's intent (trashed monitors excluded from Spatie
repository) remains untouched—only the fixture needs funding to satisfy
the credit gate as a precondition.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…zero-crossing, notification isolation, UI/docs polish

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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