Code review for agent-written code.
Your agents write more code than you can read. Delta breaks each diff into sections and walks you through them.
Add Stripe billing provider
This PR adds Billing::Stripe,
a second provider built on the same foundation as PayPal. Teams connect by pasting a Stripe API key,
verified inside the persist transaction, then recorded usage flows to a subscription item and surfaces on the dashboard.
Registering Stripe as a billing provider
The foundation: a second billing provider slotted into the existing delegated-type registry alongside PayPal. Almost everything else — the sync pipeline, webhook handling, the dashboard — is inherited rather than reinvented.
The migration and schema dump are the mechanical table-creation that follows from the new provider model.
Review
dependent: :destroy cascades to billing history
Deleting a provider now deletes its accounts and sync records too. For billing data, a soft-disable is usually safer than a hard cascade.
Talking to the Stripe API
A small HTTP client the rest of the code goes through, so nothing else ever touches Faraday directly: bearer auth, explicit timeouts, JSON in and out, and a typed Billing::ApiError on any non-2xx.
A test-support helper that stubs the connection.
Connecting: API-key auth and verification
Unlike PayPal's OAuth dance, a team connects Stripe by pasting a restricted key. persist_api_key_for runs entirely inside one transaction, so a bad key rolls back cleanly with nothing half-written.
Review
Surface Stripe's reason to the user
On a bad key the transaction rolls back, but the controller flashes a generic message. Passing Stripe's reason through (e.g. "expired key") would save a support round-trip.
Syncing usage into a subscription item
The core of the integration: when usage is recorded in the app, push it to Stripe as a usage record on the customer's subscription item. SyncUsage is meant to be idempotent on the event id, so a retried job never double-bills.
An enum addition and a rule-seed fixture that follow from the new rule type.
Review
A timeout retry can double-bill
If the POST to /usage_records succeeds but the response times out, the job retries and already_synced? is still false — the customer is billed twice. Send a Stripe idempotency-key header, or record the attempt before the request.
Billing dashboard and connect UI
What the team actually sees and clicks, built on the shared integration controller and view conventions — so it inherits the catalog card, connect modal, and status badges for free.
Two view partials that are almost entirely markup.
End-to-end tests and recorded cassettes
Coverage proving the seams hold, with real request/response shapes captured rather than mocked — so the tests break if Stripe's contract drifts.
Recorded YAML cassettes and generated fixtures, not hand-written.
A retry after a timeout can double-bill — key idempotency on a Stripe idempotency-key header, not just event.id.
A retry after a timeout can double-bill — key idempotency on a Stripe idempotency-key header, not just event.id.
dependent: :destroy will cascade-delete billing history. Is a soft disable safer here?
Pass Stripe's actual rejection reason through to the flash so users know why a key failed.
// the rest
Review, fix, and ship.
The whole loop lives in one window.
- Comment on any line
- Drag across the gutter to comment on a whole range. Copy the review out in one click.
- Fix it with an agent
- Send your comments to an agent and it gets to work while you move on.
- Commit, PR, merge
- Push, open a pull request, and squash-merge right from the title bar.
- Fix CI when it breaks
- Checks fail? Hand the failing run to an agent, or jump straight to the logs.
- Your agents, built in
- A terminal sits under the diff, running the agents already on your machine.
- One worktree per branch
- Every branch gets its own checkout, so nothing steps on anything.
- See any slice
- The whole branch, just what's uncommitted, or a single commit.
- It stays on your machine
- Your CLIs, your logins, your code. None of it touches a server.