Skip to content

Commit 1ea130e

Browse files
authored
Merge branch 'main' into banner
2 parents 6515dbb + e6c418e commit 1ea130e

297 files changed

Lines changed: 6529 additions & 7347 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/copilot-instructions.md

Lines changed: 181 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,194 @@
1-
# This project is a Ruby on Rails application.
1+
# This is a Ruby on Rails application.
2+
<!-- Keep code style rules in sync with CLAUDE.md -->
3+
4+
For project overview, tech stack, architecture reference (models, controllers, services, testing), and more, read `AGENTS.md`.
5+
6+
## Setup
7+
8+
Full setup (bundle, npm, database create/migrate/seed):
9+
```
10+
bin/setup
11+
```
12+
13+
If you just need frontend dependencies:
14+
```
15+
npm ci
16+
```
17+
18+
## AI Instruction Files
19+
20+
When the user says "AI files", "AI instructions", "tell AI to", or "remember to always", these are the files.
21+
If you notice the user repeatedly correcting the same pattern, suggest adding it to the AI files with a concrete proposal.
22+
23+
| File | Purpose |
24+
|---|---|
25+
| `CLAUDE.md` | Coding rules and conventions (this file) |
26+
| `AGENTS.md` | Architecture reference + project details |
27+
| `.github/copilot-instructions.md` | Coding rules for Copilot (duplicated from CLAUDE.md — keep in sync) |
28+
| `ai/` | Shell script shortcuts for common dev tasks |
29+
30+
## Related Files
31+
32+
When changing a model or controller, check whether these related files need updates:
33+
34+
| If you change... | Also check... |
35+
|---|---|
36+
| Model | Decorator, policy, factory, model spec |
37+
| Controller | Policy, request spec, routing spec, views |
38+
| View | System spec, Stimulus controller (if interactive) |
39+
| Service | Service spec |
40+
| Decorator | Decorator spec |
41+
| Mailer (add/remove) | Mailer spec, mailer preview (follow existing patterns) |
42+
| Add/remove model, concern, service, or gem | AGENTS.md |
43+
44+
## Code Style
245

3-
# Code style requirements:
446
- Use modern Ruby syntax
547
- Prefer early returns and guard clauses
648
- Avoid unnecessary and/or complex conditionals
7-
- Prefer enums and scopes over magic strings
49+
- Prefer constants and scopes over magic strings
850
- Use safe navigation (`&.`) where appropriate
951
- Use `presence` over blank checks
1052
- Use `Arel.sql` for raw SQL in order clauses
1153
- Avoid `update_all` unless explicitly intended
12-
- Prefer service objects under app/services
54+
- Prefer service objects under app/services/
1355
- Prefer POROs over concerns when possible
1456
- Use `after_commit` instead of `after_save` for side effects
1557

16-
# RuboCop (rubocop-rails-omakase)
58+
## RuboCop (rubocop-rails-omakase)
59+
1760
This project uses rubocop-rails-omakase. All code MUST follow these rules:
1861

