Skip to content

Commit 5b75f58

Browse files
authored
claude config (#6924)
1 parent d9cec73 commit 5b75f58

1 file changed

Lines changed: 80 additions & 0 deletions

File tree

CLAUDE.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## What this app is
6+
7+
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.
8+
9+
## Common commands
10+
11+
| Task | Command |
12+
|---|---|
13+
| One-time setup | `bin/setup` |
14+
| Run app (web + JS/CSS watchers) | `bin/dev` then visit http://localhost:3000 |
15+
| Run full RSpec suite | `bin/rails spec` |
16+
| Run a single spec file | `bundle exec rspec spec/path/to/file_spec.rb` |
17+
| Run a single example | `bundle exec rspec spec/path/to/file_spec.rb:LINE` |
18+
| Run JS tests | `npm run test` |
19+
| Run all linters w/ autofix | `bin/lint` (standardrb + erb_lint + standardjs + factory_bot:lint) |
20+
| Update env after pulling main | `bin/update` (migrate, bundle, npm, after_party) |
21+
| Run post-deploy tasks | `bundle exec rake after_party:run` |
22+
| Local mailer previews | http://localhost:3000/rails/mailers |
23+
24+
Seed login credentials (password `12345678` for all):
25+
- `volunteer1@example.com`, `supervisor1@example.com`, `casa_admin1@example.com` at `/users/sign_in`
26+
- `allcasaadmin@example.com` at `/all_casa_admins/sign_in`
27+
28+
## Tech stack
29+
30+
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).
31+
32+
## Architecture: things that require reading multiple files to understand
33+
34+
### Two user tables (ADR 0003)
35+
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.
36+
37+
### Multi-tenancy is enforced in three places
38+
1. Models include `ByOrganizationScope` (`app/models/concerns/by_organization_scope.rb`) which gives `.by_organization(casa_org)`.
39+
2. The `Organizational` controller concern (`app/controllers/concerns/organizational.rb`) provides `current_organization` from the signed-in user.
40+
3. Pundit policy scopes filter by org: `scope.by_organization(user.casa_org)`.
41+
42+
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.
43+
44+
### Authorization is policy-based (Pundit)
45+
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.
46+
47+
### Layering: where logic goes
48+
- **Controllers** stay thin. Standard pattern: `set_record` before_action, `authorize`, save/render with `:unprocessable_content` on failure.
49+
- **Service objects** (`app/services/`) — `ServiceName.new(args).perform`. Used for CSV exports, SMS reminders, complex multi-record operations.
50+
- **Decorators** (`app/decorators/`, Draper) — presentation logic. Access view helpers via `h.helper_method`. Called as `model.decorate`. Date formatting, conditional display strings, etc. belong here, NOT in models or ERB.
51+
- **ViewComponents** (`app/components/`) — reusable UI primitives (badges, modals, sidebar, dropdown menus, form bits).
52+
- **Parameter objects** (`app/values/`) — builder-pattern wrappers for non-trivial strong-params logic, e.g. `VolunteerParameters.new(params).with_password(pw).without_active`.
53+
- **Concerns** (`app/{models,controllers}/concerns/`) — shared behavior via `extend ActiveSupport::Concern`. Notable model concerns: `ByOrganizationScope`, `Roles`, `Api`, and a `CasaCase/` directory of concern modules used by the `CasaCase` model.
54+
55+
### Frontend
56+
Hotwire-first: Turbo for navigation/forms, Stimulus controllers in `app/javascript/controllers/`. There is an [in-progress migration](https://github.com/rubyforgood/casa/issues/5016) 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).
57+
58+
### Notable conventions
59+
- **Soft deletes** via Paranoia: `destroy` marks deleted, doesn't hard-delete. Be deliberate when reasoning about uniqueness or "where is this record."
60+
- **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.
61+
- **Soft-deletion-aware uniqueness**: `validate_uniqueness_of(...).scoped_to(:casa_org_id)` is the typical pattern — uniqueness is per-org.
62+
- **Enums** use prefix syntax: `enum :status, {active: 0, inactive: 1}, prefix: :status`.
63+
- **Post-deployment tasks** run via After Party (`bundle exec rake after_party:run`), invoked automatically by `bin/update` and on Heroku release.
64+
65+
### Testing conventions
66+
- **System tests are preferred** over controller tests (ADR 0006 — `app/controllers/concerns/users/` and `spec/controllers/` exist but are minimal). New UI behavior should land in `spec/system/`.
67+
- Factories use **traits** for variants: `create(:casa_case, :active)`. `bin/lint` runs `factory_bot:lint` to catch invalid factories.
68+
- Use `build` for unit tests; `create` only when persistence is required. `let` for lazy, `let!` when the record must exist before the example.
69+
- shoulda-matchers handles association/validation tests.
70+
- No `sleep` in tests — rely on Capybara waiting.
71+
- Flaky tests are disabled with `xit` + a tracking issue, never deleted.
72+
73+
## Style
74+
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.
75+
76+
## Where to look for more
77+
- `.github/instructions/ruby.instructions.md` and `.github/instructions/copilot-review.instructions.md` — the project's own review checklist; the source of truth for "what is a bug-shaped change here."
78+
- `doc/architecture-decisions/` — ADRs (especially 0002 no-UI-signup, 0003 two-user-tables, 0006 few-controller-tests, 0007 inline-email-CSS).
79+
- `doc/productsense.md` — product philosophy.
80+
- `db/schema.rb` — paste into [dbdiagram.io](https://dbdiagram.io/d) for an ERD.

0 commit comments

Comments
 (0)