Adding a Resource
The crud-with-billing shape: model, policy, controller, views, and specs for a new account-scoped resource.
Almost every feature you add to a One Shot app is the same shape: a model, a policy, a controller,
a set of views, routes, and specs, all scoped to the current account. The kit ships one working
example of this shape, the Project resource, specifically so you can copy it rather than design
it from scratch each time. The fastest way to add one is the crud-with-billing skill in Claude
Code, which generates exactly this shape for you.
/crud-with-billing
Tell it the resource name (for example Invoice), its attributes (amount:integer note:text), and
whether it's a paid feature or a free one.
The shape, piece by piece
- Model and migration.
bin/rails g model Invoice account:references amount:integer note:text, then addnull: falsewhere it belongs and any validations. Every model getsbelongs_to :account; see Accounts and Tenancy for why that's non-negotiable. - The account association.
has_many :invoices, dependent: :destroyonapp/models/account.rb, soCurrent.account.invoicesworks everywhere else. - A Pundit policy.
app/policies/invoice_policy.rb, subclassingApplicationPolicy. Its inheritedScopealready filters to the acting user's account; you don't write that part yourself. See Authorization. - A controller. Query only through
Current.account.invoices, neverInvoice.all. Callauthorizeandpolicy_scope. If this is a paid feature,include RequireEntitlement; if it's free, leave it out. See Gating a Feature. - Views.
index,show,new,edit, and_form, using the Terminal design system classes already defined inapp/assets/stylesheets/application.css(.card,.btn,.field,.eyebrow). Copy theProjectviews as a starting layout. - Routes.
resources :invoicesinconfig/routes.rb. - Billing, only if this introduces a new plan or tier. Extend
Billing::PLANSinapp/adapters/billing.rband add the price id toBilling::Stripe::PRICE_ENV; manage the actual Stripe product and price through the Stripe MCP server orbin/rails console. - Specs. A model spec, a policy spec, and a request spec, always including the tenant-isolation test: fetching a record that belongs to a different account by id must 404, not 500 and not silently succeed. A paid resource also gets an entitlement-gate test: an unsubscribed account should be redirected to pricing, not shown the feature.
Verify
Run bin/check. The tenant-isolation test is the one that matters most here: if it's missing or
passing for the wrong reason, a new resource can quietly leak data across accounts the moment it
ships.
Next
If the resource needs work outside the request cycle, see Background Jobs.