ag-cortex 0.1.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.
Files changed (162) hide show
  1. package/.agent/commands/test-browser.md +339 -0
  2. package/.agent/rules/00-constitution.md +46 -0
  3. package/.agent/rules/project-rules.md +49 -0
  4. package/.agent/skills/agent-browser/SKILL.md +223 -0
  5. package/.agent/skills/agent-native-architecture/SKILL.md +435 -0
  6. package/.agent/skills/agent-native-architecture/references/action-parity-discipline.md +409 -0
  7. package/.agent/skills/agent-native-architecture/references/agent-execution-patterns.md +467 -0
  8. package/.agent/skills/agent-native-architecture/references/agent-native-testing.md +582 -0
  9. package/.agent/skills/agent-native-architecture/references/architecture-patterns.md +478 -0
  10. package/.agent/skills/agent-native-architecture/references/dynamic-context-injection.md +338 -0
  11. package/.agent/skills/agent-native-architecture/references/files-universal-interface.md +301 -0
  12. package/.agent/skills/agent-native-architecture/references/from-primitives-to-domain-tools.md +359 -0
  13. package/.agent/skills/agent-native-architecture/references/mcp-tool-design.md +506 -0
  14. package/.agent/skills/agent-native-architecture/references/mobile-patterns.md +871 -0
  15. package/.agent/skills/agent-native-architecture/references/product-implications.md +443 -0
  16. package/.agent/skills/agent-native-architecture/references/refactoring-to-prompt-native.md +317 -0
  17. package/.agent/skills/agent-native-architecture/references/self-modification.md +269 -0
  18. package/.agent/skills/agent-native-architecture/references/shared-workspace-architecture.md +680 -0
  19. package/.agent/skills/agent-native-architecture/references/system-prompt-design.md +250 -0
  20. package/.agent/skills/agent-native-reviewer/SKILL.md +246 -0
  21. package/.agent/skills/andrew-kane-gem-writer/SKILL.md +184 -0
  22. package/.agent/skills/andrew-kane-gem-writer/references/database-adapters.md +231 -0
  23. package/.agent/skills/andrew-kane-gem-writer/references/module-organization.md +121 -0
  24. package/.agent/skills/andrew-kane-gem-writer/references/rails-integration.md +183 -0
  25. package/.agent/skills/andrew-kane-gem-writer/references/resources.md +119 -0
  26. package/.agent/skills/andrew-kane-gem-writer/references/testing-patterns.md +261 -0
  27. package/.agent/skills/ankane-readme-writer/SKILL.md +50 -0
  28. package/.agent/skills/architecture-strategist/SKILL.md +52 -0
  29. package/.agent/skills/best-practices-researcher/SKILL.md +100 -0
  30. package/.agent/skills/bug-reproduction-validator/SKILL.md +67 -0
  31. package/.agent/skills/code-simplicity-reviewer/SKILL.md +85 -0
  32. package/.agent/skills/coding-tutor/.claude-plugin/plugin.json +9 -0
  33. package/.agent/skills/coding-tutor/README.md +37 -0
  34. package/.agent/skills/coding-tutor/commands/quiz-me.md +1 -0
  35. package/.agent/skills/coding-tutor/commands/sync-tutorials.md +25 -0
  36. package/.agent/skills/coding-tutor/commands/teach-me.md +1 -0
  37. package/.agent/skills/coding-tutor/skills/coding-tutor/SKILL.md +214 -0
  38. package/.agent/skills/coding-tutor/skills/coding-tutor/scripts/create_tutorial.py +202 -0
  39. package/.agent/skills/coding-tutor/skills/coding-tutor/scripts/index_tutorials.py +203 -0
  40. package/.agent/skills/coding-tutor/skills/coding-tutor/scripts/quiz_priority.py +190 -0
  41. package/.agent/skills/coding-tutor/skills/coding-tutor/scripts/setup_tutorials.py +132 -0
  42. package/.agent/skills/compound-docs/SKILL.md +510 -0
  43. package/.agent/skills/compound-docs/assets/critical-pattern-template.md +34 -0
  44. package/.agent/skills/compound-docs/assets/resolution-template.md +93 -0
  45. package/.agent/skills/compound-docs/references/yaml-schema.md +65 -0
  46. package/.agent/skills/compound-docs/schema.yaml +176 -0
  47. package/.agent/skills/create-agent-skills/SKILL.md +299 -0
  48. package/.agent/skills/create-agent-skills/references/api-security.md +226 -0
  49. package/.agent/skills/create-agent-skills/references/be-clear-and-direct.md +531 -0
  50. package/.agent/skills/create-agent-skills/references/best-practices.md +404 -0
  51. package/.agent/skills/create-agent-skills/references/common-patterns.md +595 -0
  52. package/.agent/skills/create-agent-skills/references/core-principles.md +437 -0
  53. package/.agent/skills/create-agent-skills/references/executable-code.md +175 -0
  54. package/.agent/skills/create-agent-skills/references/iteration-and-testing.md +474 -0
  55. package/.agent/skills/create-agent-skills/references/official-spec.md +185 -0
  56. package/.agent/skills/create-agent-skills/references/recommended-structure.md +168 -0
  57. package/.agent/skills/create-agent-skills/references/skill-structure.md +372 -0
  58. package/.agent/skills/create-agent-skills/references/using-scripts.md +113 -0
  59. package/.agent/skills/create-agent-skills/references/using-templates.md +112 -0
  60. package/.agent/skills/create-agent-skills/references/workflows-and-validation.md +510 -0
  61. package/.agent/skills/create-agent-skills/templates/router-skill.md +73 -0
  62. package/.agent/skills/create-agent-skills/templates/simple-skill.md +33 -0
  63. package/.agent/skills/create-agent-skills/workflows/add-reference.md +96 -0
  64. package/.agent/skills/create-agent-skills/workflows/add-script.md +93 -0
  65. package/.agent/skills/create-agent-skills/workflows/add-template.md +74 -0
  66. package/.agent/skills/create-agent-skills/workflows/add-workflow.md +120 -0
  67. package/.agent/skills/create-agent-skills/workflows/audit-skill.md +138 -0
  68. package/.agent/skills/create-agent-skills/workflows/create-domain-expertise-skill.md +605 -0
  69. package/.agent/skills/create-agent-skills/workflows/create-new-skill.md +191 -0
  70. package/.agent/skills/create-agent-skills/workflows/get-guidance.md +121 -0
  71. package/.agent/skills/create-agent-skills/workflows/upgrade-to-router.md +161 -0
  72. package/.agent/skills/create-agent-skills/workflows/verify-skill.md +204 -0
  73. package/.agent/skills/data-integrity-guardian/SKILL.md +70 -0
  74. package/.agent/skills/data-migration-expert/SKILL.md +97 -0
  75. package/.agent/skills/deployment-verification-agent/SKILL.md +159 -0
  76. package/.agent/skills/design-implementation-reviewer/SKILL.md +85 -0
  77. package/.agent/skills/design-iterator/SKILL.md +197 -0
  78. package/.agent/skills/dhh-rails-reviewer/SKILL.md +45 -0
  79. package/.agent/skills/dhh-rails-style/SKILL.md +184 -0
  80. package/.agent/skills/dhh-rails-style/references/architecture.md +653 -0
  81. package/.agent/skills/dhh-rails-style/references/controllers.md +303 -0
  82. package/.agent/skills/dhh-rails-style/references/frontend.md +510 -0
  83. package/.agent/skills/dhh-rails-style/references/gems.md +266 -0
  84. package/.agent/skills/dhh-rails-style/references/models.md +359 -0
  85. package/.agent/skills/dhh-rails-style/references/testing.md +338 -0
  86. package/.agent/skills/dspy-ruby/SKILL.md +594 -0
  87. package/.agent/skills/dspy-ruby/assets/config-template.rb +359 -0
  88. package/.agent/skills/dspy-ruby/assets/module-template.rb +326 -0
  89. package/.agent/skills/dspy-ruby/assets/signature-template.rb +143 -0
  90. package/.agent/skills/dspy-ruby/references/core-concepts.md +265 -0
  91. package/.agent/skills/dspy-ruby/references/optimization.md +623 -0
  92. package/.agent/skills/dspy-ruby/references/providers.md +305 -0
  93. package/.agent/skills/every-style-editor/SKILL.md +134 -0
  94. package/.agent/skills/every-style-editor/references/EVERY_WRITE_STYLE.md +529 -0
  95. package/.agent/skills/figma-design-sync/SKILL.md +166 -0
  96. package/.agent/skills/file-todos/SKILL.md +251 -0
  97. package/.agent/skills/file-todos/assets/todo-template.md +155 -0
  98. package/.agent/skills/framework-docs-researcher/SKILL.md +83 -0
  99. package/.agent/skills/frontend-design/SKILL.md +42 -0
  100. package/.agent/skills/gemini-imagegen/SKILL.md +237 -0
  101. package/.agent/skills/gemini-imagegen/requirements.txt +2 -0
  102. package/.agent/skills/gemini-imagegen/scripts/compose_images.py +168 -0
  103. package/.agent/skills/gemini-imagegen/scripts/edit_image.py +157 -0
  104. package/.agent/skills/gemini-imagegen/scripts/gemini_images.py +265 -0
  105. package/.agent/skills/gemini-imagegen/scripts/generate_image.py +147 -0
  106. package/.agent/skills/gemini-imagegen/scripts/multi_turn_chat.py +215 -0
  107. package/.agent/skills/git-history-analyzer/SKILL.md +42 -0
  108. package/.agent/skills/git-worktree/SKILL.md +302 -0
  109. package/.agent/skills/git-worktree/scripts/worktree-manager.sh +345 -0
  110. package/.agent/skills/julik-frontend-races-reviewer/SKILL.md +222 -0
  111. package/.agent/skills/kieran-python-reviewer/SKILL.md +104 -0
  112. package/.agent/skills/kieran-rails-reviewer/SKILL.md +86 -0
  113. package/.agent/skills/kieran-typescript-reviewer/SKILL.md +95 -0
  114. package/.agent/skills/lint/SKILL.md +16 -0
  115. package/.agent/skills/pattern-recognition-specialist/SKILL.md +57 -0
  116. package/.agent/skills/performance-oracle/SKILL.md +110 -0
  117. package/.agent/skills/pr-comment-resolver/SKILL.md +69 -0
  118. package/.agent/skills/rclone/SKILL.md +150 -0
  119. package/.agent/skills/rclone/scripts/check_setup.sh +60 -0
  120. package/.agent/skills/repo-research-analyst/SKILL.md +113 -0
  121. package/.agent/skills/security-sentinel/SKILL.md +93 -0
  122. package/.agent/skills/skill-creator/SKILL.md +209 -0
  123. package/.agent/skills/skill-creator/scripts/init_skill.py +304 -0
  124. package/.agent/skills/skill-creator/scripts/package_skill.py +112 -0
  125. package/.agent/skills/skill-creator/scripts/quick_validate.py +72 -0
  126. package/.agent/skills/spec-flow-analyzer/SKILL.md +113 -0
  127. package/.agent/skills/test-agent/SKILL.md +4 -0
  128. package/.agent/workflows/agent-native-audit.md +277 -0
  129. package/.agent/workflows/ask-user-question.md +21 -0
  130. package/.agent/workflows/changelog.md +137 -0
  131. package/.agent/workflows/compound.md +202 -0
  132. package/.agent/workflows/create-agent-skill.md +8 -0
  133. package/.agent/workflows/deepen-plan-research.md +334 -0
  134. package/.agent/workflows/deepen-plan-synthesis.md +182 -0
  135. package/.agent/workflows/deepen-plan.md +79 -0
  136. package/.agent/workflows/feature-video.md +342 -0
  137. package/.agent/workflows/generate-command.md +162 -0
  138. package/.agent/workflows/heal-skill.md +142 -0
  139. package/.agent/workflows/lfg.md +20 -0
  140. package/.agent/workflows/plan-analysis.md +67 -0
  141. package/.agent/workflows/plan-next-steps.md +63 -0
  142. package/.agent/workflows/plan-review.md +33 -0
  143. package/.agent/workflows/plan-synthesis.md +106 -0
  144. package/.agent/workflows/plan.md +49 -0
  145. package/.agent/workflows/report-bug.md +150 -0
  146. package/.agent/workflows/reproduce-bug.md +99 -0
  147. package/.agent/workflows/resolve-parallel.md +34 -0
  148. package/.agent/workflows/resolve-pr-parallel.md +49 -0
  149. package/.agent/workflows/resolve-todo-parallel.md +35 -0
  150. package/.agent/workflows/review-analysis.md +145 -0
  151. package/.agent/workflows/review-synthesis.md +262 -0
  152. package/.agent/workflows/review.md +64 -0
  153. package/.agent/workflows/ship.md +90 -0
  154. package/.agent/workflows/test-command.md +3 -0
  155. package/.agent/workflows/triage.md +310 -0
  156. package/.agent/workflows/work.md +157 -0
  157. package/.agent/workflows/xcode-test.md +332 -0
  158. package/LICENSE +22 -0
  159. package/README.md +49 -0
  160. package/bin/ag-cortex.js +54 -0
  161. package/lib/core.js +165 -0
  162. package/package.json +31 -0