19-
## Strings
20-
- Always use double quotes: `"foo"` not `'foo'`
21-
22-
## Spacing
23-
- Spaces inside array brackets: `[ a, b, c ]` not `[a, b, c]` (empty arrays: `[]`)
24-
- Spaces inside hash braces: `{ a: 1, b: 2 }` not `{a: 1}` (empty hashes: `{}`)
25-
- Spaces inside block braces: `foo { bar }` not `foo {bar}` (empty blocks: `foo { }`)
26-
- No spaces inside parens: `foo(bar)` not `foo( bar )`
27-
- No spaces inside reference brackets: `hash[:key]` not `hash[ :key ]`
28-
- Space before block braces: `foo { }` not `foo{ }`
29-
30-
## Commas
31-
- No trailing commas in arrays, hashes, or method arguments
32-
33-
## Indentation
34-
- 2-space indentation, no tabs
35-
- Consistent indentation at normal level — do NOT indent methods under `private`/`protected`
36-
- Align `end` with the variable in assignments
37-
- Align `when` with `end`, not with `case`
38-
39-
## Whitespace
40-
- No trailing whitespace on any line
41-
- No trailing blank lines at end of file
42-
- No empty lines inside class, module, method, or block bodies
43-
44-
## Syntax
45-
- Use `%w[]` and `%i[]` with square bracket delimiters (not parens)
46-
- Use modern hash syntax: `{ key: value }` not `{ :key => value }`
47-
- No redundant returns — omit `return` on last expression
48-
- Use `flat_map` instead of `.map { }.flatten`
49-
- No redundant `.to_s` inside string interpolation
50-
- Use `Foo.method` not `Foo::method` for method calls
51-
- No parentheses around conditions: `if foo` not `if (foo)`
52-
- No semicolons to separate statements
62+
### Strings
63+
- **Always use double quotes** for strings: `"foo"` not `'foo'`
64+
65+
### Spacing
66+
- **Spaces inside array brackets:** `[ a, b, c ]` not `[a, b, c]` (empty arrays: `[]`)
67+
- **Spaces inside hash braces:** `{ a: 1, b: 2 }` not `{a: 1}` (empty hashes: `{}`)
68+
- **Spaces inside block braces:** `foo { bar }` not `foo {bar}` (empty blocks: `foo { }`)
69+
- **No spaces inside parens:** `foo(bar)` not `foo( bar )`
70+
- **No spaces inside reference brackets:** `hash[:key]` not `hash[ :key ]`
71+
- **Space before block braces:** `foo { }` not `foo{ }`
72+
73+
### Commas
74+
- **No trailing commas** in arrays, hashes, or method arguments
75+
76+
### Indentation
77+
- **2-space indentation**, no tabs
78+
- **Consistent indentation** at normal level — do NOT indent methods under `private`/`protected`
79+
- **Align `end` with the variable** in assignments:
80+
```ruby
81+
result = if condition
82+
value
83+
end
84+
```
85+
- **Align `when` with `end`**, not with `case`
86+
87+
### Whitespace
88+
- **No trailing whitespace** on any line
89+
- **No trailing blank lines** at end of file
90+
- **No empty lines** inside class, module, method, or block bodies
91+
92+
### Syntax
93+
- **Use `%w[]` and `%i[]`** with square bracket delimiters (not parens)
94+
- **Use modern hash syntax:** `{ key: value }` not `{ :key => value }`
95+
- **No redundant returns** — omit `return` on last expression
96+
- **Use `flat_map`** instead of `.map { }.flatten`
97+
- **No redundant `.to_s`** inside string interpolation
98+
- **Use `Foo.method`** not `Foo::method` for method calls
99+
- **No parentheses around conditions:** `if foo` not `if (foo)`
100+
- **No semicolons** to separate statements
101+
102+
## Casing
103+
104+
- **Use sentence case** for UI labels, headings, and display text — not title case
105+
- "Age range" not "Age Range", "Art type" not "Art Type"
106+
- Use `.underscore.humanize` to convert PascalCase model/type names to sentence case (e.g., `"AgeRange".underscore.humanize``"Age range"`)
107+
- Avoid `.titleize` for user-facing labels — it produces title case
108+
- **Exception:** when a category type name prefixes a category name (e.g., "Age Range: 3-5"), use `.titleize` for the prefix
109+
110+
## HTML/ERB Formatting
111+
112+
### Tag Attributes
113+
- **Closing `>` on same line as last attribute** — do not put `>` on its own line
114+
- When attributes span multiple lines, keep the closing `>` with the last attribute
115+
- Example (GOOD):
116+
```erb
117+
<div class="relative z-10 w-full bg-white text-gray-800 py-2 px-4"
118+
id="dropdown">
119+
```
120+
- Example (BAD):
121+
```erb
122+
<div class="relative z-10 w-full bg-white text-gray-800 py-2 px-4"
123+
id="dropdown"
124+
>
125+
```
126+
127+
## JavaScript
128+
129+
- ES6+ syntax, ESM imports/exports, `const`/`let` (no `var`)
130+
- Use `const` for fixed values — not `SCREAMING_SNAKE_CASE` constants (e.g., `const styleId = "foo"` not `const STYLE_ID = "foo"`)
131+
- **Strongly prefer Stimulus** for JavaScript behavior — do not write raw/inline JS or jQuery
132+
- **Always use Tailwind CSS** utility classes for styling — do not write custom CSS unless absolutely necessary
133+
- **Prefer Font Awesome (free)** icons over inline SVGs — use `icon("fa-solid fa-foo")` helper. Inline SVGs are acceptable when a specific icon design is preferred.
134+
- Prefer Turbo for navigation and form submissions before reaching for Stimulus
135+
- Controller naming: `[name]_controller.js`
136+
- Keep controllers focused and small
137+
138+
### Stimulus Conventions
139+
140+
Follow the [Stimulus Handbook](https://stimulus.hotwired.dev/handbook/introduction) and reference docs. Key rules:
141+
142+
**Targets over querySelector** — declare `static targets = [...]` and use `data-[controller]-target` attributes in views. Never use `this.element.querySelector` or `document.getElementById` to find elements that could be targets. Exception: elements outside the controller's scope (e.g., in a parent view).
143+
144+
**Values API for state** — use `static values = { name: Type }` for any state that persists or drives UI. Do not store state in instance variables when a value would work. Use `[name]ValueChanged()` callbacks for reactive updates instead of manual syncing.
145+
146+
**Actions over manual listeners** — use `data-action` attributes instead of `addEventListener` in `connect()`. Omit the event when it's the default for the element (`click` for buttons/links, `input` for inputs/textareas, `submit` for forms, `change` for selects). Use `@window` or `@document` suffixes for global events when possible (e.g., `resize@window->controller#layout`). Use action options like `:prevent` and `:stop` instead of calling `event.preventDefault()` in methods.
147+
148+
**Classes API for CSS** — use `static classes = [...]` when CSS classes need to be configurable from HTML. For standard Tailwind utilities used internally (e.g., `"hidden"`), hardcoding is acceptable.
149+
150+
**Outlets for cross-controller communication** — use `static outlets = [...]` to reference other controllers instead of `document.getElementById` or custom events when the relationship is stable.
151+
152+
**Lifecycle discipline** — every listener, timer, or observer created in `connect()` must be cleaned up in `disconnect()`. Store bound handler references so they can be removed. Use `initialize()` for one-time setup (e.g., binding functions).
153+
154+
**Target lifecycle callbacks** — use `[name]TargetConnected(element)` and `[name]TargetDisconnected(element)` to respond to dynamically added/removed targets (e.g., cocoon nested fields, Turbo streams).
155+
156+
**Visibility** — toggle the `hidden` class via `classList.toggle("hidden", condition)` instead of setting `style.display`. Use `class="hidden"` in HTML for initial hidden state, not `style="display:none"`.
157+
158+
## Migrations
159+
160+
- Name migration files using **UTC timestamps** (e.g., `20260228143000`), not sequential numbers (e.g., `20260228000007`)
161+
- Multiple branches adding migrations on the same date will collide if they use sequential numbering
162+
163+
## Git
164+
165+
- Default branch is `main`
166+
- Commit messages should explain why, not what
167+
- CI runs via GitHub Actions (`.github/workflows/`)
168+
- **When rebasing onto main**, review incoming changes for their intent and flag any oversights — missing tests, incomplete migrations, broken assumptions, or conflicts between the two branches. Check both directions: schema/model changes on either branch that affect views, partials, or layouts on the other (e.g., main redesigned a table's CSS but your branch adds new columns to it, or vice versa)
169+
170+
## PRs
171+
172+
- **Push to a draft PR early** — push commits and create a draft PR (`gh pr create --draft`) as soon as work begins, rather than keeping changes in a local branch. Push on every commit.
173+
- After completing work, **mark the PR ready** using `gh pr ready`
174+
- Once the PR is created, **prepend the PR number to the branch name** (e.g., rename `maebeale/fix-login` to `maebeale/1234-fix-login`) using `git branch -m` and `git push origin -u` with the new name, then delete the old remote branch
175+
- Use `docs/pull_request_template.md` for PR description structure
176+
- Use bullet points, not paragraphs, when filling out each section
177+
- Description must explain why the change was made, not just what
178+
- Include screenshots for UI changes
179+
- **On every push**, update the PR title and content to reflect the current diff — preserve any existing images/screenshots in the description
180+
- **On every push**, update AI instruction files if the diff adds, removes, or renames anything tracked in AGENTS.md — specifically: Stimulus controllers, services, model/controller concerns, mailers, rake tasks, and directory file counts
181+
182+
## Quick Commands
183+
184+
See `ai/` directory for executable scripts:
185+
186+
| Command | What it does |
187+
|---|---|
188+
| `ai/test [args]` | Run RSpec |
189+
| `ai/lint` | Rubocop on all files |
190+
| `ai/lint --fix` | Auto-fix lint issues |
191+
| `ai/server` | Start dev services (web + vite) |
192+
| `ai/console` | Rails console |
193+
| `ai/routes -g pattern` | Search Rails routes |
194+
| `ai/db-migrate` | Run database migrations |

0 commit comments

Comments
 (0)