create-standards 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -0
- package/bin/create-standards.js +3 -0
- package/package.json +32 -0
- package/src/cli.js +355 -0
- package/src/discover.js +113 -0
- package/src/ide-config.js +57 -0
- package/src/install.js +77 -0
- package/standards/nodejs-express-standards/README.md +59 -0
- package/standards/nodejs-express-standards/antigravity/.agents/skills/nodejs-express-standards/SKILL.md +40 -0
- package/standards/nodejs-express-standards/antigravity/.agents/skills/nodejs-express-standards/references/01-project-structure.md +33 -0
- package/standards/nodejs-express-standards/antigravity/.agents/skills/nodejs-express-standards/references/02-api-design-standards.md +41 -0
- package/standards/nodejs-express-standards/antigravity/.agents/skills/nodejs-express-standards/references/03-error-handling-validation.md +27 -0
- package/standards/nodejs-express-standards/antigravity/.agents/skills/nodejs-express-standards/references/04-security-standards.md +17 -0
- package/standards/nodejs-express-standards/antigravity/.agents/skills/nodejs-express-standards/references/05-database-model-standards.md +10 -0
- package/standards/nodejs-express-standards/antigravity/.agents/skills/nodejs-express-standards/references/06-testing-standards.md +10 -0
- package/standards/nodejs-express-standards/antigravity/.agents/skills/nodejs-express-standards/references/07-new-component-checklist.md +16 -0
- package/standards/nodejs-express-standards/antigravity/.agents/skills/nodejs-express-standards/references/08-code-style-general.md +9 -0
- package/standards/nodejs-express-standards/claude-code/.claude/skills/nodejs-express-standards/SKILL.md +40 -0
- package/standards/nodejs-express-standards/claude-code/.claude/skills/nodejs-express-standards/references/01-project-structure.md +33 -0
- package/standards/nodejs-express-standards/claude-code/.claude/skills/nodejs-express-standards/references/02-api-design-standards.md +41 -0
- package/standards/nodejs-express-standards/claude-code/.claude/skills/nodejs-express-standards/references/03-error-handling-validation.md +27 -0
- package/standards/nodejs-express-standards/claude-code/.claude/skills/nodejs-express-standards/references/04-security-standards.md +17 -0
- package/standards/nodejs-express-standards/claude-code/.claude/skills/nodejs-express-standards/references/05-database-model-standards.md +10 -0
- package/standards/nodejs-express-standards/claude-code/.claude/skills/nodejs-express-standards/references/06-testing-standards.md +10 -0
- package/standards/nodejs-express-standards/claude-code/.claude/skills/nodejs-express-standards/references/07-new-component-checklist.md +16 -0
- package/standards/nodejs-express-standards/claude-code/.claude/skills/nodejs-express-standards/references/08-code-style-general.md +9 -0
- package/standards/nodejs-express-standards/cursor/.cursor/rules/00-index.mdc +25 -0
- package/standards/nodejs-express-standards/cursor/.cursor/rules/01-project-structure.mdc +38 -0
- package/standards/nodejs-express-standards/cursor/.cursor/rules/02-api-design.mdc +46 -0
- package/standards/nodejs-express-standards/cursor/.cursor/rules/03-error-handling-validation.mdc +32 -0
- package/standards/nodejs-express-standards/cursor/.cursor/rules/04-security.mdc +22 -0
- package/standards/nodejs-express-standards/cursor/.cursor/rules/05-database-models.mdc +15 -0
- package/standards/nodejs-express-standards/cursor/.cursor/rules/06-testing.mdc +15 -0
- package/standards/nodejs-express-standards/cursor/.cursor/rules/07-new-component-checklist.mdc +21 -0
- package/standards/nodejs-express-standards/cursor/.cursor/rules/08-code-style.mdc +14 -0
- package/standards/nodejs-express-standards/shared-reference/01-project-structure.md +33 -0
- package/standards/nodejs-express-standards/shared-reference/02-api-design-standards.md +41 -0
- package/standards/nodejs-express-standards/shared-reference/03-error-handling-validation.md +27 -0
- package/standards/nodejs-express-standards/shared-reference/04-security-standards.md +17 -0
- package/standards/nodejs-express-standards/shared-reference/05-database-model-standards.md +10 -0
- package/standards/nodejs-express-standards/shared-reference/06-testing-standards.md +10 -0
- package/standards/nodejs-express-standards/shared-reference/07-new-component-checklist.md +16 -0
- package/standards/nodejs-express-standards/shared-reference/08-code-style-general.md +8 -0
- package/standards/rails-standards/README.md +86 -0
- package/standards/rails-standards/antigravity/.agents/skills/rails-standards/SKILL.md +66 -0
- package/standards/rails-standards/antigravity/.agents/skills/rails-standards/references/00-quick-scaffold.md +83 -0
- package/standards/rails-standards/antigravity/.agents/skills/rails-standards/references/01-project-structure.md +44 -0
- package/standards/rails-standards/antigravity/.agents/skills/rails-standards/references/02-api-design-standards.md +46 -0
- package/standards/rails-standards/antigravity/.agents/skills/rails-standards/references/03-error-handling-validation.md +35 -0
- package/standards/rails-standards/antigravity/.agents/skills/rails-standards/references/04-security-standards.md +32 -0
- package/standards/rails-standards/antigravity/.agents/skills/rails-standards/references/05-database-model-standards.md +25 -0
- package/standards/rails-standards/antigravity/.agents/skills/rails-standards/references/06-testing-standards.md +26 -0
- package/standards/rails-standards/antigravity/.agents/skills/rails-standards/references/07-new-component-checklist.md +26 -0
- package/standards/rails-standards/antigravity/.agents/skills/rails-standards/references/08-code-style-general.md +47 -0
- package/standards/rails-standards/claude-code/.claude/skills/rails-standards/SKILL.md +66 -0
- package/standards/rails-standards/claude-code/.claude/skills/rails-standards/references/00-quick-scaffold.md +83 -0
- package/standards/rails-standards/claude-code/.claude/skills/rails-standards/references/01-project-structure.md +44 -0
- package/standards/rails-standards/claude-code/.claude/skills/rails-standards/references/02-api-design-standards.md +46 -0
- package/standards/rails-standards/claude-code/.claude/skills/rails-standards/references/03-error-handling-validation.md +35 -0
- package/standards/rails-standards/claude-code/.claude/skills/rails-standards/references/04-security-standards.md +32 -0
- package/standards/rails-standards/claude-code/.claude/skills/rails-standards/references/05-database-model-standards.md +25 -0
- package/standards/rails-standards/claude-code/.claude/skills/rails-standards/references/06-testing-standards.md +26 -0
- package/standards/rails-standards/claude-code/.claude/skills/rails-standards/references/07-new-component-checklist.md +26 -0
- package/standards/rails-standards/claude-code/.claude/skills/rails-standards/references/08-code-style-general.md +47 -0
- package/standards/rails-standards/cursor/.cursor/rules/00-always-on.mdc +26 -0
- package/standards/rails-standards/cursor/.cursor/rules/01-project-structure.mdc +49 -0
- package/standards/rails-standards/cursor/.cursor/rules/02-api-design.mdc +51 -0
- package/standards/rails-standards/cursor/.cursor/rules/03-error-handling-validation.mdc +40 -0
- package/standards/rails-standards/cursor/.cursor/rules/04-security.mdc +37 -0
- package/standards/rails-standards/cursor/.cursor/rules/05-database-models.mdc +30 -0
- package/standards/rails-standards/cursor/.cursor/rules/06-testing.mdc +31 -0
- package/standards/rails-standards/cursor/.cursor/rules/07-new-component-checklist.mdc +31 -0
- package/standards/rails-standards/cursor/.cursor/rules/08-code-style.mdc +52 -0
- package/standards/rails-standards/cursor/.cursor/rules/09-quick-scaffold.mdc +88 -0
- package/standards/rails-standards/shared-reference/00-quick-scaffold.md +83 -0
- package/standards/rails-standards/shared-reference/01-project-structure.md +44 -0
- package/standards/rails-standards/shared-reference/02-api-design-standards.md +46 -0
- package/standards/rails-standards/shared-reference/03-error-handling-validation.md +35 -0
- package/standards/rails-standards/shared-reference/04-security-standards.md +32 -0
- package/standards/rails-standards/shared-reference/05-database-model-standards.md +25 -0
- package/standards/rails-standards/shared-reference/06-testing-standards.md +26 -0
- package/standards/rails-standards/shared-reference/07-new-component-checklist.md +26 -0
- package/standards/rails-standards/shared-reference/08-code-style-general.md +47 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Project Structure Standards (Ruby on Rails)
|
|
2
|
+
|
|
3
|
+
## Detect before you structure anything
|
|
4
|
+
|
|
5
|
+
This codebase already has a structure — the job is to recognize it and extend it consistently,
|
|
6
|
+
not to impose a textbook layout on top of it. Before adding a new folder or a new kind of class:
|
|
7
|
+
|
|
8
|
+
1. Look at `app/` — which of these already exist: `services/`, `queries/`, `forms/`, `policies/`,
|
|
9
|
+
`serializers/`, `decorators/`? Whatever's there is the convention for this codebase.
|
|
10
|
+
2. If a folder you'd normally reach for (e.g. `services/`) **doesn't exist yet** and the task is
|
|
11
|
+
small, that's a signal — either this codebase keeps that logic somewhere else (check a model or
|
|
12
|
+
controller for where similar logic lives), or it genuinely doesn't have this layer yet. If
|
|
13
|
+
introducing a new top-level pattern for the first time, say so plainly rather than silently
|
|
14
|
+
creating it, and ask if it's a big structural addition.
|
|
15
|
+
3. Don't scan the entire `app/` tree to figure this out — `ls app/` plus opening one or two
|
|
16
|
+
existing files near where you're adding code is enough.
|
|
17
|
+
|
|
18
|
+
## Common Rails layers, when they're already in use
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
app/
|
|
22
|
+
controllers/ # thin: auth -> authorize -> delegate -> render
|
|
23
|
+
models/ # persistence + validations + associations
|
|
24
|
+
services/ # multi-step business logic, if this codebase uses the pattern
|
|
25
|
+
queries/ # complex/reusable query objects, if in use
|
|
26
|
+
forms/ # form objects for multi-model or non-persisted input, if in use
|
|
27
|
+
policies/ # authorization objects, matching whichever auth gem is detected
|
|
28
|
+
serializers/ # JSON shape, matching whichever serialization approach is detected
|
|
29
|
+
jobs/ # background jobs, matching whichever adapter/gem is detected
|
|
30
|
+
lib/ # framework-agnostic code with no Rails dependency
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Rules that hold regardless of which layers this codebase uses
|
|
34
|
+
|
|
35
|
+
- **Controllers stay thin** no matter what: auth → authorize (if the app has an authorization
|
|
36
|
+
layer) → delegate to wherever business logic already lives → render. Multi-step business logic
|
|
37
|
+
doesn't belong inline in a controller action, whatever it's called in this codebase.
|
|
38
|
+
- **Don't let one model absorb unrelated logic.** If a model file is already large and you're
|
|
39
|
+
about to add another unrelated method, look at how the codebase has handled that split before
|
|
40
|
+
(a concern? a PORO? a service?) and match it, rather than inventing a new extraction pattern.
|
|
41
|
+
- **Concerns are for genuinely shared behavior** (used by 2+ classes already, or clearly about to
|
|
42
|
+
be). A concern used by exactly one class is unnecessary indirection — inline it instead.
|
|
43
|
+
- **Namespace and version the API** the way the existing routes already do. If this is the first
|
|
44
|
+
API endpoint in the app, ask whether versioning is wanted before picking a scheme.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# REST API Design Standards (Rails)
|
|
2
|
+
|
|
3
|
+
## Follow the existing convention first
|
|
4
|
+
|
|
5
|
+
If the app already has API endpoints, open one and copy its shape exactly — route style,
|
|
6
|
+
namespacing, response envelope, status codes, error format. Consistency with the rest of the app
|
|
7
|
+
matters more than matching a generic "best practice" that conflicts with it. Only fall back to the
|
|
8
|
+
generic guidance below if this is genuinely the first endpoint of its kind, and mention that
|
|
9
|
+
you're establishing a new convention.
|
|
10
|
+
|
|
11
|
+
## Routing
|
|
12
|
+
|
|
13
|
+
- Use `resources`/`resource` with explicit `only:`/`except:` — don't leave all 7 RESTful actions
|
|
14
|
+
open if only a few are used, whether or not the rest of the app does this consistently.
|
|
15
|
+
- Nest one level max: `resources :orders do resources :items, only: [:index, :create] end`.
|
|
16
|
+
- Namespace + version if the app already does (`Api::V1::...`); if there's no existing API and
|
|
17
|
+
this is the first one, ask whether versioning is wanted before picking a scheme.
|
|
18
|
+
|
|
19
|
+
## Controllers
|
|
20
|
+
|
|
21
|
+
- Auth → authorize (using whatever authorization approach is already in the codebase — see
|
|
22
|
+
`04-security-standards.md` for how to detect it) → delegate to wherever business logic already
|
|
23
|
+
lives → render. No inline multi-step `ActiveRecord` chains in the controller action itself.
|
|
24
|
+
- Strong parameters are mandatory on every write action regardless of anything else detected.
|
|
25
|
+
|
|
26
|
+
## Status codes & responses
|
|
27
|
+
|
|
28
|
+
- `200`/`201`/`204` success, `400`/`422` validation, `401`/`403` auth, `404` not found, `429` rate
|
|
29
|
+
limited, `500` unhandled — these are stable regardless of gem choices.
|
|
30
|
+
- **Response shape**: detect what the app already returns (check an existing controller/serializer).
|
|
31
|
+
If it already uses Blueprinter, `ActiveModel::Serializer`, `jsonapi-rb`, Jbuilder, or plain
|
|
32
|
+
`as_json`/`render json:` with a hand-built hash — match that, don't introduce a second approach.
|
|
33
|
+
If there's genuinely no existing pattern, ask which the team prefers before picking one; don't
|
|
34
|
+
assume a specific serializer gem.
|
|
35
|
+
- Never call `.to_json` on a raw ActiveRecord object/relation regardless of which serialization
|
|
36
|
+
approach is chosen — that leaks DB columns either way.
|
|
37
|
+
|
|
38
|
+
## Other requirements
|
|
39
|
+
|
|
40
|
+
- **Pagination**: match the existing approach if list endpoints already paginate (Kaminari, Pagy,
|
|
41
|
+
will_paginate, manual). If there's no existing list endpoint to reference, ask before adding a
|
|
42
|
+
pagination gem — a small internal API may not need one.
|
|
43
|
+
- **Filtering/sorting** via an explicit allow-listed param set — never pass raw `params` into
|
|
44
|
+
`.where`/`.order`, independent of which query pattern the app uses.
|
|
45
|
+
- **API docs**: match whatever the app already uses (rswag/OpenAPI annotations, a hand-maintained
|
|
46
|
+
file, or none). If none exists, flag that rather than silently skipping documentation.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Error Handling & Validation Standards (Rails)
|
|
2
|
+
|
|
3
|
+
## Detect the existing pattern
|
|
4
|
+
|
|
5
|
+
Check `ApplicationController` (or the relevant base controller) for existing `rescue_from`
|
|
6
|
+
declarations, and check one or two model/service files for how validation failures are currently
|
|
7
|
+
surfaced. Match that pattern. Only introduce a new centralized `rescue_from` chain if none exists
|
|
8
|
+
— and note that you're adding a new cross-cutting pattern, since it affects every controller.
|
|
9
|
+
|
|
10
|
+
## If there's an existing pattern, extend it
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
# Example shape — only use this if the codebase doesn't already have something similar
|
|
14
|
+
class Api::BaseController < ActionController::API
|
|
15
|
+
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
|
|
16
|
+
rescue_from ActiveRecord::RecordInvalid, with: :render_unprocessable
|
|
17
|
+
end
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- If custom domain error classes already exist (`ApplicationError` or similar), follow that
|
|
21
|
+
hierarchy for new errors. If none exist yet, a base `ApplicationError < StandardError` with
|
|
22
|
+
domain subclasses is a reasonable default to introduce — but call out that it's new.
|
|
23
|
+
|
|
24
|
+
## Rules that hold regardless of the existing pattern
|
|
25
|
+
|
|
26
|
+
- **No bare `rescue => e` that swallows errors silently.** If broad rescue is unavoidable (e.g.
|
|
27
|
+
around a third-party call), re-raise a typed error or log with full context.
|
|
28
|
+
- Validation belongs on the model (persistence-level rules) or a form object (cross-model/
|
|
29
|
+
non-persisted input) — match whichever the codebase already uses for similar cases, rather than
|
|
30
|
+
hand-validating raw `params` in the controller.
|
|
31
|
+
- `.save!`/`.update!` inside service-style objects so failures raise and get caught centrally,
|
|
32
|
+
matching whatever the surrounding code already does with bang vs non-bang methods.
|
|
33
|
+
- Validation error responses return `422` with per-attribute detail
|
|
34
|
+
(`record.errors.full_messages` or similar) — the specific shape should match the response
|
|
35
|
+
envelope already established (see `02-api-design-standards.md`).
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Security Standards (Rails)
|
|
2
|
+
|
|
3
|
+
## Detect the existing security stack before adding anything
|
|
4
|
+
|
|
5
|
+
- **Authorization**: check `Gemfile` for `pundit` or `cancancan`, and check an existing
|
|
6
|
+
controller for `authorize`/`can?`/`load_and_authorize_resource`. Use whichever is already
|
|
7
|
+
present. **If neither is present and the task needs authorization, ask which the team wants**
|
|
8
|
+
(Pundit and CanCanCan solve the same problem differently — don't default to one silently).
|
|
9
|
+
- **Auth (login)**: check for `devise`, `has_secure_password`, or a custom token scheme already in
|
|
10
|
+
the app, and match it. Don't introduce a second auth mechanism alongside an existing one.
|
|
11
|
+
- **Rate limiting**: check for `rack-attack` in `Gemfile`/`config/initializers`. If absent and the
|
|
12
|
+
task is a new public-facing endpoint, flag that rate limiting isn't set up rather than silently
|
|
13
|
+
skipping it — whether to add it is a call worth surfacing, not assuming.
|
|
14
|
+
|
|
15
|
+
## Non-negotiable regardless of which specific gems are in use
|
|
16
|
+
|
|
17
|
+
- **Strong parameters** on every controller write action — no `params.permit!`, no mass-assigning
|
|
18
|
+
a raw hash to `.new`/`.update`.
|
|
19
|
+
- **Secrets**: Rails encrypted credentials or ENV vars — never commit `config/master.key`, never
|
|
20
|
+
hardcode API keys, regardless of how the rest of the app manages config.
|
|
21
|
+
- **SQL injection**: parameterized queries / ActiveRecord query methods always — never
|
|
22
|
+
string-interpolate user input into `.where("... #{params[...]}")` or raw `execute`.
|
|
23
|
+
- **Data exposure**: whatever the serialization layer is (see `02-api-design-standards.md`), it
|
|
24
|
+
must explicitly control which columns are exposed — never render a model with sensitive columns
|
|
25
|
+
(password_digest, tokens) without an explicit allow-list.
|
|
26
|
+
- **CSRF**: `protect_from_forgery` for cookie-session-based controllers; token-authenticated JSON
|
|
27
|
+
APIs (`ActionController::API`) rely on the auth token instead — know which mode the controller
|
|
28
|
+
you're editing is actually in before assuming either way.
|
|
29
|
+
- **Static analysis**: if `brakeman` is already in the project's CI, keep it passing on new code.
|
|
30
|
+
If it isn't set up, that's worth flagging on a security-sensitive change, not silently skipping.
|
|
31
|
+
- HTTPS/secure cookies: match `config/environments/production.rb` if it already sets
|
|
32
|
+
`force_ssl`/cookie flags; don't change security config as a side effect of an unrelated task.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Database & Model Standards (Rails)
|
|
2
|
+
|
|
3
|
+
- **Migrations only** — never hand-edit `schema.rb`/`structure.sql` or run destructive changes
|
|
4
|
+
directly against a shared DB, regardless of project size. Every migration reversible where
|
|
5
|
+
possible.
|
|
6
|
+
- **DB-level constraints**: match how strict the existing schema already is. If existing tables
|
|
7
|
+
consistently use `null: false`/unique indexes/FK constraints, keep that standard for new tables.
|
|
8
|
+
If the existing schema is looser, still add constraints for genuinely required/unique fields —
|
|
9
|
+
this one isn't a "match the existing laxness" case, since it's a correctness/data-integrity
|
|
10
|
+
issue, not a style choice.
|
|
11
|
+
- **Index every foreign key** and every column used in a frequent `WHERE`/`ORDER BY`/`JOIN`.
|
|
12
|
+
- **N+1 prevention**: use `includes`/`preload`/`eager_load` for any association accessed in a loop
|
|
13
|
+
or in the serialization layer. Check whether `bullet` is already in the Gemfile — if so, its
|
|
14
|
+
warnings should be treated as real; if not, this is still worth watching for regardless.
|
|
15
|
+
- **Callbacks**: check how the existing models handle side effects (email sending, external API
|
|
16
|
+
calls) — if they're already in callbacks, that's the established (if not ideal) pattern; don't
|
|
17
|
+
silently "fix" that as part of an unrelated task. For genuinely new logic, prefer keeping side
|
|
18
|
+
effects out of callbacks and explicit in a service/controller, and mention the choice.
|
|
19
|
+
- **Soft delete**: check for `discard`, `paranoia`, or a manual `deleted_at` column already in use.
|
|
20
|
+
If none exists and the task needs "deletion," ask whether soft or hard delete is wanted rather
|
|
21
|
+
than assuming — this has real product/compliance implications.
|
|
22
|
+
- **Transactions** (`ActiveRecord::Base.transaction`) wrap any operation writing to more than one
|
|
23
|
+
table, independent of any other convention question.
|
|
24
|
+
- Prefer `find_each`/`in_batches` over `.each` on large relations to avoid loading an entire table
|
|
25
|
+
into memory.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Testing Standards (Rails)
|
|
2
|
+
|
|
3
|
+
## Detect the test framework and match it exactly
|
|
4
|
+
|
|
5
|
+
Check for `spec/` (RSpec) vs `test/` (Minitest) — usually unambiguous. If somehow both exist,
|
|
6
|
+
follow whichever the file you're adding tests near already uses; don't introduce the other
|
|
7
|
+
framework into a directory that's consistently using one.
|
|
8
|
+
|
|
9
|
+
- Match the existing fixture strategy (FactoryBot vs fixtures) — don't introduce FactoryBot into a
|
|
10
|
+
fixtures-based test suite (or vice versa) without flagging it as a bigger change.
|
|
11
|
+
- Match whether the app uses request specs, controller specs, or Minitest functional/integration
|
|
12
|
+
tests for controller-level coverage — follow the existing style for consistency, even if a newer
|
|
13
|
+
convention exists (e.g. request specs over controller specs), unless asked to modernize it.
|
|
14
|
+
|
|
15
|
+
## Rules that hold regardless of framework
|
|
16
|
+
|
|
17
|
+
- New model logic (validations, scopes, non-trivial methods) gets a corresponding test — don't
|
|
18
|
+
re-test what Rails/ActiveRecord already guarantees.
|
|
19
|
+
- New endpoints get at least: one happy-path test, one validation/error-path test, one
|
|
20
|
+
auth-failure test if the action is protected.
|
|
21
|
+
- New service/business-logic objects get their own test file covering the success path and each
|
|
22
|
+
distinct failure mode.
|
|
23
|
+
- Mock/stub external services (payment gateways, third-party APIs) — tests must not hit the real
|
|
24
|
+
network, regardless of the mocking library already in use (`webmock`, `vcr`, or hand-rolled).
|
|
25
|
+
- System/browser-level tests (Capybara) are for critical user flows only — don't add one for every
|
|
26
|
+
CRUD screen; match how sparingly (or not) the existing suite already uses them.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Checklist: Before Creating Any New Model, Controller, or Service (Rails)
|
|
2
|
+
|
|
3
|
+
1. **Look at the codebase first** — open one existing example of the thing you're about to create
|
|
4
|
+
(a similar controller/model/service) and mirror its structure, naming, and error-handling
|
|
5
|
+
shape. Don't start from a generic template if a real example already exists to copy from.
|
|
6
|
+
2. **Confirm which layer this belongs in**, based on what this codebase actually has (model,
|
|
7
|
+
service, query object, form object) — don't introduce a layer the codebase doesn't already use
|
|
8
|
+
without flagging it as a new pattern.
|
|
9
|
+
3. **Check for undetected architectural choices** before writing code that depends on one:
|
|
10
|
+
authorization library, serialization approach, pagination, background jobs, soft-delete
|
|
11
|
+
strategy. If genuinely undetected and it matters for this task, ask — see
|
|
12
|
+
`00-quick-scaffold.md` for the specific list.
|
|
13
|
+
4. **Write the migration** (with appropriate DB-level constraints and indexes) before the model
|
|
14
|
+
code that depends on it.
|
|
15
|
+
5. **Strong parameters + validation** before writing the controller action body.
|
|
16
|
+
6. **Authorization**: add/update whatever authorization mechanism the codebase already uses; if
|
|
17
|
+
none exists and this action needs to be protected, ask before picking a library.
|
|
18
|
+
7. **Error handling**: match the existing error-handling pattern; don't invent a new one for this
|
|
19
|
+
feature alone.
|
|
20
|
+
8. **Write tests** matching the existing framework/style: happy-path + at least one failure path.
|
|
21
|
+
9. **Update API docs** if the codebase already maintains them.
|
|
22
|
+
10. **Run whatever lint/static-analysis tools are already configured** — no new offenses.
|
|
23
|
+
11. **No secrets, no hardcoded config.**
|
|
24
|
+
12. **Minimal diff**: touch only what this task requires — see `08-code-style-general.md` for the
|
|
25
|
+
restraint principle. Don't refactor unrelated code or add scaffolding for hypothetical future
|
|
26
|
+
needs in the same change.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# General Code Style Standards (Rails)
|
|
2
|
+
|
|
3
|
+
## Match the codebase, don't impose a preference
|
|
4
|
+
|
|
5
|
+
Before writing new code, look at one or two nearby existing files and match their formatting,
|
|
6
|
+
naming conventions, and idioms — even where a "more standard" alternative exists. A style
|
|
7
|
+
disagreement with the existing codebase is a conversation to have with the team, not something to
|
|
8
|
+
silently fix inside an unrelated task.
|
|
9
|
+
|
|
10
|
+
- Check whether Rubocop (or another linter) is already configured — if so, its config IS the style
|
|
11
|
+
guide; follow it rather than a generic external standard. If none is configured, standard Ruby
|
|
12
|
+
conventions (2-space indent, snake_case methods/variables, CamelCase classes) are a reasonable
|
|
13
|
+
default, and it's worth flagging that no linter is set up rather than assuming one exists.
|
|
14
|
+
- If a genuinely new project has no tooling yet: for a real greenfield build, set up
|
|
15
|
+
Rubocop as part of initial scaffolding; for a quick prototype or when explicitly told not to
|
|
16
|
+
bother, don't force tooling — just say plainly it was skipped.
|
|
17
|
+
- No business logic in views/templates.
|
|
18
|
+
- Structured logging matching whatever the app already uses (`Rails.logger`, `semantic_logger`,
|
|
19
|
+
etc.) — no bare `puts`/`p` left in committed code regardless.
|
|
20
|
+
- Commit message style: match the existing git history's convention (Conventional Commits or
|
|
21
|
+
otherwise) rather than assuming one.
|
|
22
|
+
|
|
23
|
+
## Restraint — make the smallest correct change
|
|
24
|
+
|
|
25
|
+
- **For a small/targeted change, write a small/targeted diff.** Fixing one validation, one bug, or
|
|
26
|
+
one endpoint should touch the lines that fix it — not the surrounding file, not "while I'm here"
|
|
27
|
+
refactors, not unrelated files.
|
|
28
|
+
- **Don't pre-build for hypothetical future requirements.** No speculative service objects, config
|
|
29
|
+
options, or abstraction layers for a feature that wasn't asked for (YAGNI) — extract an
|
|
30
|
+
abstraction only once a real second use case exists.
|
|
31
|
+
- **Don't scaffold more than was asked.** "Add a `cancel` action to `OrdersController`" means one
|
|
32
|
+
action, not a re-generation of the whole controller, a new serializer, and a new policy file
|
|
33
|
+
unless the task actually needs them.
|
|
34
|
+
- **Prefer editing existing code over generating new files** when a small addition to an existing
|
|
35
|
+
service/model/controller does the job.
|
|
36
|
+
|
|
37
|
+
## Comments
|
|
38
|
+
|
|
39
|
+
- Comments explain **why**, not **what** — a comment repeating what the code already says is
|
|
40
|
+
noise (`# increment counter` above `counter += 1` is not acceptable).
|
|
41
|
+
- Keep comments **short** — one line, plain language. If a comment needs a paragraph, the method
|
|
42
|
+
is probably too complex and should be simplified or split instead.
|
|
43
|
+
- Only comment: non-obvious business rules, workarounds for a specific bug/gem quirk (link the
|
|
44
|
+
issue/ticket if one exists), or intentional deviations from the codebase's usual pattern.
|
|
45
|
+
- Delete commented-out code — that's what git history is for.
|
|
46
|
+
- Don't add a comment/docstring to every method by default — match the existing codebase's comment
|
|
47
|
+
density; if it's sparse, stay sparse; comment the exception, not the rule.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rails-standards
|
|
3
|
+
description: Standards for Ruby on Rails backend work that detect and match THIS codebase's existing conventions (auth library, serialization approach, project structure, error handling, tests) rather than assuming a generic stack, and ask a clarifying question when a real architectural choice can't be detected. Use this skill whenever creating, editing, or reviewing any Rails model, controller, service, migration, or API endpoint, or before scaffolding any new module/component, even if the user doesn't explicitly ask for "standards" or "best practices."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Rails Standards
|
|
7
|
+
|
|
8
|
+
This skill's job is to make new code look like it was written by the same person who wrote the
|
|
9
|
+
rest of the codebase — not to impose a generic "senior architect" stack on top of it. Detecting
|
|
10
|
+
and matching existing conventions is the primary behavior; the reference files below are fallback
|
|
11
|
+
guidance for when no existing convention is detectable.
|
|
12
|
+
|
|
13
|
+
## Detect → match → ask (do this before writing code, every time)
|
|
14
|
+
|
|
15
|
+
1. **Graceful search, not a full scan.** Read `Gemfile`/`Gemfile.lock` once for the relevant gem
|
|
16
|
+
category. That's it for step 1 — don't grep the whole repo.
|
|
17
|
+
2. Open **one** existing file of the same kind near where you're adding code (one controller, one
|
|
18
|
+
model, one spec) and mirror its structure, naming, error handling, and comment density.
|
|
19
|
+
3. If Gemfile + the example agree, use that — it's detected, not assumed.
|
|
20
|
+
4. **If nothing exists to detect from and the choice materially affects the code** (authorization
|
|
21
|
+
library, serialization approach, pagination, background jobs, soft-delete strategy, test
|
|
22
|
+
framework, rate limiting, API versioning) — **ask one question with the common options** rather
|
|
23
|
+
than silently defaulting to a specific gem.
|
|
24
|
+
5. Never skip step 2 when the codebase is non-empty — overlooking existing code and writing from a
|
|
25
|
+
generic template is the failure mode this skill exists to prevent.
|
|
26
|
+
|
|
27
|
+
## Token discipline — read this before opening any reference file
|
|
28
|
+
|
|
29
|
+
- **New model / controller / service / endpoint** → after doing the detect-match-ask steps above,
|
|
30
|
+
read **only** `references/00-quick-scaffold.md` for anything not resolved by what you found.
|
|
31
|
+
- **Deep dive on one specific concern** → read the one matching full reference file, not the
|
|
32
|
+
others.
|
|
33
|
+
- **Full audit / reviewing an existing large PR** → the only case that justifies reading
|
|
34
|
+
multiple/all reference files.
|
|
35
|
+
|
|
36
|
+
| Task involves... | Read |
|
|
37
|
+
|--------------------------------------------|------------------------------------------------|
|
|
38
|
+
| Scaffolding anything new (default case) | `references/00-quick-scaffold.md` **(start here)** |
|
|
39
|
+
| Deep dive: overall app layout | `references/01-project-structure.md` |
|
|
40
|
+
| Deep dive: routes/controllers/serializers | `references/02-api-design-standards.md` |
|
|
41
|
+
| Deep dive: error handling/validation | `references/03-error-handling-validation.md` |
|
|
42
|
+
| Deep dive: auth/security | `references/04-security-standards.md` |
|
|
43
|
+
| Deep dive: models/migrations/query objects | `references/05-database-model-standards.md` |
|
|
44
|
+
| Deep dive: tests | `references/06-testing-standards.md` |
|
|
45
|
+
| Full pre-scaffold checklist (verbose form) | `references/07-new-component-checklist.md` |
|
|
46
|
+
| Deep dive: style/restraint/comments | `references/08-code-style-general.md` |
|
|
47
|
+
|
|
48
|
+
## Core non-negotiables (apply even without reading further)
|
|
49
|
+
|
|
50
|
+
- Detect and match existing conventions before applying any generic default; ask rather than
|
|
51
|
+
assume when a real architectural choice isn't detectable.
|
|
52
|
+
- No business logic in controllers or views; strong params on every write action.
|
|
53
|
+
- Every error flows through whatever centralized error-handling pattern the codebase already has
|
|
54
|
+
(or a `rescue_from` chain if none exists — flagged as new).
|
|
55
|
+
- **Restraint, always**: make the smallest correct change for the task asked. No speculative
|
|
56
|
+
abstractions, no unrelated refactors, no scaffolding beyond what's needed. Comments are short,
|
|
57
|
+
explain *why* not *what*, and only appear where the code isn't already self-evident.
|
|
58
|
+
|
|
59
|
+
## Workflow for a new feature (token-efficient path)
|
|
60
|
+
|
|
61
|
+
1. Do the detect → match → ask steps above.
|
|
62
|
+
2. Read `references/00-quick-scaffold.md` for anything the detection step didn't resolve.
|
|
63
|
+
3. Only pull in a full reference file if the quick-scaffold section on that topic isn't detailed
|
|
64
|
+
enough, or the task is a deep dive on one area.
|
|
65
|
+
4. Write the smallest diff that satisfies the task; run whatever lint/static-analysis tools are
|
|
66
|
+
already configured before considering it done.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Quick Scaffold Cheat Sheet — New Model / Controller / Service (Rails)
|
|
2
|
+
|
|
3
|
+
Read this file first for "create/add a new X" tasks — it's the condensed version of the full pack.
|
|
4
|
+
|
|
5
|
+
## 0. Understand before you write (always — this is the point of this skill)
|
|
6
|
+
|
|
7
|
+
This skill exists to match how **this specific codebase** already writes code, not to impose a
|
|
8
|
+
generic opinion or assume a from-scratch build. Before writing anything:
|
|
9
|
+
|
|
10
|
+
1. **Graceful search, not a full scan.** Read `Gemfile`/`Gemfile.lock` once (one file) for the
|
|
11
|
+
relevant gem category (auth, serialization, pagination, jobs, testing).
|
|
12
|
+
2. Open **one** existing file of the same kind, near where you're adding code (one existing
|
|
13
|
+
controller, one existing model, one existing spec). Mimic its structure, naming, error-handling
|
|
14
|
+
shape, and comment density. One representative example is enough — don't scan the whole `app/`
|
|
15
|
+
tree.
|
|
16
|
+
3. If Gemfile + the example file agree on an approach, **use it, don't ask** — that's a detected
|
|
17
|
+
convention, not a guess.
|
|
18
|
+
4. **If there's no existing example and the choice materially affects the code, ask one question
|
|
19
|
+
with the common options** — don't silently default to "the popular gem" as if it were a rule.
|
|
20
|
+
5. Low-stakes style details (indentation, private-method order) — just mirror the nearest file, no
|
|
21
|
+
need to ask.
|
|
22
|
+
|
|
23
|
+
### Ask, don't assume, when these aren't already evident in the codebase:
|
|
24
|
+
- Authorization (Pundit / CanCanCan / custom / none)
|
|
25
|
+
- Response/serialization approach (Blueprinter / ActiveModel::Serializer / jsonapi-rb / Jbuilder / plain `as_json`)
|
|
26
|
+
- Pagination (Kaminari / Pagy / will_paginate / none needed)
|
|
27
|
+
- Background jobs (Sidekiq / plain ActiveJob / Resque / none needed)
|
|
28
|
+
- Soft delete vs hard delete, and which gem if soft
|
|
29
|
+
- Test framework (RSpec / Minitest) — usually obvious from `spec/` vs `test/`, confirm if not
|
|
30
|
+
- Rate limiting (rack-attack / none needed)
|
|
31
|
+
- API versioning scheme, if this is the first versioned endpoint
|
|
32
|
+
|
|
33
|
+
## 1. Layering (holds regardless of gem choices)
|
|
34
|
+
|
|
35
|
+
model (persistence/validation) → form object (if the codebase uses one) → service/business logic
|
|
36
|
+
(wherever it already lives) → query object (if in use) → controller (auth → authorize → delegate
|
|
37
|
+
→ render) → whatever serialization approach was detected in step 0.
|
|
38
|
+
|
|
39
|
+
## 2. API shape
|
|
40
|
+
|
|
41
|
+
If endpoints already exist, copy the established response envelope / status-code pattern / error
|
|
42
|
+
shape exactly, even over a "more standard" alternative — consistency wins. Only fall back to
|
|
43
|
+
generic REST conventions if this is genuinely the first endpoint of its kind, and say so.
|
|
44
|
+
|
|
45
|
+
## 3. Validation + errors
|
|
46
|
+
|
|
47
|
+
Match the existing `rescue_from`/error-class pattern if one exists. If none exists yet, a central
|
|
48
|
+
`rescue_from` chain with typed errors is a reasonable default to introduce — but flag that it's a
|
|
49
|
+
new cross-cutting pattern, since it affects every controller.
|
|
50
|
+
|
|
51
|
+
## 4. Security (non-negotiable regardless of which gems are chosen)
|
|
52
|
+
|
|
53
|
+
Strong params on every write action. Parameterized queries only, never string-interpolated SQL.
|
|
54
|
+
Secrets via Rails credentials/ENV, never hardcoded. Authorization checked using whichever library
|
|
55
|
+
is detected (or ask, per step 0, if none is set up).
|
|
56
|
+
|
|
57
|
+
## 5. Database/model
|
|
58
|
+
|
|
59
|
+
New column/table → a migration with DB-level constraints + indexes on FKs and hot query columns.
|
|
60
|
+
Avoid N+1 (`includes`/`preload`) on associations touched in a loop or the serialization layer.
|
|
61
|
+
Multi-table writes wrapped in a transaction.
|
|
62
|
+
|
|
63
|
+
## 6. Tests
|
|
64
|
+
|
|
65
|
+
Match the existing test style (framework, factories vs fixtures) exactly. Minimum bar for new
|
|
66
|
+
code: one happy-path test, one validation/error-path test, one auth-failure test if protected.
|
|
67
|
+
|
|
68
|
+
## 7. Restraint (applies to every task, especially small ones)
|
|
69
|
+
|
|
70
|
+
Make the smallest correct change. No unrelated refactors, no speculative abstractions, no
|
|
71
|
+
scaffolding beyond what was asked. Comments are short, explain *why* not *what*, and only appear
|
|
72
|
+
where the code isn't already self-evident.
|
|
73
|
+
|
|
74
|
+
## 8. Before you call it done
|
|
75
|
+
|
|
76
|
+
Whatever lint tool the codebase already uses, run it clean → no hardcoded secrets → pagination on
|
|
77
|
+
new index actions if the codebase paginates elsewhere → N+1 checked → diff scoped to exactly this
|
|
78
|
+
task.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
Need more depth? `01-project-structure.md`, `02-api-design-standards.md`,
|
|
82
|
+
`03-error-handling-validation.md`, `04-security-standards.md`, `05-database-model-standards.md`,
|
|
83
|
+
`06-testing-standards.md`, `07-new-component-checklist.md`, `08-code-style-general.md`.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Project Structure Standards (Ruby on Rails)
|
|
2
|
+
|
|
3
|
+
## Detect before you structure anything
|
|
4
|
+
|
|
5
|
+
This codebase already has a structure — the job is to recognize it and extend it consistently,
|
|
6
|
+
not to impose a textbook layout on top of it. Before adding a new folder or a new kind of class:
|
|
7
|
+
|
|
8
|
+
1. Look at `app/` — which of these already exist: `services/`, `queries/`, `forms/`, `policies/`,
|
|
9
|
+
`serializers/`, `decorators/`? Whatever's there is the convention for this codebase.
|
|
10
|
+
2. If a folder you'd normally reach for (e.g. `services/`) **doesn't exist yet** and the task is
|
|
11
|
+
small, that's a signal — either this codebase keeps that logic somewhere else (check a model or
|
|
12
|
+
controller for where similar logic lives), or it genuinely doesn't have this layer yet. If
|
|
13
|
+
introducing a new top-level pattern for the first time, say so plainly rather than silently
|
|
14
|
+
creating it, and ask if it's a big structural addition.
|
|
15
|
+
3. Don't scan the entire `app/` tree to figure this out — `ls app/` plus opening one or two
|
|
16
|
+
existing files near where you're adding code is enough.
|
|
17
|
+
|
|
18
|
+
## Common Rails layers, when they're already in use
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
app/
|
|
22
|
+
controllers/ # thin: auth -> authorize -> delegate -> render
|
|
23
|
+
models/ # persistence + validations + associations
|
|
24
|
+
services/ # multi-step business logic, if this codebase uses the pattern
|
|
25
|
+
queries/ # complex/reusable query objects, if in use
|
|
26
|
+
forms/ # form objects for multi-model or non-persisted input, if in use
|
|
27
|
+
policies/ # authorization objects, matching whichever auth gem is detected
|
|
28
|
+
serializers/ # JSON shape, matching whichever serialization approach is detected
|
|
29
|
+
jobs/ # background jobs, matching whichever adapter/gem is detected
|
|
30
|
+
lib/ # framework-agnostic code with no Rails dependency
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Rules that hold regardless of which layers this codebase uses
|
|
34
|
+
|
|
35
|
+
- **Controllers stay thin** no matter what: auth → authorize (if the app has an authorization
|
|
36
|
+
layer) → delegate to wherever business logic already lives → render. Multi-step business logic
|
|
37
|
+
doesn't belong inline in a controller action, whatever it's called in this codebase.
|
|
38
|
+
- **Don't let one model absorb unrelated logic.** If a model file is already large and you're
|
|
39
|
+
about to add another unrelated method, look at how the codebase has handled that split before
|
|
40
|
+
(a concern? a PORO? a service?) and match it, rather than inventing a new extraction pattern.
|
|
41
|
+
- **Concerns are for genuinely shared behavior** (used by 2+ classes already, or clearly about to
|
|
42
|
+
be). A concern used by exactly one class is unnecessary indirection — inline it instead.
|
|
43
|
+
- **Namespace and version the API** the way the existing routes already do. If this is the first
|
|
44
|
+
API endpoint in the app, ask whether versioning is wanted before picking a scheme.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# REST API Design Standards (Rails)
|
|
2
|
+
|
|
3
|
+
## Follow the existing convention first
|
|
4
|
+
|
|
5
|
+
If the app already has API endpoints, open one and copy its shape exactly — route style,
|
|
6
|
+
namespacing, response envelope, status codes, error format. Consistency with the rest of the app
|
|
7
|
+
matters more than matching a generic "best practice" that conflicts with it. Only fall back to the
|
|
8
|
+
generic guidance below if this is genuinely the first endpoint of its kind, and mention that
|
|
9
|
+
you're establishing a new convention.
|
|
10
|
+
|
|
11
|
+
## Routing
|
|
12
|
+
|
|
13
|
+
- Use `resources`/`resource` with explicit `only:`/`except:` — don't leave all 7 RESTful actions
|
|
14
|
+
open if only a few are used, whether or not the rest of the app does this consistently.
|
|
15
|
+
- Nest one level max: `resources :orders do resources :items, only: [:index, :create] end`.
|
|
16
|
+
- Namespace + version if the app already does (`Api::V1::...`); if there's no existing API and
|
|
17
|
+
this is the first one, ask whether versioning is wanted before picking a scheme.
|
|
18
|
+
|
|
19
|
+
## Controllers
|
|
20
|
+
|
|
21
|
+
- Auth → authorize (using whatever authorization approach is already in the codebase — see
|
|
22
|
+
`04-security-standards.md` for how to detect it) → delegate to wherever business logic already
|
|
23
|
+
lives → render. No inline multi-step `ActiveRecord` chains in the controller action itself.
|
|
24
|
+
- Strong parameters are mandatory on every write action regardless of anything else detected.
|
|
25
|
+
|
|
26
|
+
## Status codes & responses
|
|
27
|
+
|
|
28
|
+
- `200`/`201`/`204` success, `400`/`422` validation, `401`/`403` auth, `404` not found, `429` rate
|
|
29
|
+
limited, `500` unhandled — these are stable regardless of gem choices.
|
|
30
|
+
- **Response shape**: detect what the app already returns (check an existing controller/serializer).
|
|
31
|
+
If it already uses Blueprinter, `ActiveModel::Serializer`, `jsonapi-rb`, Jbuilder, or plain
|
|
32
|
+
`as_json`/`render json:` with a hand-built hash — match that, don't introduce a second approach.
|
|
33
|
+
If there's genuinely no existing pattern, ask which the team prefers before picking one; don't
|
|
34
|
+
assume a specific serializer gem.
|
|
35
|
+
- Never call `.to_json` on a raw ActiveRecord object/relation regardless of which serialization
|
|
36
|
+
approach is chosen — that leaks DB columns either way.
|
|
37
|
+
|
|
38
|
+
## Other requirements
|
|
39
|
+
|
|
40
|
+
- **Pagination**: match the existing approach if list endpoints already paginate (Kaminari, Pagy,
|
|
41
|
+
will_paginate, manual). If there's no existing list endpoint to reference, ask before adding a
|
|
42
|
+
pagination gem — a small internal API may not need one.
|
|
43
|
+
- **Filtering/sorting** via an explicit allow-listed param set — never pass raw `params` into
|
|
44
|
+
`.where`/`.order`, independent of which query pattern the app uses.
|
|
45
|
+
- **API docs**: match whatever the app already uses (rswag/OpenAPI annotations, a hand-maintained
|
|
46
|
+
file, or none). If none exists, flag that rather than silently skipping documentation.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Error Handling & Validation Standards (Rails)
|
|
2
|
+
|
|
3
|
+
## Detect the existing pattern
|
|
4
|
+
|
|
5
|
+
Check `ApplicationController` (or the relevant base controller) for existing `rescue_from`
|
|
6
|
+
declarations, and check one or two model/service files for how validation failures are currently
|
|
7
|
+
surfaced. Match that pattern. Only introduce a new centralized `rescue_from` chain if none exists
|
|
8
|
+
— and note that you're adding a new cross-cutting pattern, since it affects every controller.
|
|
9
|
+
|
|
10
|
+
## If there's an existing pattern, extend it
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
# Example shape — only use this if the codebase doesn't already have something similar
|
|
14
|
+
class Api::BaseController < ActionController::API
|
|
15
|
+
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
|
|
16
|
+
rescue_from ActiveRecord::RecordInvalid, with: :render_unprocessable
|
|
17
|
+
end
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- If custom domain error classes already exist (`ApplicationError` or similar), follow that
|
|
21
|
+
hierarchy for new errors. If none exist yet, a base `ApplicationError < StandardError` with
|
|
22
|
+
domain subclasses is a reasonable default to introduce — but call out that it's new.
|
|
23
|
+
|
|
24
|
+
## Rules that hold regardless of the existing pattern
|
|
25
|
+
|
|
26
|
+
- **No bare `rescue => e` that swallows errors silently.** If broad rescue is unavoidable (e.g.
|
|
27
|
+
around a third-party call), re-raise a typed error or log with full context.
|
|
28
|
+
- Validation belongs on the model (persistence-level rules) or a form object (cross-model/
|
|
29
|
+
non-persisted input) — match whichever the codebase already uses for similar cases, rather than
|
|
30
|
+
hand-validating raw `params` in the controller.
|
|
31
|
+
- `.save!`/`.update!` inside service-style objects so failures raise and get caught centrally,
|
|
32
|
+
matching whatever the surrounding code already does with bang vs non-bang methods.
|
|
33
|
+
- Validation error responses return `422` with per-attribute detail
|
|
34
|
+
(`record.errors.full_messages` or similar) — the specific shape should match the response
|
|
35
|
+
envelope already established (see `02-api-design-standards.md`).
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Security Standards (Rails)
|
|
2
|
+
|
|
3
|
+
## Detect the existing security stack before adding anything
|
|
4
|
+
|
|
5
|
+
- **Authorization**: check `Gemfile` for `pundit` or `cancancan`, and check an existing
|
|
6
|
+
controller for `authorize`/`can?`/`load_and_authorize_resource`. Use whichever is already
|
|
7
|
+
present. **If neither is present and the task needs authorization, ask which the team wants**
|
|
8
|
+
(Pundit and CanCanCan solve the same problem differently — don't default to one silently).
|
|
9
|
+
- **Auth (login)**: check for `devise`, `has_secure_password`, or a custom token scheme already in
|
|
10
|
+
the app, and match it. Don't introduce a second auth mechanism alongside an existing one.
|
|
11
|
+
- **Rate limiting**: check for `rack-attack` in `Gemfile`/`config/initializers`. If absent and the
|
|
12
|
+
task is a new public-facing endpoint, flag that rate limiting isn't set up rather than silently
|
|
13
|
+
skipping it — whether to add it is a call worth surfacing, not assuming.
|
|
14
|
+
|
|
15
|
+
## Non-negotiable regardless of which specific gems are in use
|
|
16
|
+
|
|
17
|
+
- **Strong parameters** on every controller write action — no `params.permit!`, no mass-assigning
|
|
18
|
+
a raw hash to `.new`/`.update`.
|
|
19
|
+
- **Secrets**: Rails encrypted credentials or ENV vars — never commit `config/master.key`, never
|
|
20
|
+
hardcode API keys, regardless of how the rest of the app manages config.
|
|
21
|
+
- **SQL injection**: parameterized queries / ActiveRecord query methods always — never
|
|
22
|
+
string-interpolate user input into `.where("... #{params[...]}")` or raw `execute`.
|
|
23
|
+
- **Data exposure**: whatever the serialization layer is (see `02-api-design-standards.md`), it
|
|
24
|
+
must explicitly control which columns are exposed — never render a model with sensitive columns
|
|
25
|
+
(password_digest, tokens) without an explicit allow-list.
|
|
26
|
+
- **CSRF**: `protect_from_forgery` for cookie-session-based controllers; token-authenticated JSON
|
|
27
|
+
APIs (`ActionController::API`) rely on the auth token instead — know which mode the controller
|
|
28
|
+
you're editing is actually in before assuming either way.
|
|
29
|
+
- **Static analysis**: if `brakeman` is already in the project's CI, keep it passing on new code.
|
|
30
|
+
If it isn't set up, that's worth flagging on a security-sensitive change, not silently skipping.
|
|
31
|
+
- HTTPS/secure cookies: match `config/environments/production.rb` if it already sets
|
|
32
|
+
`force_ssl`/cookie flags; don't change security config as a side effect of an unrelated task.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Database & Model Standards (Rails)
|
|
2
|
+
|
|
3
|
+
- **Migrations only** — never hand-edit `schema.rb`/`structure.sql` or run destructive changes
|
|
4
|
+
directly against a shared DB, regardless of project size. Every migration reversible where
|
|
5
|
+
possible.
|
|
6
|
+
- **DB-level constraints**: match how strict the existing schema already is. If existing tables
|
|
7
|
+
consistently use `null: false`/unique indexes/FK constraints, keep that standard for new tables.
|
|
8
|
+
If the existing schema is looser, still add constraints for genuinely required/unique fields —
|
|
9
|
+
this one isn't a "match the existing laxness" case, since it's a correctness/data-integrity
|
|
10
|
+
issue, not a style choice.
|
|
11
|
+
- **Index every foreign key** and every column used in a frequent `WHERE`/`ORDER BY`/`JOIN`.
|
|
12
|
+
- **N+1 prevention**: use `includes`/`preload`/`eager_load` for any association accessed in a loop
|
|
13
|
+
or in the serialization layer. Check whether `bullet` is already in the Gemfile — if so, its
|
|
14
|
+
warnings should be treated as real; if not, this is still worth watching for regardless.
|
|
15
|
+
- **Callbacks**: check how the existing models handle side effects (email sending, external API
|
|
16
|
+
calls) — if they're already in callbacks, that's the established (if not ideal) pattern; don't
|
|
17
|
+
silently "fix" that as part of an unrelated task. For genuinely new logic, prefer keeping side
|
|
18
|
+
effects out of callbacks and explicit in a service/controller, and mention the choice.
|
|
19
|
+
- **Soft delete**: check for `discard`, `paranoia`, or a manual `deleted_at` column already in use.
|
|
20
|
+
If none exists and the task needs "deletion," ask whether soft or hard delete is wanted rather
|
|
21
|
+
than assuming — this has real product/compliance implications.
|
|
22
|
+
- **Transactions** (`ActiveRecord::Base.transaction`) wrap any operation writing to more than one
|
|
23
|
+
table, independent of any other convention question.
|
|
24
|
+
- Prefer `find_each`/`in_batches` over `.each` on large relations to avoid loading an entire table
|
|
25
|
+
into memory.
|