@@ -0,0 +1,266 @@
1
+ # Gems - DHH Rails Style
2
+
3
+ <what_they_use>
4
+ ## What 37signals Uses
5
+
6
+ **Core Rails stack:**
7
+ - turbo-rails, stimulus-rails, importmap-rails
8
+ - propshaft (asset pipeline)
9
+
10
+ **Database-backed services (Solid suite):**
11
+ - solid_queue - background jobs
12
+ - solid_cache - caching
13
+ - solid_cable - WebSockets/Action Cable
14
+
15
+ **Authentication & Security:**
16
+ - bcrypt (for any password hashing needed)
17
+
18
+ **Their own gems:**
19
+ - geared_pagination (cursor-based pagination)
20
+ - lexxy (rich text editor)
21
+ - mittens (mailer utilities)
22
+
23
+ **Utilities:**
24
+ - rqrcode (QR code generation)
25
+ - redcarpet + rouge (Markdown rendering)
26
+ - web-push (push notifications)
27
+
28
+ **Deployment & Operations:**
29
+ - kamal (Docker deployment)
30
+ - thruster (HTTP/2 proxy)
31
+ - mission_control-jobs (job monitoring)
32
+ - autotuner (GC tuning)
33
+ </what_they_use>
34
+
35
+ <what_they_avoid>
36
+ ## What They Deliberately Avoid
37
+
38
+ **Authentication:**
39
+ ```
40
+ devise → Custom ~150-line auth
41
+ ```
42
+ Why: Full control, no password liability with magic links, simpler.
43
+
44
+ **Authorization:**
45
+ ```
46
+ pundit/cancancan → Simple role checks in models
47
+ ```
48
+ Why: Most apps don't need policy objects. A method on the model suffices:
49
+ ```ruby
50
+ class Board < ApplicationRecord
51
+ def editable_by?(user)
52
+ user.admin? || user == creator
53
+ end
54
+ end
55
+ ```
56
+
57
+ **Background Jobs:**
58
+ ```
59
+ sidekiq → Solid Queue
60
+ ```
61
+ Why: Database-backed means no Redis, same transactional guarantees.
62
+
63
+ **Caching:**
64
+ ```
65
+ redis → Solid Cache
66
+ ```
67
+ Why: Database is already there, simpler infrastructure.
68
+
69
+ **Search:**
70
+ ```
71
+ elasticsearch → Custom sharded search
72
+ ```
73
+ Why: Built exactly what they need, no external service dependency.
74
+
75
+ **View Layer:**
76
+ ```
77
+ view_component → Standard partials
78
+ ```
79
+ Why: Partials work fine. ViewComponents add complexity without clear benefit for their use case.
80
+
81
+ **API:**
82
+ ```
83
+ GraphQL → REST with Turbo
84
+ ```
85
+ Why: REST is sufficient when you control both ends. GraphQL complexity not justified.
86
+
87
+ **Factories:**
88
+ ```
89
+ factory_bot → Fixtures
90
+ ```
91
+ Why: Fixtures are simpler, faster, and encourage thinking about data relationships upfront.
92
+
93
+ **Service Objects:**
94
+ ```
95
+ Interactor, Trailblazer → Fat models
96
+ ```
97
+ Why: Business logic stays in models. Methods like `card.close` instead of `CardCloser.call(card)`.
98
+
99
+ **Form Objects:**
100
+ ```
101
+ Reform, dry-validation → params.expect + model validations
102
+ ```
103
+ Why: Rails 7.1's `params.expect` is clean enough. Contextual validations on model.
104
+
105
+ **Decorators:**
106
+ ```
107
+ Draper → View helpers + partials
108
+ ```
109
+ Why: Helpers and partials are simpler. No decorator indirection.
110
+
111
+ **CSS:**
112
+ ```
113
+ Tailwind, Sass → Native CSS
114
+ ```
115
+ Why: Modern CSS has nesting, variables, layers. No build step needed.
116
+
117
+ **Frontend:**
118
+ ```
119
+ React, Vue, SPAs → Turbo + Stimulus
120
+ ```
121
+ Why: Server-rendered HTML with sprinkles of JS. SPA complexity not justified.
122
+
123
+ **Testing:**
124
+ ```
125
+ RSpec → Minitest
126
+ ```
127
+ Why: Simpler, faster boot, less DSL magic, ships with Rails.
128
+ </what_they_avoid>
129
+
130
+ <testing_philosophy>
131
+ ## Testing Philosophy
132
+
133
+ **Minitest** - simpler, faster:
134
+ ```ruby
135
+ class CardTest < ActiveSupport::TestCase
136
+ test "closing creates closure" do
137
+ card = cards(:one)
138
+ assert_difference -> { Card::Closure.count } do
139
+ card.close
140
+ end
141
+ assert card.closed?
142
+ end
143
+ end
144
+ ```
145
+
146
+ **Fixtures** - loaded once, deterministic:
147
+ ```yaml
148
+ # test/fixtures/cards.yml
149
+ open_card:
150
+ title: Open Card
151
+ board: main
152
+ creator: alice
153
+
154
+ closed_card:
155
+ title: Closed Card
156
+ board: main
157
+ creator: bob
158
+ ```
159
+
160
+ **Dynamic timestamps** with ERB:
161
+ ```yaml
162
+ recent:
163
+ title: Recent
164
+ created_at: <%= 1.hour.ago %>
165
+
166
+ old:
167
+ title: Old
168
+ created_at: <%= 1.month.ago %>
169
+ ```
170
+
171
+ **Time travel** for time-dependent tests:
172
+ ```ruby
173
+ test "expires after 15 minutes" do
174
+ magic_link = MagicLink.create!(user: users(:alice))
175
+
176
+ travel 16.minutes
177
+
178
+ assert magic_link.expired?
179
+ end
180
+ ```
181
+
182
+ **VCR** for external APIs:
183
+ ```ruby
184
+ VCR.use_cassette("stripe/charge") do
185
+ charge = Stripe::Charge.create(amount: 1000)
186
+ assert charge.paid
187
+ end
188
+ ```
189
+
190
+ **Tests ship with features** - same commit, not before or after.
191
+ </testing_philosophy>
192
+
193
+ <decision_framework>
194
+ ## Decision Framework
195
+
196
+ Before adding a gem, ask:
197
+
198
+ 1. **Can vanilla Rails do this?**
199
+ - ActiveRecord can do most things Sequel can
200
+ - ActionMailer handles email fine
201
+ - ActiveJob works for most job needs
202
+
203
+ 2. **Is the complexity worth it?**
204
+ - 150 lines of custom code vs. 10,000-line gem
205
+ - You'll understand your code better
206
+ - Fewer upgrade headaches
207
+
208
+ 3. **Does it add infrastructure?**
209
+ - Redis? Consider database-backed alternatives
210
+ - External service? Consider building in-house
211
+ - Simpler infrastructure = fewer failure modes
212
+
213
+ 4. **Is it from someone you trust?**
214
+ - 37signals gems: battle-tested at scale
215
+ - Well-maintained, focused gems: usually fine
216
+ - Kitchen-sink gems: probably overkill
217
+
218
+ **The philosophy:**
219
+ > "Build solutions before reaching for gems."
220
+
221
+ Not anti-gem, but pro-understanding. Use gems when they genuinely solve a problem you have, not a problem you might have.
222
+ </decision_framework>
223
+
224
+ <gem_patterns>
225
+ ## Gem Usage Patterns
226
+
227
+ **Pagination:**
228
+ ```ruby
229
+ # geared_pagination - cursor-based
230
+ class CardsController < ApplicationController
231
+ def index
232
+ @cards = @board.cards.geared(page: params[:page])
233
+ end
234
+ end
235
+ ```
236
+
237
+ **Markdown:**
238
+ ```ruby
239
+ # redcarpet + rouge
240
+ class MarkdownRenderer
241
+ def self.render(text)
242
+ Redcarpet::Markdown.new(
243
+ Redcarpet::Render::HTML.new(filter_html: true),
244
+ autolink: true,
245
+ fenced_code_blocks: true
246
+ ).render(text)
247
+ end
248
+ end
249
+ ```
250
+
251
+ **Background jobs:**
252
+ ```ruby
253
+ # solid_queue - no Redis
254
+ class ApplicationJob < ActiveJob::Base
255
+ queue_as :default
256
+ # Just works, backed by database
257
+ end
258
+ ```
259
+
260
+ **Caching:**
261
+ ```ruby
262
+ # solid_cache - no Redis
263
+ # config/environments/production.rb
264
+ config.cache_store = :solid_cache_store
265
+ ```
266
+ </gem_patterns>
@@ -0,0 +1,359 @@
1
+ # Models - DHH Rails Style
2
+
3
+ <model_concerns>
4
+ ## Concerns for Horizontal Behavior
5
+
6
+ Models heavily use concerns. A typical Card model includes 14+ concerns:
7
+
8
+ ```ruby
9
+ class Card < ApplicationRecord
10
+ include Assignable
11
+ include Attachments
12
+ include Broadcastable
13
+ include Closeable
14
+ include Colored
15
+ include Eventable
16
+ include Golden
17
+ include Mentions
18
+ include Multistep
19
+ include Pinnable
20
+ include Postponable
21
+ include Readable
22
+ include Searchable
23
+ include Taggable
24
+ include Watchable
25
+ end
26
+ ```
27
+
28
+ Each concern is self-contained with associations, scopes, and methods.
29
+
30
+ **Naming:** Adjectives describing capability (`Closeable`, `Publishable`, `Watchable`)
31
+ </model_concerns>
32
+
33
+ <state_records>
34
+ ## State as Records, Not Booleans
35
+
36
+ Instead of boolean columns, create separate records:
37
+
38
+ ```ruby
39
+ # Instead of:
40
+ closed: boolean
41
+ is_golden: boolean
42
+ postponed: boolean
43
+
44
+ # Create records:
45
+ class Card::Closure < ApplicationRecord
46
+ belongs_to :card
47
+ belongs_to :creator, class_name: "User"
48
+ end
49
+
50
+ class Card::Goldness < ApplicationRecord
51
+ belongs_to :card
52
+ belongs_to :creator, class_name: "User"
53
+ end
54
+
55
+ class Card::NotNow < ApplicationRecord
56
+ belongs_to :card
57
+ belongs_to :creator, class_name: "User"
58
+ end
59
+ ```
60
+
61
+ **Benefits:**
62
+ - Automatic timestamps (when it happened)
63
+ - Track who made changes
64
+ - Easy filtering via joins and `where.missing`
65
+ - Enables rich UI showing when/who
66
+
67
+ **In the model:**
68
+ ```ruby
69
+ module Closeable
70
+ extend ActiveSupport::Concern
71
+
72
+ included do
73
+ has_one :closure, dependent: :destroy
74
+ end
75
+
76
+ def closed?
77
+ closure.present?
78
+ end
79
+
80
+ def close(creator: Current.user)
81
+ create_closure!(creator: creator)
82
+ end
83
+
84
+ def reopen
85
+ closure&.destroy
86
+ end
87
+ end
88
+ ```
89
+
90
+ **Querying:**
91
+ ```ruby
92
+ Card.joins(:closure) # closed cards
93
+ Card.where.missing(:closure) # open cards
94
+ ```
95
+ </state_records>
96
+
97
+ <callbacks>
98
+ ## Callbacks - Used Sparingly
99
+
100
+ Only 38 callback occurrences across 30 files in Fizzy. Guidelines:
101
+
102
+ **Use for:**
103
+ - `after_commit` for async work
104
+ - `before_save` for derived data
105
+ - `after_create_commit` for side effects
106
+
107
+ **Avoid:**
108
+ - Complex callback chains
109
+ - Business logic in callbacks
110
+ - Synchronous external calls
111
+
112
+ ```ruby
113
+ class Card < ApplicationRecord
114
+ after_create_commit :notify_watchers_later
115
+ before_save :update_search_index, if: :title_changed?
116
+
117
+ private
118
+ def notify_watchers_later
119
+ NotifyWatchersJob.perform_later(self)
120
+ end
121
+ end
122
+ ```
123
+ </callbacks>
124
+
125
+ <scopes>
126
+ ## Scope Naming
127
+
128
+ Standard scope names:
129
+
130
+ ```ruby
131
+ class Card < ApplicationRecord
132
+ scope :chronologically, -> { order(created_at: :asc) }
133
+ scope :reverse_chronologically, -> { order(created_at: :desc) }
134
+ scope :alphabetically, -> { order(title: :asc) }
135
+ scope :latest, -> { reverse_chronologically.limit(10) }
136
+
137
+ # Standard eager loading
138
+ scope :preloaded, -> { includes(:creator, :assignees, :tags) }
139
+
140
+ # Parameterized
141
+ scope :indexed_by, ->(column) { order(column => :asc) }
142
+ scope :sorted_by, ->(column, direction = :asc) { order(column => direction) }
143
+ end
144
+ ```
145
+ </scopes>
146
+
147
+ <poros>
148
+ ## Plain Old Ruby Objects
149
+
150
+ POROs namespaced under parent models:
151
+
152
+ ```ruby
153
+ # app/models/event/description.rb
154
+ class Event::Description
155
+ def initialize(event)
156
+ @event = event
157
+ end
158
+
159
+ def to_s
160
+ # Presentation logic for event description
161
+ end
162
+ end
163
+
164
+ # app/models/card/eventable/system_commenter.rb
165
+ class Card::Eventable::SystemCommenter
166
+ def initialize(card)
167
+ @card = card
168
+ end
169
+
170
+ def comment(message)
171
+ # Business logic
172
+ end
173
+ end
174
+
175
+ # app/models/user/filtering.rb
176
+ class User::Filtering
177
+ # View context bundling
178
+ end
179
+ ```
180
+
181
+ **NOT used for service objects.** Business logic stays in models.
182
+ </poros>
183
+
184
+ <verbs_predicates>
185
+ ## Method Naming
186
+
187
+ **Verbs** - Actions that change state:
188
+ ```ruby
189
+ card.close
190
+ card.reopen
191
+ card.gild # make golden
192
+ card.ungild
193
+ board.publish
194
+ board.archive
195
+ ```
196
+
197
+ **Predicates** - Queries derived from state:
198
+ ```ruby
199
+ card.closed? # closure.present?
200
+ card.golden? # goldness.present?
201
+ board.published?
202
+ ```
203
+
204
+ **Avoid** generic setters:
205
+ ```ruby
206
+ # Bad
207
+ card.set_closed(true)
208
+ card.update_golden_status(false)
209
+
210
+ # Good
211
+ card.close
212
+ card.ungild
213
+ ```
214
+ </verbs_predicates>
215
+
216
+ <validation_philosophy>
217
+ ## Validation Philosophy
218
+
219
+ Minimal validations on models. Use contextual validations on form/operation objects:
220
+
221
+ ```ruby
222
+ # Model - minimal
223
+ class User < ApplicationRecord
224
+ validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
225
+ end
226
+
227
+ # Form object - contextual
228
+ class Signup
229
+ include ActiveModel::Model
230
+
231
+ attr_accessor :email, :name, :terms_accepted
232
+
233
+ validates :email, :name, presence: true
234
+ validates :terms_accepted, acceptance: true
235
+
236
+ def save
237
+ return false unless valid?
238
+ User.create!(email: email, name: name)
239
+ end
240
+ end
241
+ ```
242
+
243
+ **Prefer database constraints** over model validations for data integrity:
244
+ ```ruby
245
+ # migration
246
+ add_index :users, :email, unique: true
247
+ add_foreign_key :cards, :boards
248
+ ```
249
+ </validation_philosophy>
250
+
251
+ <error_handling>
252
+ ## Let It Crash Philosophy
253
+
254
+ Use bang methods that raise exceptions on failure:
255
+
256
+ ```ruby
257
+ # Preferred - raises on failure
258
+ @card = Card.create!(card_params)
259
+ @card.update!(title: new_title)
260
+ @comment.destroy!
261
+
262
+ # Avoid - silent failures
263
+ @card = Card.create(card_params) # returns false on failure
264
+ if @card.save
265
+ # ...
266
+ end
267
+ ```
268
+
269
+ Let errors propagate naturally. Rails handles ActiveRecord::RecordInvalid with 422 responses.
270
+ </error_handling>
271
+
272
+ <default_values>
273
+ ## Default Values with Lambdas
274
+
275
+ Use lambda defaults for associations with Current:
276
+
277
+ ```ruby
278
+ class Card < ApplicationRecord
279
+ belongs_to :creator, class_name: "User", default: -> { Current.user }
280
+ belongs_to :account, default: -> { Current.account }
281
+ end
282
+
283
+ class Comment < ApplicationRecord
284
+ belongs_to :commenter, class_name: "User", default: -> { Current.user }
285
+ end
286
+ ```
287
+
288
+ Lambdas ensure dynamic resolution at creation time.
289
+ </default_values>
290
+
291
+ <rails_71_patterns>
292
+ ## Rails 7.1+ Model Patterns
293
+
294
+ **Normalizes** - clean data before validation:
295
+ ```ruby
296
+ class User < ApplicationRecord
297
+ normalizes :email, with: ->(email) { email.strip.downcase }
298
+ normalizes :phone, with: ->(phone) { phone.gsub(/\D/, "") }
299
+ end
300
+ ```
301
+
302
+ **Delegated Types** - replace polymorphic associations:
303
+ ```ruby
304
+ class Message < ApplicationRecord
305
+ delegated_type :messageable, types: %w[Comment Reply Announcement]
306
+ end
307
+
308
+ # Now you get:
309
+ message.comment? # true if Comment
310
+ message.comment # returns the Comment
311
+ Message.comments # scope for Comment messages
312
+ ```
313
+
314
+ **Store Accessor** - structured JSON storage:
315
+ ```ruby
316
+ class User < ApplicationRecord
317
+ store :settings, accessors: [:theme, :notifications_enabled], coder: JSON
318
+ end
319
+
320
+ user.theme = "dark"
321
+ user.notifications_enabled = true
322
+ ```
323
+ </rails_71_patterns>
324
+
325
+ <concern_guidelines>
326
+ ## Concern Guidelines
327
+
328
+ - **50-150 lines** per concern (most are ~100)
329
+ - **Cohesive** - related functionality only
330
+ - **Named for capabilities** - `Closeable`, `Watchable`, not `CardHelpers`
331
+ - **Self-contained** - associations, scopes, methods together
332
+ - **Not for mere organization** - create when genuine reuse needed
333
+
334
+ **Touch chains** for cache invalidation:
335
+ ```ruby
336
+ class Comment < ApplicationRecord
337
+ belongs_to :card, touch: true
338
+ end
339
+
340
+ class Card < ApplicationRecord
341
+ belongs_to :board, touch: true
342
+ end
343
+ ```
344
+
345
+ When comment updates, card's `updated_at` changes, which cascades to board.
346
+
347
+ **Transaction wrapping** for related updates:
348
+ ```ruby
349
+ class Card < ApplicationRecord
350
+ def close(creator: Current.user)
351
+ transaction do
352
+ create_closure!(creator: creator)
353
+ record_event(:closed)
354
+ notify_watchers_later
355
+ end
356
+ end
357
+ end
358
+ ```
359
+ </concern_guidelines>