This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
CASA is a Rails app used by CASA (Court Appointed Special Advocate) chapters to track volunteer work with foster youth. The domain shape: a CasaCase (a youth) is assigned to a Volunteer, who logs CaseContacts; Supervisors oversee volunteers; CasaAdmins run the chapter. The app is multi-tenant — every record belongs to a CasaOrg, and cross-org data leaks are the most important class of bug to avoid.
| Task | Command |
|---|---|
| One-time setup | bin/setup |
| Run app (web + JS/CSS watchers) | bin/dev then visit http://localhost:3000 |
| Run full RSpec suite | bin/rails spec |
| Run a single spec file | bundle exec rspec spec/path/to/file_spec.rb |
| Run a single example | bundle exec rspec spec/path/to/file_spec.rb:LINE |
| Run JS tests | npm run test |
| Run all linters w/ autofix | bin/lint (standardrb + erb_lint + standardjs + factory_bot:lint) |
| Update env after pulling main | bin/update (migrate, bundle, npm, after_party) |
| Run post-deploy tasks | bundle exec rake after_party:run |
| Local mailer previews | http://localhost:3000/rails/mailers |
Seed login credentials (password 12345678 for all):
volunteer1@example.com,supervisor1@example.com,casa_admin1@example.comat/users/sign_inallcasaadmin@example.comat/all_casa_admins/sign_in
Rails 7.2 on Ruby (.ruby-version), PostgreSQL, Hotwire (Turbo + Stimulus), Bootstrap 5, ESBuild, Devise + Devise-Invitable, Pundit, Draper, ViewComponent, Delayed Job, Flipper, Paranoia (soft deletes), Strong Migrations. Linting: Standard.rb + standard-rails, StandardJS, erb-lint. Testing: RSpec + Capybara (system tests via Selenium/Chrome), Jest. There is no UI sign-up — users are admin-invited only (ADR 0002).
There are two separate Devise models: User (with subclasses Volunteer, Supervisor, CasaAdmin — these are real STI subclasses, each with its own model file) and AllCasaAdmin (a separate top-level model and table for super-admins who span orgs). Devise is configured for both in config/routes.rb, and there is a parallel app/controllers/all_casa_admins/ namespace + app/models/all_casa_admins/ for the all-casa flows. When touching auth, check both sides.
- Models include
ByOrganizationScope(app/models/concerns/by_organization_scope.rb) which gives.by_organization(casa_org). - The
Organizationalcontroller concern (app/controllers/concerns/organizational.rb) providescurrent_organizationfrom the signed-in user. - Pundit policy scopes filter by org:
scope.by_organization(user.casa_org).
Every controller index action should call policy_scope; every show/update/destroy should call authorize. Controllers typically use after_action :verify_authorized (with explicit except:). When adding a new model with org-scoped data, all three places must be wired up or queries will leak across orgs.
Policies live in app/policies/. They define both predicate methods (update?, destroy?) and permitted_attributes (role-based field allowlists for strong params). When adding a model attribute that admins/supervisors/volunteers should be able to set, update permitted_attributes in the policy — not just the controller.
- Controllers stay thin. Standard pattern:
set_recordbefore_action,authorize, save/render with:unprocessable_contenton failure. - Service objects (
app/services/) —ServiceName.new(args).perform. Used for CSV exports, SMS reminders, complex multi-record operations. - Decorators (
app/decorators/, Draper) — presentation logic. Access view helpers viah.helper_method. Called asmodel.decorate. Date formatting, conditional display strings, etc. belong here, NOT in models or ERB. - ViewComponents (
app/components/) — reusable UI primitives (badges, modals, sidebar, dropdown menus, form bits). - Parameter objects (
app/values/) — builder-pattern wrappers for non-trivial strong-params logic, e.g.VolunteerParameters.new(params).with_password(pw).without_active. - Concerns (
app/{models,controllers}/concerns/) — shared behavior viaextend ActiveSupport::Concern. Notable model concerns:ByOrganizationScope,Roles,Api, and aCasaCase/directory of concern modules used by theCasaCasemodel.
Hotwire-first: Turbo for navigation/forms, Stimulus controllers in app/javascript/controllers/. There is an in-progress migration from inline JS / jQuery to Stimulus — new code should be Stimulus, but legacy jQuery is not flagged. JS is bundled via ESBuild (bin/asset_bundling_scripts/build_js.js); SCSS via the sass CLI. Both have watchers in Procfile.dev. Email views require inline CSS for client compatibility (ADR 0007).
- Soft deletes via Paranoia:
destroymarks deleted, doesn't hard-delete. Be deliberate when reasoning about uniqueness or "where is this record." - Strong Migrations is enabled and will block unsafe DDL (column removal without
safety_assured, non-concurrent index adds on large tables, type changes). Reversibility is required. - Soft-deletion-aware uniqueness:
validate_uniqueness_of(...).scoped_to(:casa_org_id)is the typical pattern — uniqueness is per-org. - Enums use prefix syntax:
enum :status, {active: 0, inactive: 1}, prefix: :status. - Post-deployment tasks run via After Party (
bundle exec rake after_party:run), invoked automatically bybin/updateand on Heroku release.
- System tests are preferred over controller tests (ADR 0006 —
app/controllers/concerns/users/andspec/controllers/exist but are minimal). New UI behavior should land inspec/system/. - Factories use traits for variants:
create(:casa_case, :active).bin/lintrunsfactory_bot:lintto catch invalid factories. - Use
buildfor unit tests;createonly when persistence is required.letfor lazy,let!when the record must exist before the example. - shoulda-matchers handles association/validation tests.
- No
sleepin tests — rely on Capybara waiting. - Flaky tests are disabled with
xit+ a tracking issue, never deleted.
This project uses Standard.rb (not vanilla RuboCop). Don't fight it on spacing/quotes/trailing commas. There is also a .standard_todo.yml of grandfathered violations — leave older files alone unless touching them substantively. Older migration files (2020–2024) are intentionally excluded from linting.
.github/instructions/ruby.instructions.mdand.github/instructions/copilot-review.instructions.md— the project's own review checklist; the source of truth for "what is a bug-shaped change here."doc/architecture-decisions/— ADRs (especially 0002 no-UI-signup, 0003 two-user-tables, 0006 few-controller-tests, 0007 inline-email-CSS).doc/productsense.md— product philosophy.db/schema.rb— paste into dbdiagram.io for an ERD.