@mrciphersmith/keryx 0.3.1 → 0.3.3
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/dist/cli.js +7310 -2471
- package/dist/core.js +116 -10
- package/package.json +1 -1
- package/src/gdskills/bundled/install-manifest.json +349 -2
- package/src/gdskills/bundled/rules/core/model-selection.mdc +18 -0
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/planning/interviewer/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/quality/deploy/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/review/review-jev-comments/SKILL.md +184 -0
- package/src/gdskills/bundled/skills/review/review-jev-contract/SKILL.md +193 -0
- package/src/gdskills/bundled/skills/review/review-jev-docs/SKILL.md +189 -0
- package/src/gdskills/bundled/skills/review/review-jev-risk/SKILL.md +190 -0
- package/src/gdskills/bundled/skills/review/review-jev-scenarios/SKILL.md +187 -0
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.detail.md +88 -15
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +4 -4
- package/src/gdskills/bundled/stacks/c-cpp/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/c-cpp/governance/eval.json +1777 -0
- package/src/gdskills/bundled/stacks/c-cpp/governance/scout.json +31 -0
- package/src/gdskills/bundled/stacks/c-cpp/pack.json +42 -0
- package/src/gdskills/bundled/stacks/c-cpp/rules/coding-style.mdc +80 -0
- package/src/gdskills/bundled/stacks/c-cpp/rules/patterns.mdc +87 -0
- package/src/gdskills/bundled/stacks/c-cpp/rules/security.mdc +90 -0
- package/src/gdskills/bundled/stacks/c-cpp/rules/testing.mdc +83 -0
- package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-build-fix/SKILL.md +153 -0
- package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-build-fix/evals.json +74 -0
- package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-code-review/SKILL.md +132 -0
- package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-code-review/evals.json +73 -0
- package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-implementation/SKILL.md +151 -0
- package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-implementation/evals.json +74 -0
- package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-testing/SKILL.md +152 -0
- package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-testing/evals.json +74 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/governance/eval.json +1295 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/governance/scout.json +26 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/pack.json +41 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/rules/patterns.mdc +77 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/rules/security.mdc +144 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-build-fix/SKILL.md +121 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-build-fix/evals.json +73 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-code-review/SKILL.md +139 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-code-review/evals.json +73 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-implementation/SKILL.md +147 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-implementation/evals.json +74 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/governance/eval.json +865 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/governance/scout.json +16 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/pack.json +46 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/rules/coding-style.mdc +74 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/rules/patterns.mdc +81 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/rules/security.mdc +146 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/rules/testing.mdc +61 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/skills/docker-k8s-terraform-build-fix/SKILL.md +151 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/skills/docker-k8s-terraform-build-fix/evals.json +74 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/skills/docker-k8s-terraform-review/SKILL.md +135 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/skills/docker-k8s-terraform-review/evals.json +76 -0
- package/src/gdskills/bundled/stacks/php-laravel/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/php-laravel/governance/eval.json +1829 -0
- package/src/gdskills/bundled/stacks/php-laravel/governance/scout.json +33 -0
- package/src/gdskills/bundled/stacks/php-laravel/pack.json +41 -0
- package/src/gdskills/bundled/stacks/php-laravel/rules/coding-style.mdc +82 -0
- package/src/gdskills/bundled/stacks/php-laravel/rules/patterns.mdc +80 -0
- package/src/gdskills/bundled/stacks/php-laravel/rules/security.mdc +80 -0
- package/src/gdskills/bundled/stacks/php-laravel/rules/testing.mdc +82 -0
- package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-build-fix/SKILL.md +143 -0
- package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-build-fix/evals.json +74 -0
- package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-code-review/SKILL.md +126 -0
- package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-code-review/evals.json +76 -0
- package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-implementation/SKILL.md +140 -0
- package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-implementation/evals.json +75 -0
- package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-testing/SKILL.md +124 -0
- package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-testing/evals.json +74 -0
- package/src/gdskills/bundled/stacks/ruby-rails/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/ruby-rails/governance/eval.json +1673 -0
- package/src/gdskills/bundled/stacks/ruby-rails/governance/scout.json +33 -0
- package/src/gdskills/bundled/stacks/ruby-rails/pack.json +42 -0
- package/src/gdskills/bundled/stacks/ruby-rails/rules/coding-style.mdc +69 -0
- package/src/gdskills/bundled/stacks/ruby-rails/rules/patterns.mdc +93 -0
- package/src/gdskills/bundled/stacks/ruby-rails/rules/security.mdc +90 -0
- package/src/gdskills/bundled/stacks/ruby-rails/rules/testing.mdc +89 -0
- package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-build-fix/SKILL.md +143 -0
- package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-build-fix/evals.json +73 -0
- package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-code-review/SKILL.md +134 -0
- package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-code-review/evals.json +71 -0
- package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-implementation/SKILL.md +141 -0
- package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-implementation/evals.json +72 -0
- package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-testing/SKILL.md +125 -0
- package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-testing/evals.json +72 -0
- package/src/gdskills/bundled/stacks/sql-db/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/sql-db/governance/eval.json +1829 -0
- package/src/gdskills/bundled/stacks/sql-db/governance/scout.json +30 -0
- package/src/gdskills/bundled/stacks/sql-db/pack.json +40 -0
- package/src/gdskills/bundled/stacks/sql-db/rules/coding-style.mdc +69 -0
- package/src/gdskills/bundled/stacks/sql-db/rules/patterns.mdc +134 -0
- package/src/gdskills/bundled/stacks/sql-db/rules/security.mdc +74 -0
- package/src/gdskills/bundled/stacks/sql-db/rules/testing.mdc +83 -0
- package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-build-fix/SKILL.md +147 -0
- package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-build-fix/evals.json +72 -0
- package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-code-review/SKILL.md +132 -0
- package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-code-review/evals.json +73 -0
- package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-implementation/SKILL.md +153 -0
- package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-implementation/evals.json +77 -0
- package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-testing/SKILL.md +129 -0
- package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-testing/evals.json +73 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"query": "Use when implementing or extending a feature in a Ruby on Rails app -- MVC boundaries, strong parameters, ActiveRecord associations and scopes, service objects for fat controllers/models, ActiveJob, and modern Ruby 3.x idiom (pattern matching, endless methods, keyword args).",
|
|
4
|
+
"decision": "create",
|
|
5
|
+
"topMatch": "ruby-rails/ruby-rails-testing",
|
|
6
|
+
"recordedAt": "2026-09-25T15:28:04.114Z",
|
|
7
|
+
"skillName": "ruby-rails-implementation"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"query": "Use when a Rails app's test suite needs writing, extending, or fixing -- RSpec request/model/job specs (or Minitest equivalents), FactoryBot fixtures, stubbing external HTTP calls, asserting on enqueued ActiveJobs, and avoiding sleep-based waits.",
|
|
11
|
+
"decision": "fork",
|
|
12
|
+
"topMatch": "php-laravel/php-laravel-testing",
|
|
13
|
+
"recordedAt": "2026-09-25T15:28:28.999Z",
|
|
14
|
+
"skillName": "ruby-rails-testing",
|
|
15
|
+
"justification": "Nearest match php-laravel/php-laravel-testing (0.32) covers PHPUnit/Pest specs, Laravel factories, and PHP HTTP mocking -- not RSpec/Minitest syntax, FactoryBot, or ActiveJob's have_enqueued_job/perform_enqueued_jobs test helpers. react/react-testing (0.23) and python/python-testing (0.21) are further still, sharing only generic testing vocabulary. None share Rails-specific test vocabulary (RSpec, FactoryBot, ActiveJob, request specs) so this is a genuine fork, not a substitute."
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"query": "Use when bundle install/bundle exec fails, a Rails migration or db:test:prepare fails, rubocop reports offenses, or a Rails test suite fails to boot -- resolves Gemfile.lock mismatches, pending/failed migrations, autoload/zeitwerk errors, and rubocop findings with the smallest root-cause fix.",
|
|
19
|
+
"decision": "fork",
|
|
20
|
+
"topMatch": "php-laravel/php-laravel-build-fix",
|
|
21
|
+
"recordedAt": "2026-09-25T15:28:36.534Z",
|
|
22
|
+
"skillName": "ruby-rails-build-fix",
|
|
23
|
+
"justification": "Nearest matches: php-laravel/php-laravel-build-fix (0.44) fixes Composer/artisan migration and PHP linter failures, not Bundler/Gemfile.lock, ActiveRecord migrations, or Zeitwerk autoload errors. go/go-build-fix (0.26) and python/python-build-fix (0.24) fix their own compiler/module-toolchain surfaces, unrelated to rubocop or db/schema.rb regeneration. None share Ruby/Rails-specific vocabulary (Bundler, Zeitwerk, rubocop, db:test:prepare) so this is a genuine fork, not a substitute."
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"query": "Use when reviewing a Rails change for framework-specific risks -- mass assignment gaps, N+1 queries, raw SQL interpolation, raw/html_safe XSS, missing auth/authorization filters, fat controllers/models, and non-idempotent ActiveJobs. Not for a change in another web framework's own MVC layer (use that framework's own code-review skill). Read-only, no edits.",
|
|
27
|
+
"decision": "fork",
|
|
28
|
+
"topMatch": "php-laravel/php-laravel-code-review",
|
|
29
|
+
"recordedAt": "2026-09-25T18:46:59.677Z",
|
|
30
|
+
"skillName": "ruby-rails-code-review",
|
|
31
|
+
"justification": "Nearest match php-laravel-code-review (0.38) shares generic web-framework risk vocabulary (mass assignment, N+1, raw SQL) but reviews Laravel/Eloquent/Blade-specific APIs ($fillable/$guarded, VerifyCsrfToken, ShouldBeUnique) with no knowledge of Rails' strong_parameters, ActiveRecord query interface, or ERB escaping; a reviewer following the Laravel skill could not name the right file/line pattern in a Rails diff. This pack has no lang:ruby skill to extend and is the first Rails reviewer in the catalog, so this is a genuine fork, not a substitute."
|
|
32
|
+
}
|
|
33
|
+
]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "ruby-rails",
|
|
3
|
+
"family": "framework",
|
|
4
|
+
"modules": ["ruby-rails-rules", "ruby-rails-skills"],
|
|
5
|
+
"detectionMarkers": ["ruby", "rails"],
|
|
6
|
+
"provenance": {
|
|
7
|
+
"origin": "authored",
|
|
8
|
+
"sourceRef": "flow 337, Wave 4 batch 5"
|
|
9
|
+
},
|
|
10
|
+
"stability": "experimental",
|
|
11
|
+
"skills": {
|
|
12
|
+
"implement": ["ruby-rails-implementation"],
|
|
13
|
+
"test": ["ruby-rails-testing"],
|
|
14
|
+
"review": ["ruby-rails-code-review"],
|
|
15
|
+
"build-fix": ["ruby-rails-build-fix"],
|
|
16
|
+
"migrate": []
|
|
17
|
+
},
|
|
18
|
+
"agentProfile": {
|
|
19
|
+
"displayName": "Ruby on Rails",
|
|
20
|
+
"auditFocus": [
|
|
21
|
+
"a controller action that builds a model from raw params without strong parameters (params.expect/params.require+permit), enabling mass assignment",
|
|
22
|
+
"raw SQL built with string interpolation instead of ActiveRecord parameterized conditions or sanitize_sql",
|
|
23
|
+
"a loop over an association that triggers N+1 queries because includes/preload/eager_load was not used",
|
|
24
|
+
"raw/html_safe applied to a value that can contain user input, bypassing ERB's automatic HTML escaping",
|
|
25
|
+
"a controller action reachable without before_action :authenticate_user! (or the app's equivalent) or an authorization check",
|
|
26
|
+
"an ActiveJob whose perform is not safe to run twice (no idempotency key or guard) on an adapter that can redeliver it -- true of most production Active Job adapters",
|
|
27
|
+
"a credential, API key, or signing secret committed in source instead of Rails credentials or ENV"
|
|
28
|
+
],
|
|
29
|
+
"buildCommands": [
|
|
30
|
+
"bundle exec rspec (or bin/rails test, whichever the project uses)",
|
|
31
|
+
"bundle exec rubocop",
|
|
32
|
+
"bin/rails db:test:prepare (when a migration changed the schema)",
|
|
33
|
+
"bundle exec brakeman -q (only when the project has Brakeman configured)"
|
|
34
|
+
],
|
|
35
|
+
"fixGuardrails": [
|
|
36
|
+
"Never add rubocop:disable to silence an offense instead of fixing the underlying code.",
|
|
37
|
+
"Never edit db/schema.rb by hand to make a migration pass; fix or add the migration and re-run it.",
|
|
38
|
+
"Never loosen strong parameters (e.g. permit!) or drop a before_action authentication/authorization filter just to make a failing request test pass.",
|
|
39
|
+
"Never delete or skip a failing test to reach a green build."
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
extends: common
|
|
3
|
+
paths: ["**/*.rb"]
|
|
4
|
+
metadata:
|
|
5
|
+
origin: authored
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Ruby on Rails coding style
|
|
9
|
+
|
|
10
|
+
Narrows `core-common-rules`' stack-agnostic style rules to modern Ruby
|
|
11
|
+
(3.x) idiom and Rails conventions in one pack (this pack has no separate
|
|
12
|
+
`lang:ruby` base to extend). Applies only to `*.rb` files.
|
|
13
|
+
|
|
14
|
+
## Naming and structure
|
|
15
|
+
|
|
16
|
+
- `snake_case` for methods, local variables, file names, and DB
|
|
17
|
+
columns/tables; `CamelCase` for classes and modules — never mix Ruby's
|
|
18
|
+
own convention with a borrowed one from another language.
|
|
19
|
+
- A predicate method (returns true/false) ends in `?` (`published?`, not
|
|
20
|
+
`is_published`); a destructive/bang method that mutates in place or
|
|
21
|
+
raises instead of returning nil ends in `!` and normally has a
|
|
22
|
+
non-bang counterpart (`save!` alongside `save`).
|
|
23
|
+
- Two-space indentation, no tabs; keep a method short enough to read on
|
|
24
|
+
one screen — a model method past ~10 lines is a signal to extract a
|
|
25
|
+
private method, a concern, or (per `rules/patterns.mdc`) a service
|
|
26
|
+
object, not to keep growing in place.
|
|
27
|
+
- Follow the app's own file layout: `app/models`, `app/controllers`,
|
|
28
|
+
`app/jobs`, `app/services` (if the app already has one) — do not
|
|
29
|
+
invent a new top-level directory for one class.
|
|
30
|
+
|
|
31
|
+
## Modern Ruby (3.x) idiom
|
|
32
|
+
|
|
33
|
+
- Reach for `case/in` pattern matching when destructuring a
|
|
34
|
+
hash/array-shaped value (parsed JSON, a multi-branch conditional on a
|
|
35
|
+
value's shape) reads clearer than a chain of `if`/`elsif` or
|
|
36
|
+
`dig`/`fetch` calls; do not force pattern matching onto a simple
|
|
37
|
+
`if value.nil?` check where it adds no clarity.
|
|
38
|
+
- An endless method (`def full_name = "#{first_name} #{last_name}"`) is
|
|
39
|
+
fine for a genuine one-expression method; do not squeeze a
|
|
40
|
+
multi-statement method onto one line just to use the syntax.
|
|
41
|
+
- Keyword arguments (`def initialize(name:, email:)`) for a method with
|
|
42
|
+
more than one or two parameters, especially when some are optional —
|
|
43
|
+
clearer at the call site than positional args, and safer against
|
|
44
|
+
argument-order mistakes than a trailing options hash.
|
|
45
|
+
- Prefer `Data.define`/a plain `Struct` for a small immutable value
|
|
46
|
+
object over a full `ActiveRecord`-backed model when nothing needs
|
|
47
|
+
persisting.
|
|
48
|
+
- Safe navigation (`user&.profile&.bio`) only where `nil` is an expected,
|
|
49
|
+
legitimate state for the chain; do not use it to paper over a case
|
|
50
|
+
where a `nil` actually signals a bug that should raise or be handled
|
|
51
|
+
explicitly.
|
|
52
|
+
|
|
53
|
+
## Frozen string literals and immutability
|
|
54
|
+
|
|
55
|
+
- New Ruby files start with `# frozen_string_literal: true` (match the
|
|
56
|
+
project's own convention if it already has one) — string literals are
|
|
57
|
+
frozen by default, so any `String#<<`/`gsub!`/`concat` mutation must
|
|
58
|
+
target an explicit `.dup`/`String.new`, not a literal.
|
|
59
|
+
- Prefer returning a new value over mutating an argument in place, except
|
|
60
|
+
for methods whose name signals mutation (`!`-suffixed, or a
|
|
61
|
+
conventionally mutating method like `each`'s block).
|
|
62
|
+
|
|
63
|
+
## Formatting and linting
|
|
64
|
+
|
|
65
|
+
- Match whatever the project's own `.rubocop.yml` enforces; do not
|
|
66
|
+
hand-format around a configured linter or introduce a style the
|
|
67
|
+
project's cops would flag.
|
|
68
|
+
- Run `bundle exec rubocop` (scoped to changed files is fine during
|
|
69
|
+
iteration) before considering a change formatted correctly.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
extends: common
|
|
3
|
+
paths: ["**/*.rb", "**/*.erb"]
|
|
4
|
+
metadata:
|
|
5
|
+
origin: authored
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Ruby on Rails patterns
|
|
9
|
+
|
|
10
|
+
Narrows `core-common-rules`' stack-agnostic design guidance to idiomatic
|
|
11
|
+
Rails application design and its common anti-patterns. Applies to
|
|
12
|
+
`*.rb` and `*.erb` files.
|
|
13
|
+
|
|
14
|
+
## MVC boundaries
|
|
15
|
+
|
|
16
|
+
- A controller action orchestrates: it reads params, calls into a
|
|
17
|
+
model/service, and renders/redirects — it does not contain business
|
|
18
|
+
logic (validation rules, multi-step workflows, external API calls)
|
|
19
|
+
inline in the action body.
|
|
20
|
+
- A view renders presentation only; move any non-trivial conditional or
|
|
21
|
+
data-shaping logic in a view into a helper or a presenter/decorator
|
|
22
|
+
object instead of embedded Ruby blocks doing real work.
|
|
23
|
+
- `ApplicationRecord` models hold persistence concerns (validations,
|
|
24
|
+
associations, scopes) and behavior that genuinely belongs to the
|
|
25
|
+
record itself; a model accumulating callbacks that orchestrate
|
|
26
|
+
unrelated side effects (send an email, call a third-party API, update
|
|
27
|
+
a different model) is a "fat model" — extract that orchestration into
|
|
28
|
+
a service object or an `ActiveJob`.
|
|
29
|
+
|
|
30
|
+
## Concerns
|
|
31
|
+
|
|
32
|
+
- A `Concern` (`app/models/concerns`, `app/controllers/concerns`) should
|
|
33
|
+
bundle genuinely reusable, cohesive behavior shared by more than one
|
|
34
|
+
class — not become a dumping ground for one model's unrelated methods
|
|
35
|
+
split out just to shrink the file. If nothing else in the app is ever
|
|
36
|
+
going to `include` it, it is not a concern, it is the class's own
|
|
37
|
+
method.
|
|
38
|
+
- Name a concern for the *capability* it adds (`Archivable`,
|
|
39
|
+
`Sluggable`), not for the model it happens to have been extracted
|
|
40
|
+
from.
|
|
41
|
+
|
|
42
|
+
## Service objects and fat controllers/models
|
|
43
|
+
|
|
44
|
+
- When an action's logic spans multiple models, calls an external
|
|
45
|
+
service, or has enough branching to need its own tests independent of
|
|
46
|
+
the controller, extract a plain Ruby service object (a
|
|
47
|
+
`SomethingVerb` class with one public entry point, e.g. `.call`) that
|
|
48
|
+
the controller/model delegates to — do not grow the controller action
|
|
49
|
+
or a model callback to hold it, and match the app's own existing
|
|
50
|
+
service-object convention (directory, base class, `.call` vs `#call`)
|
|
51
|
+
if one already exists rather than inventing a second style.
|
|
52
|
+
- A service object should be easy to unit test without spinning up a
|
|
53
|
+
full request — keep framework concerns (params, session, rendering)
|
|
54
|
+
out of it; pass it plain values or model instances instead.
|
|
55
|
+
|
|
56
|
+
## ActiveRecord query patterns
|
|
57
|
+
|
|
58
|
+
- Eager-load an association before iterating over it in a loop
|
|
59
|
+
(`Book.includes(:author)`), not after the N+1 has already happened —
|
|
60
|
+
prefer `includes` (Rails picks `preload` or `eager_load` for you) over
|
|
61
|
+
hand-choosing one of the lower-level methods unless you have a
|
|
62
|
+
specific reason to force one.
|
|
63
|
+
- Push filtering/aggregation into the database (`where`, `group`,
|
|
64
|
+
`count`, a named `scope`) rather than loading full ActiveRecord
|
|
65
|
+
objects into Ruby and filtering/reducing them in memory.
|
|
66
|
+
- A named `scope` (`scope :published, -> { where(published: true) }`)
|
|
67
|
+
for a query fragment reused in more than one place, instead of
|
|
68
|
+
copy-pasting the same `where` clause across controllers/models.
|
|
69
|
+
|
|
70
|
+
## Background jobs
|
|
71
|
+
|
|
72
|
+
- An `ActiveJob` should be idempotent — most production queue adapters
|
|
73
|
+
(Sidekiq, SQS) are at-least-once, so a retried or duplicated job run must not
|
|
74
|
+
double-charge, double-send, or otherwise corrupt state; guard with a
|
|
75
|
+
uniqueness check, an idempotency key, or an upsert instead of assuming
|
|
76
|
+
`perform` runs exactly once.
|
|
77
|
+
- Pass simple identifiers (an id) into a job's `perform`, not a whole
|
|
78
|
+
ActiveRecord object — the record can change or be deleted between
|
|
79
|
+
enqueue and execution (GlobalID serializes it, but the job should
|
|
80
|
+
re-fetch and handle a missing record rather than trust a stale copy).
|
|
81
|
+
|
|
82
|
+
## Anti-patterns to flag
|
|
83
|
+
|
|
84
|
+
- A "fat controller" action with inline business logic, or a "fat
|
|
85
|
+
model" whose callbacks orchestrate unrelated side effects — extract a
|
|
86
|
+
service object per above.
|
|
87
|
+
- A concern used purely to split up one class's file size rather than to
|
|
88
|
+
share real behavior.
|
|
89
|
+
- Raw SQL string interpolation instead of ActiveRecord's parameterized
|
|
90
|
+
query interface or `sanitize_sql` (see `rules/security.mdc`).
|
|
91
|
+
- A `default_scope` that silently changes every query against a model —
|
|
92
|
+
usually surprising at a call site far from the model definition;
|
|
93
|
+
prefer an explicit named scope callers opt into.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
extends: common
|
|
3
|
+
paths: ["**/*.rb", "**/*.erb"]
|
|
4
|
+
metadata:
|
|
5
|
+
origin: authored
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Ruby on Rails security
|
|
9
|
+
|
|
10
|
+
Narrows `core-common-rules`' stack-agnostic security rules to
|
|
11
|
+
Rails-specific, OWASP-relevant risks and the safe framework API to use
|
|
12
|
+
instead. Applies to `*.rb` and `*.erb` files.
|
|
13
|
+
|
|
14
|
+
## Mass assignment and strong parameters
|
|
15
|
+
|
|
16
|
+
- Never build or update a model from a raw `params` hash
|
|
17
|
+
(`Model.new(params[:model])`, `record.update(params)`); require the
|
|
18
|
+
permitted shape first with `params.expect(model: [:field, ...])`
|
|
19
|
+
(current Rails idiom) or `params.require(:model).permit(:field, ...)`,
|
|
20
|
+
and never call `permit!` on user-controlled params — it disables
|
|
21
|
+
strong parameters entirely and lets any attacker-supplied key through,
|
|
22
|
+
including ones like `admin`/`role` that were never meant to be
|
|
23
|
+
user-settable.
|
|
24
|
+
- List every permitted attribute explicitly; do not permit a whole
|
|
25
|
+
nested hash with a bare `permit(:nested)` when only specific
|
|
26
|
+
sub-keys should be writable.
|
|
27
|
+
|
|
28
|
+
## SQL and data access
|
|
29
|
+
|
|
30
|
+
- Use ActiveRecord's parameterized conditions (`where("age > ?", age)`,
|
|
31
|
+
`where(age: age)`, or named placeholders) — never
|
|
32
|
+
string-interpolate a user-influenced value into `where`,
|
|
33
|
+
`find_by_sql`, `order`, or any other query method
|
|
34
|
+
(`where("name = '#{params[:name]}'")` is SQL injection).
|
|
35
|
+
- When raw SQL genuinely cannot be avoided (a fragment passed to
|
|
36
|
+
`order`/`pluck` built from a dynamic column list, for instance), pass
|
|
37
|
+
untrusted values through `ActiveRecord::Base.sanitize_sql`/
|
|
38
|
+
`sanitize_sql_array`, never through direct string interpolation.
|
|
39
|
+
|
|
40
|
+
## Output encoding and XSS
|
|
41
|
+
|
|
42
|
+
- Rely on ERB's automatic HTML escaping for any value that can contain
|
|
43
|
+
user input; using `raw(value)` or `value.html_safe` on
|
|
44
|
+
user-controlled or user-influenced content reintroduces XSS by
|
|
45
|
+
marking untrusted text as pre-escaped. Reserve `raw`/`html_safe` for
|
|
46
|
+
genuinely static, developer-authored content, and prefer a sanitizer
|
|
47
|
+
(`sanitize`) over `html_safe` when some user-supplied markup must be
|
|
48
|
+
allowed through.
|
|
49
|
+
|
|
50
|
+
## Authentication and authorization
|
|
51
|
+
|
|
52
|
+
- Every controller action that requires a logged-in user runs a
|
|
53
|
+
`before_action` (e.g. `:authenticate_user!` or the app's own
|
|
54
|
+
equivalent) — an action added to an already-protected controller
|
|
55
|
+
inherits the filter only if it is not explicitly skipped
|
|
56
|
+
(`skip_before_action`); check that a new action was not accidentally
|
|
57
|
+
excluded.
|
|
58
|
+
- Authentication (who is this) is not authorization (what can they do):
|
|
59
|
+
confirm the current user is allowed to act on *this specific* record
|
|
60
|
+
(ownership/role check), not just that they are logged in at all —
|
|
61
|
+
an unauthorized action on someone else's resource (IDOR) is a
|
|
62
|
+
distinct bug from a missing login check.
|
|
63
|
+
|
|
64
|
+
## CSRF
|
|
65
|
+
|
|
66
|
+
- Keep `protect_from_forgery with: :exception` (or the equivalent
|
|
67
|
+
`ActionController::Base` default) enabled for any controller serving
|
|
68
|
+
browser-submitted forms; do not disable CSRF protection
|
|
69
|
+
(`skip_before_action :verify_authenticity_token`,
|
|
70
|
+
`protect_from_forgery with: :null_session`) on an action reachable
|
|
71
|
+
from a standard HTML form just to make a request easier to test or
|
|
72
|
+
call from a script — scope any real exception (a webhook endpoint
|
|
73
|
+
verified by its own signature, for instance) as narrowly as possible
|
|
74
|
+
and say why in the change.
|
|
75
|
+
|
|
76
|
+
## Background jobs
|
|
77
|
+
|
|
78
|
+
- An `ActiveJob` must tolerate being run more than once for the same
|
|
79
|
+
logical unit of work (most production queue adapters are at-least-once) —
|
|
80
|
+
guard non-idempotent side effects (charging a card, sending an email)
|
|
81
|
+
with a uniqueness check or an idempotency key rather than assuming
|
|
82
|
+
single delivery.
|
|
83
|
+
|
|
84
|
+
## Secrets
|
|
85
|
+
|
|
86
|
+
- Never commit a credential, API key, or signing secret in source —
|
|
87
|
+
store it in Rails encrypted credentials (`bin/rails credentials:edit`)
|
|
88
|
+
or an environment variable the deployment already manages, and load it
|
|
89
|
+
through `Rails.application.credentials`/`ENV`, not a literal in a
|
|
90
|
+
`.rb` file.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
extends: common
|
|
3
|
+
paths: ["**/*.rb"]
|
|
4
|
+
metadata:
|
|
5
|
+
origin: authored
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Ruby on Rails testing
|
|
9
|
+
|
|
10
|
+
Narrows `core-common-rules`' stack-agnostic testing rules to Rails
|
|
11
|
+
testing idiom. Covers RSpec (the more common choice in current Rails
|
|
12
|
+
apps) and notes the Minitest equivalent where it differs meaningfully.
|
|
13
|
+
Applies only to `*.rb` files.
|
|
14
|
+
|
|
15
|
+
## Layout and framework choice
|
|
16
|
+
|
|
17
|
+
- Match whichever framework the project already uses — `spec/` with
|
|
18
|
+
RSpec (`*_spec.rb`) or `test/` with Minitest (`*_test.rb`); do not
|
|
19
|
+
introduce the other framework into a project that has standardized on
|
|
20
|
+
one.
|
|
21
|
+
- Mirror `app/`'s structure under `spec/`/`test/`: a model at
|
|
22
|
+
`app/models/order.rb` gets a spec at `spec/models/order_spec.rb`, a
|
|
23
|
+
job at `app/jobs/charge_job.rb` gets
|
|
24
|
+
`spec/jobs/charge_job_spec.rb`, and so on.
|
|
25
|
+
|
|
26
|
+
## RSpec conventions
|
|
27
|
+
|
|
28
|
+
- `describe`/`context`/`it` read as a sentence — `context` for a
|
|
29
|
+
precondition ("when the user is not signed in"), `it` for the
|
|
30
|
+
observable behavior ("redirects to the login page"); avoid vague
|
|
31
|
+
`it "works"` descriptions.
|
|
32
|
+
- Use `let`/`let!` for lazily/eagerly built fixtures shared across
|
|
33
|
+
examples in a block instead of repeating setup in every `it`;
|
|
34
|
+
`before` blocks for side effects that must run before each example.
|
|
35
|
+
- Request specs (`spec/requests`) exercise a controller action through
|
|
36
|
+
the full stack (routing, before_actions, rendering) and are generally
|
|
37
|
+
preferred over controller specs for new Rails apps; system specs
|
|
38
|
+
(`spec/system`, via Capybara) exercise a full browser flow — pick the
|
|
39
|
+
narrowest layer that actually covers what changed.
|
|
40
|
+
- Use FactoryBot (`create`/`build`, `build_stubbed` when persistence
|
|
41
|
+
isn't needed) if the project already has it configured, matching its
|
|
42
|
+
existing factory definitions rather than hand-building fixtures a
|
|
43
|
+
different way.
|
|
44
|
+
|
|
45
|
+
## Minitest conventions (when the project uses it instead)
|
|
46
|
+
|
|
47
|
+
- `test "does something" do ... end` inside a `class XyzTest <
|
|
48
|
+
ActiveSupport::TestCase` (or `ActionDispatch::IntegrationTest` for
|
|
49
|
+
request tests); fixtures come from `test/fixtures/*.yml` or the
|
|
50
|
+
project's own factory setup.
|
|
51
|
+
- Assertions read `assert_equal expected, actual` (expected first) and
|
|
52
|
+
`assert_predicate`/`assert_raises` over a bare `assert` on a boolean
|
|
53
|
+
expression, for a clearer failure message.
|
|
54
|
+
|
|
55
|
+
## Database and external boundaries
|
|
56
|
+
|
|
57
|
+
- Tests run inside a transaction per example (RSpec's
|
|
58
|
+
`use_transactional_fixtures`/Rails' default Minitest behavior) that
|
|
59
|
+
rolls back automatically — do not manually clean up records created
|
|
60
|
+
in a test unless the app has opted out of transactional tests for a
|
|
61
|
+
specific reason (e.g. system specs across threads), in which case
|
|
62
|
+
match whatever cleanup strategy (DatabaseCleaner, truncation) it
|
|
63
|
+
already uses.
|
|
64
|
+
- Stub/mock an external HTTP call (a third-party API, a webhook) at the
|
|
65
|
+
boundary (WebMock, VCR cassettes, or the project's existing tooling)
|
|
66
|
+
— a test must not make a real network call.
|
|
67
|
+
- Assert an `ActiveJob` was enqueued with `have_enqueued_job`
|
|
68
|
+
(RSpec) / `assert_enqueued_with` (Minitest) rather than asserting on
|
|
69
|
+
its side effects by actually running it, unless the test's purpose is
|
|
70
|
+
specifically to exercise the job's `perform` behavior — in which case
|
|
71
|
+
drain the queue explicitly (`perform_enqueued_jobs`) instead of
|
|
72
|
+
sleeping or polling for it to run.
|
|
73
|
+
|
|
74
|
+
## Determinism
|
|
75
|
+
|
|
76
|
+
- Never synchronize with `sleep` to wait for a background job, an
|
|
77
|
+
async callback, or an eventually-consistent read — use
|
|
78
|
+
`perform_enqueued_jobs`, a Capybara-provided wait matcher
|
|
79
|
+
(`have_content` retries automatically), or an explicit polling helper
|
|
80
|
+
with a timeout.
|
|
81
|
+
- Freeze time (`travel_to`/`Timecop`, matching whatever the project
|
|
82
|
+
already uses) for any test asserting on a timestamp or a
|
|
83
|
+
time-dependent calculation, instead of asserting against
|
|
84
|
+
`Time.current` computed twice and racing a clock tick.
|
|
85
|
+
|
|
86
|
+
## Regression discipline
|
|
87
|
+
|
|
88
|
+
- A bug fix gets a regression test that fails before the fix and passes
|
|
89
|
+
after; new behavior gets new coverage in the same change.
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ruby-rails-build-fix
|
|
3
|
+
description: "Use when bundle install/bundle exec fails, a Rails migration or db:test:prepare fails, rubocop reports offenses, or a Rails test suite fails to boot -- resolves Gemfile.lock mismatches, pending/failed migrations, autoload/zeitwerk errors, and rubocop findings with the smallest root-cause fix."
|
|
4
|
+
triggers:
|
|
5
|
+
- "bundle install is failing"
|
|
6
|
+
- "this Rails migration won't run"
|
|
7
|
+
- "rubocop is reporting offenses on this Rails file"
|
|
8
|
+
- "zeitwerk autoloading error in this Rails app"
|
|
9
|
+
- "Gemfile.lock is out of sync"
|
|
10
|
+
- "Rails test suite won't boot, pending migration"
|
|
11
|
+
metadata:
|
|
12
|
+
origin: authored
|
|
13
|
+
category: build-fix
|
|
14
|
+
version: "1.0.0"
|
|
15
|
+
compatible_harnesses: "claude,codex,cursor,zed,opencode"
|
|
16
|
+
license: "MIT"
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# Ruby on Rails build fix
|
|
20
|
+
|
|
21
|
+
Resolve a `bundle install`/`bundle exec` failure, a pending or failing
|
|
22
|
+
migration, a Zeitwerk autoload error, a `rubocop` finding, or a Rails
|
|
23
|
+
test suite that fails to boot — with the smallest change that fixes the
|
|
24
|
+
actual root cause. `rules/coding-style.mdc` and `rules/security.mdc`
|
|
25
|
+
govern what a "correct" fix looks like; this skill never reaches for a
|
|
26
|
+
suppression instead of a fix.
|
|
27
|
+
|
|
28
|
+
## Workflow
|
|
29
|
+
|
|
30
|
+
### Step 1: Reproduce and classify
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
bundle install
|
|
34
|
+
bundle exec rubocop
|
|
35
|
+
bin/rails db:migrate:status
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Read the exact error text and classify it:
|
|
39
|
+
|
|
40
|
+
- **Bundler/Gemfile.lock mismatch** (`Gemfile.lock` doesn't satisfy
|
|
41
|
+
`Gemfile`, a version conflict, a missing platform entry).
|
|
42
|
+
- **Pending/failing migration** (`db:migrate:status` shows `down`
|
|
43
|
+
entries, or `db:migrate` raises).
|
|
44
|
+
- **Zeitwerk autoload error** (`Zeitwerk::NameError`, a file whose
|
|
45
|
+
constant name doesn't match its path).
|
|
46
|
+
- **Rubocop offense** (a cop violation reported by `bundle exec
|
|
47
|
+
rubocop`).
|
|
48
|
+
- **Test suite boot failure** (the suite errors before any example
|
|
49
|
+
runs — usually a load error, a missing fixture/factory, or a schema
|
|
50
|
+
mismatch against `db/test.sqlite3`/the test database).
|
|
51
|
+
|
|
52
|
+
### Step 2: Fix by category
|
|
53
|
+
|
|
54
|
+
**Bundler/Gemfile.lock:** run `bundle install` first — if it reports a
|
|
55
|
+
real version conflict rather than a simple stale lock, check `bundle
|
|
56
|
+
outdated <gem>` and the Gemfile's version constraints before bumping a
|
|
57
|
+
version by hand. Only edit `Gemfile.lock` by running Bundler commands
|
|
58
|
+
against it, never by hand-editing the lock file's resolved versions.
|
|
59
|
+
|
|
60
|
+
**Pending/failing migration:** run `bin/rails db:migrate` to apply a
|
|
61
|
+
genuinely pending migration. For a migration that raises, fix the
|
|
62
|
+
migration itself (a bad column type, a missing `up`/`down` pair) and
|
|
63
|
+
re-run it — never hand-edit `db/schema.rb` to match what the migration
|
|
64
|
+
was supposed to produce; the schema is generated, not authored. Run
|
|
65
|
+
`bin/rails db:test:prepare` afterward so the test database matches.
|
|
66
|
+
|
|
67
|
+
**Zeitwerk autoload error:** match the file's path to its expected
|
|
68
|
+
constant name (`app/models/order_item.rb` must define `OrderItem`, not
|
|
69
|
+
`OrderItem` inside a wrongly-cased directory, or vice versa) — Zeitwerk
|
|
70
|
+
infers the constant from the path. If the class genuinely needs a
|
|
71
|
+
different constant name than its conventional path implies, use an
|
|
72
|
+
explicit `inflect`/custom loader entry in
|
|
73
|
+
`config/initializers/zeitwerk.rb` rather than working around the
|
|
74
|
+
mismatch with a `require` or a `require_relative`.
|
|
75
|
+
|
|
76
|
+
**Rubocop offense:** fix the code the offense names. Never add
|
|
77
|
+
`# rubocop:disable Cop/Name` (or a blanket disable at the top of the
|
|
78
|
+
file) to silence a finding without fixing or explaining the underlying
|
|
79
|
+
issue in the report — a narrow, justified disable comment is only
|
|
80
|
+
acceptable when the project's own `.rubocop.yml` already documents that
|
|
81
|
+
exception pattern.
|
|
82
|
+
|
|
83
|
+
**Test suite boot failure:** read the actual load error — a missing
|
|
84
|
+
factory/fixture reference, a schema mismatch (run `bin/rails
|
|
85
|
+
db:test:prepare`), or a require error in `spec_helper.rb`/`rails_helper.rb`.
|
|
86
|
+
Fix the referenced file or run the schema sync; do not comment out the
|
|
87
|
+
failing require or skip loading part of the suite to get past the
|
|
88
|
+
error.
|
|
89
|
+
|
|
90
|
+
### Step 3: Verify
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
bundle exec rspec # or: bin/rails test
|
|
94
|
+
bundle exec rubocop
|
|
95
|
+
bin/rails db:migrate:status
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
All must exit 0 (migrate:status shows no pending migrations) before
|
|
99
|
+
reporting done.
|
|
100
|
+
|
|
101
|
+
### Step 4: Report
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
Fixed: pending migration 20260101000000_add_index_to_orders.rb
|
|
105
|
+
- Root cause: migration was committed but never run against the dev/test DB
|
|
106
|
+
- Ran bin/rails db:migrate + db:test:prepare; rspec/rubocop both pass
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
State the root cause in one sentence, not just "fixed the error."
|
|
110
|
+
|
|
111
|
+
## Rules
|
|
112
|
+
|
|
113
|
+
- Find and fix the smallest change that addresses the actual root
|
|
114
|
+
cause — never widen a fix beyond what the failure requires.
|
|
115
|
+
- NEVER add `# rubocop:disable` to silence a finding instead of fixing
|
|
116
|
+
what it found, unless the project's own config already documents
|
|
117
|
+
that exception.
|
|
118
|
+
- NEVER hand-edit `db/schema.rb` to make a migration failure disappear
|
|
119
|
+
— fix or add the migration and let it regenerate the schema.
|
|
120
|
+
- NEVER hand-edit `Gemfile.lock`'s resolved versions directly; use
|
|
121
|
+
Bundler commands.
|
|
122
|
+
- NEVER delete or skip a failing test to reach a green build.
|
|
123
|
+
|
|
124
|
+
## Red Flags
|
|
125
|
+
|
|
126
|
+
| Rationalization | Why it is wrong |
|
|
127
|
+
|---|---|
|
|
128
|
+
| "I'll add `# rubocop:disable Metrics/MethodLength` here so it stops complaining" | Silences the finding without addressing the actual length/complexity issue the cop caught; refactor instead unless the project's config already allows this exception |
|
|
129
|
+
| "I'll just edit db/schema.rb to add the column the migration was supposed to add" | `db/schema.rb` is generated from migrations, not authored directly; a hand-edit drifts from what `db:migrate` actually produces and breaks the next `db:schema:load` |
|
|
130
|
+
| "This gem conflict is annoying, I'll just delete Gemfile.lock and let it regenerate" | Throws away every other gem's pinned, tested version along with the one causing the conflict; resolve the actual constraint instead |
|
|
131
|
+
| "I'll require_relative the file manually to work around the Zeitwerk naming error" | Papers over a path/constant mismatch Zeitwerk will keep re-flagging elsewhere; fix the file's name/location (or the explicit inflection) instead |
|
|
132
|
+
|
|
133
|
+
## Verification
|
|
134
|
+
|
|
135
|
+
Do not report the fix done until all of the following hold:
|
|
136
|
+
|
|
137
|
+
- `bundle exec rspec` (or `bin/rails test`) and `bundle exec rubocop`
|
|
138
|
+
both exit 0, and `bin/rails db:migrate:status` shows no pending
|
|
139
|
+
migrations.
|
|
140
|
+
- The change is the smallest one that addresses the stated root cause —
|
|
141
|
+
no unrelated files touched.
|
|
142
|
+
- The report states the root cause in one sentence, not just "build now
|
|
143
|
+
passes."
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"triggers": {
|
|
3
|
+
"positive": [
|
|
4
|
+
"After merging main, bundle exec rspec won't even start because Gemfile.lock pins pg to an older version than what's installed",
|
|
5
|
+
"I pulled a teammate's branch and now rails db:test:prepare errors out because the schema doesn't include their new migration",
|
|
6
|
+
"rubocop is flagging Metrics/MethodLength on this file, how do I fix it",
|
|
7
|
+
"Zeitwerk can't find the constant for this file, what's wrong",
|
|
8
|
+
"The test suite errors before any example runs",
|
|
9
|
+
"Gemfile.lock doesn't match Gemfile after I added a gem"
|
|
10
|
+
],
|
|
11
|
+
"negative": [
|
|
12
|
+
"npm install is failing with a peer dependency conflict",
|
|
13
|
+
"cargo build is failing for this Rust crate",
|
|
14
|
+
"Implement the feature once the build is fixed",
|
|
15
|
+
"Review this Rails diff for security issues",
|
|
16
|
+
"Write a regression test for this migration bug"
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"scenarios": [
|
|
20
|
+
{
|
|
21
|
+
"id": "no-rubocop-disable-suppression",
|
|
22
|
+
"prompt": "rubocop is flagging Metrics/MethodLength on this method I just wrote. How should I fix it?",
|
|
23
|
+
"strictness": "high",
|
|
24
|
+
"expected_behavior": [
|
|
25
|
+
{
|
|
26
|
+
"grader": "judge",
|
|
27
|
+
"rubric": "A correct answer fixes the actual complexity the cop flagged by breaking the method into smaller, well-named pieces (an extracted private method or, for cross-model/external-call orchestration, a service object), rather than silencing the finding with a rubocop:disable comment, whether as the sole fix or as extra headroom after a partial refactor.",
|
|
28
|
+
"pass_criteria": [
|
|
29
|
+
"Identifies the fix as breaking the method into smaller pieces (an extracted private method or a service object) to reduce its length/complexity, not merely suppressing the finding.",
|
|
30
|
+
"Shows a concrete extraction direction (names what sub-step would become its own method or object), not just 'refactor it'.",
|
|
31
|
+
"Does not rely on rubocop:disable as part of the fix for the flagged finding."
|
|
32
|
+
],
|
|
33
|
+
"fail_criteria": [
|
|
34
|
+
"Recommends adding a rubocop:disable comment (e.g. `# rubocop:disable Metrics/MethodLength`) -- whether as the sole fix or as extra 'headroom' after a partial refactor -- instead of finishing the refactor so the method satisfies the cop on its own. Mentioning rubocop:disable only to warn against it is not a failure."
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
"calibration": {
|
|
39
|
+
"known_right": "That cop is telling you the method has grown too long to read in one pass. Look for a natural sub-step -- a chunk of the method that computes one intermediate value or handles one branch -- and pull it into its own well-named private method (or, if it's coordinating several models/external calls, a service object per the project's own pattern). That shrinks the flagged method while keeping each piece testable on its own. Once you've split it up, re-run `bundle exec rubocop` to confirm the cop is actually satisfied, not just that the method looks shorter to you.",
|
|
40
|
+
"known_wrong": "Quickest fix is to add `# rubocop:disable Metrics/MethodLength` above the method (and `# rubocop:enable` after it, or just leave it disabled for the rest of the file) -- that clears the offense without needing to restructure anything. The method works fine as written; the cop's default threshold is often too strict for methods that just have a lot of straightforward steps in a row.",
|
|
41
|
+
"vague": "You should probably break that method up a bit instead of just silencing the linter.",
|
|
42
|
+
"subtle_wrong": "Extract the middle chunk of the method -- the part building the response hash -- into a private method so the main method reads shorter, which should bring the line count close to the limit. If it's still one or two lines over after that, just add `# rubocop:disable Metrics/MethodLength` above the method as a small bit of headroom rather than splitting it further -- you've already done the real work of shrinking it, so a narrow disable for the last couple of lines is a reasonable trade-off."
|
|
43
|
+
},
|
|
44
|
+
"anti_patterns": ["rubocop:disable"]
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": "no-hand-edit-schema",
|
|
48
|
+
"prompt": "A migration to add an index failed halfway and now db:test:prepare is failing because the test schema doesn't have it. What should I do?",
|
|
49
|
+
"strictness": "high",
|
|
50
|
+
"expected_behavior": [
|
|
51
|
+
{
|
|
52
|
+
"grader": "judge",
|
|
53
|
+
"rubric": "A correct answer treats db/schema.rb as generated output and fixes the root cause by repairing and re-running the migration (bin/rails db:migrate) rather than hand-editing schema.rb to add the missing index, then syncs the test database with db:test:prepare.",
|
|
54
|
+
"pass_criteria": [
|
|
55
|
+
"States that the migration itself needs to be fixed and re-run (bin/rails db:migrate) rather than hand-editing schema.rb.",
|
|
56
|
+
"Names the concrete command sequence: fix the migration, run db:migrate, then db:test:prepare to sync the test database.",
|
|
57
|
+
"States or implies that db/schema.rb is generated output, not something to author directly."
|
|
58
|
+
],
|
|
59
|
+
"fail_criteria": [
|
|
60
|
+
"Recommends directly hand-editing schema.rb to add the missing index instead of fixing and re-running the migration. Mentioning that only to warn against it is not a failure."
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"calibration": {
|
|
65
|
+
"known_right": "Don't hand-edit `db/schema.rb` -- it's generated output, not something you author directly, and editing it won't actually apply the index to your database, just to the file that's supposed to describe it. Figure out why the migration failed halfway (a lock timeout, a bad column reference, the index already existing under a different name) and fix that in the migration file itself, then re-run `bin/rails db:migrate`. Once that succeeds and `db:migrate:status` shows nothing pending, run `bin/rails db:test:prepare` to sync the test database's schema from the fixed migration.",
|
|
66
|
+
"known_wrong": "Simplest fix: since the migration already ran partway, just open `db/schema.rb` and add the `add_index` line by hand so it matches what the migration was trying to do, then run `db:test:prepare` -- that gets the test database back in sync without needing to debug why the migration itself failed.",
|
|
67
|
+
"vague": "You should get the schema back in sync with the migration before running the tests again.",
|
|
68
|
+
"subtle_wrong": "Add the index to `db/schema.rb` by hand to match what the migration intended, then also mark the migration as run in `schema_migrations` (or just re-run `db:migrate` after the manual schema edit) so Rails doesn't try to apply it again and conflict with your manual change."
|
|
69
|
+
},
|
|
70
|
+
"anti_patterns": ["schema.rb"]
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
}
|