agent-notes 2.0.4__py3-none-any.whl

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. agent_notes/VERSION +1 -0
  2. agent_notes/__init__.py +1 -0
  3. agent_notes/__main__.py +4 -0
  4. agent_notes/cli.py +348 -0
  5. agent_notes/commands/__init__.py +27 -0
  6. agent_notes/commands/_install_helpers.py +262 -0
  7. agent_notes/commands/build.py +170 -0
  8. agent_notes/commands/doctor.py +112 -0
  9. agent_notes/commands/info.py +95 -0
  10. agent_notes/commands/install.py +99 -0
  11. agent_notes/commands/list.py +169 -0
  12. agent_notes/commands/memory.py +430 -0
  13. agent_notes/commands/regenerate.py +152 -0
  14. agent_notes/commands/set_role.py +143 -0
  15. agent_notes/commands/uninstall.py +26 -0
  16. agent_notes/commands/update.py +169 -0
  17. agent_notes/commands/validate.py +199 -0
  18. agent_notes/commands/wizard.py +720 -0
  19. agent_notes/config.py +154 -0
  20. agent_notes/data/agents/agents.yaml +352 -0
  21. agent_notes/data/agents/analyst.md +45 -0
  22. agent_notes/data/agents/api-reviewer.md +47 -0
  23. agent_notes/data/agents/architect.md +46 -0
  24. agent_notes/data/agents/coder.md +28 -0
  25. agent_notes/data/agents/database-specialist.md +45 -0
  26. agent_notes/data/agents/debugger.md +47 -0
  27. agent_notes/data/agents/devil.md +47 -0
  28. agent_notes/data/agents/devops.md +38 -0
  29. agent_notes/data/agents/explorer.md +23 -0
  30. agent_notes/data/agents/integrations.md +44 -0
  31. agent_notes/data/agents/lead.md +216 -0
  32. agent_notes/data/agents/performance-profiler.md +44 -0
  33. agent_notes/data/agents/refactorer.md +48 -0
  34. agent_notes/data/agents/reviewer.md +44 -0
  35. agent_notes/data/agents/security-auditor.md +44 -0
  36. agent_notes/data/agents/system-auditor.md +38 -0
  37. agent_notes/data/agents/tech-writer.md +32 -0
  38. agent_notes/data/agents/test-runner.md +36 -0
  39. agent_notes/data/agents/test-writer.md +39 -0
  40. agent_notes/data/cli/claude.yaml +25 -0
  41. agent_notes/data/cli/copilot.yaml +18 -0
  42. agent_notes/data/cli/opencode.yaml +22 -0
  43. agent_notes/data/commands/brainstorm.md +8 -0
  44. agent_notes/data/commands/debug.md +9 -0
  45. agent_notes/data/commands/review.md +10 -0
  46. agent_notes/data/global-claude.md +290 -0
  47. agent_notes/data/global-copilot.md +27 -0
  48. agent_notes/data/global-opencode.md +40 -0
  49. agent_notes/data/hooks/session-context.md.tpl +19 -0
  50. agent_notes/data/models/claude-haiku-4-5.yaml +15 -0
  51. agent_notes/data/models/claude-opus-4-1.yaml +16 -0
  52. agent_notes/data/models/claude-opus-4-5.yaml +16 -0
  53. agent_notes/data/models/claude-opus-4-6.yaml +16 -0
  54. agent_notes/data/models/claude-opus-4-7.yaml +15 -0
  55. agent_notes/data/models/claude-sonnet-4-5.yaml +16 -0
  56. agent_notes/data/models/claude-sonnet-4-6.yaml +15 -0
  57. agent_notes/data/models/claude-sonnet-4.yaml +16 -0
  58. agent_notes/data/pricing.yaml +33 -0
  59. agent_notes/data/roles/orchestrator.yaml +5 -0
  60. agent_notes/data/roles/reasoner.yaml +5 -0
  61. agent_notes/data/roles/scout.yaml +5 -0
  62. agent_notes/data/roles/worker.yaml +5 -0
  63. agent_notes/data/rules/code-quality.md +9 -0
  64. agent_notes/data/rules/safety.md +10 -0
  65. agent_notes/data/scripts/cost-report +211 -0
  66. agent_notes/data/skills/brainstorming/SKILL.md +57 -0
  67. agent_notes/data/skills/code-review/SKILL.md +64 -0
  68. agent_notes/data/skills/debugging-protocol/SKILL.md +51 -0
  69. agent_notes/data/skills/docker-compose/SKILL.md +318 -0
  70. agent_notes/data/skills/docker-compose-advanced/SKILL.md +575 -0
  71. agent_notes/data/skills/docker-dockerfile/SKILL.md +385 -0
  72. agent_notes/data/skills/docker-dockerfile-languages/SKILL.md +293 -0
  73. agent_notes/data/skills/git/SKILL.md +87 -0
  74. agent_notes/data/skills/rails-active-storage/SKILL.md +321 -0
  75. agent_notes/data/skills/rails-broadcasting/SKILL.md +374 -0
  76. agent_notes/data/skills/rails-concerns/SKILL.md +806 -0
  77. agent_notes/data/skills/rails-controllers/SKILL.md +510 -0
  78. agent_notes/data/skills/rails-controllers-advanced/SKILL.md +441 -0
  79. agent_notes/data/skills/rails-helpers/SKILL.md +677 -0
  80. agent_notes/data/skills/rails-initializers/SKILL.md +79 -0
  81. agent_notes/data/skills/rails-javascript/SKILL.md +567 -0
  82. agent_notes/data/skills/rails-jobs/SKILL.md +700 -0
  83. agent_notes/data/skills/rails-kamal/SKILL.md +483 -0
  84. agent_notes/data/skills/rails-lib/SKILL.md +101 -0
  85. agent_notes/data/skills/rails-mailers/SKILL.md +321 -0
  86. agent_notes/data/skills/rails-migrations/SKILL.md +268 -0
  87. agent_notes/data/skills/rails-models/SKILL.md +459 -0
  88. agent_notes/data/skills/rails-models-advanced/SKILL.md +398 -0
  89. agent_notes/data/skills/rails-routes/SKILL.md +804 -0
  90. agent_notes/data/skills/rails-style/SKILL.md +538 -0
  91. agent_notes/data/skills/rails-testing-controllers/SKILL.md +343 -0
  92. agent_notes/data/skills/rails-testing-models/SKILL.md +296 -0
  93. agent_notes/data/skills/rails-testing-system/SKILL.md +375 -0
  94. agent_notes/data/skills/rails-validations/SKILL.md +108 -0
  95. agent_notes/data/skills/rails-view-components/SKILL.md +511 -0
  96. agent_notes/data/skills/rails-view-components-advanced/SKILL.md +376 -0
  97. agent_notes/data/skills/rails-views/SKILL.md +413 -0
  98. agent_notes/data/skills/rails-views-advanced/SKILL.md +450 -0
  99. agent_notes/data/skills/refactoring-protocol/SKILL.md +64 -0
  100. agent_notes/data/skills/tdd/SKILL.md +57 -0
  101. agent_notes/data/templates/__init__.py +1 -0
  102. agent_notes/data/templates/__pycache__/__init__.cpython-314.pyc +0 -0
  103. agent_notes/data/templates/frontmatter/__init__.py +1 -0
  104. agent_notes/data/templates/frontmatter/__pycache__/__init__.cpython-314.pyc +0 -0
  105. agent_notes/data/templates/frontmatter/__pycache__/claude.cpython-314.pyc +0 -0
  106. agent_notes/data/templates/frontmatter/__pycache__/cursor.cpython-314.pyc +0 -0
  107. agent_notes/data/templates/frontmatter/__pycache__/opencode.cpython-314.pyc +0 -0
  108. agent_notes/data/templates/frontmatter/claude.py +44 -0
  109. agent_notes/data/templates/frontmatter/opencode.py +104 -0
  110. agent_notes/doctor_checks.py +189 -0
  111. agent_notes/domain/__init__.py +17 -0
  112. agent_notes/domain/agent.py +34 -0
  113. agent_notes/domain/cli_backend.py +40 -0
  114. agent_notes/domain/diagnostics.py +29 -0
  115. agent_notes/domain/diff.py +44 -0
  116. agent_notes/domain/model.py +27 -0
  117. agent_notes/domain/role.py +13 -0
  118. agent_notes/domain/rule.py +13 -0
  119. agent_notes/domain/skill.py +15 -0
  120. agent_notes/domain/state.py +46 -0
  121. agent_notes/install_state.py +11 -0
  122. agent_notes/registries/__init__.py +16 -0
  123. agent_notes/registries/_base.py +46 -0
  124. agent_notes/registries/agent_registry.py +107 -0
  125. agent_notes/registries/cli_registry.py +89 -0
  126. agent_notes/registries/model_registry.py +85 -0
  127. agent_notes/registries/role_registry.py +64 -0
  128. agent_notes/registries/rule_registry.py +80 -0
  129. agent_notes/registries/skill_registry.py +141 -0
  130. agent_notes/services/__init__.py +8 -0
  131. agent_notes/services/diagnostics/__init__.py +47 -0
  132. agent_notes/services/diagnostics/_checks.py +272 -0
  133. agent_notes/services/diagnostics/_display.py +346 -0
  134. agent_notes/services/diagnostics/_fix.py +169 -0
  135. agent_notes/services/diff.py +349 -0
  136. agent_notes/services/fs.py +195 -0
  137. agent_notes/services/install_state_builder.py +210 -0
  138. agent_notes/services/installer.py +293 -0
  139. agent_notes/services/memory_backend.py +155 -0
  140. agent_notes/services/rendering.py +329 -0
  141. agent_notes/services/session_context.py +23 -0
  142. agent_notes/services/settings_writer.py +79 -0
  143. agent_notes/services/state_store.py +249 -0
  144. agent_notes/services/ui.py +419 -0
  145. agent_notes/services/user_config.py +62 -0
  146. agent_notes/services/validation.py +67 -0
  147. agent_notes/state.py +21 -0
  148. agent_notes-2.0.4.dist-info/METADATA +14 -0
  149. agent_notes-2.0.4.dist-info/RECORD +162 -0
  150. agent_notes-2.0.4.dist-info/WHEEL +5 -0
  151. agent_notes-2.0.4.dist-info/entry_points.txt +2 -0
  152. agent_notes-2.0.4.dist-info/licenses/LICENSE +21 -0
  153. agent_notes-2.0.4.dist-info/top_level.txt +2 -0
  154. tests/conftest.py +20 -0
  155. tests/functional/__init__.py +0 -0
  156. tests/functional/test_build_commands.py +88 -0
  157. tests/functional/test_registries.py +128 -0
  158. tests/integration/__init__.py +0 -0
  159. tests/integration/test_build_output.py +129 -0
  160. tests/plugins/__init__.py +0 -0
  161. tests/plugins/test_agents.py +93 -0
  162. tests/plugins/test_skills.py +77 -0
@@ -0,0 +1,321 @@
1
+ ---
2
+ name: rails-mailers
3
+ description: "Rails Action Mailer: templates, layouts, attachments, previews, and testing"
4
+ group: rails
5
+ ---
6
+
7
+ # Mailers Guide
8
+
9
+ Comprehensive guide for Rails Action Mailer.
10
+
11
+ ---
12
+
13
+ ## Philosophy
14
+
15
+ 1. **Mailers are like controllers** - Thin, delegate to models
16
+ 2. **Preview mailers in development** - Use mailer previews
17
+ 3. **Test email delivery** - Test content, not delivery mechanism
18
+ 4. **Layouts for consistency** - DRY up email HTML
19
+ 5. **Plain text + HTML** - Always provide both formats
20
+
21
+ ---
22
+
23
+ ## File Structure
24
+
25
+ ```
26
+ app/mailers/
27
+ ├── application_mailer.rb
28
+ ├── user_mailer.rb
29
+ ├── notification_mailer.rb
30
+ └── magic_link_mailer.rb
31
+
32
+ app/views/
33
+ ├── layouts/
34
+ │ └── mailer.html.erb
35
+ └── user_mailer/
36
+ ├── welcome.html.erb
37
+ └── welcome.text.erb
38
+ ```
39
+
40
+ ---
41
+
42
+ ## Basic Mailer
43
+
44
+ ### Application Mailer
45
+
46
+ ```ruby
47
+ # app/mailers/application_mailer.rb
48
+ class ApplicationMailer < ActionMailer::Base
49
+ default from: ENV.fetch("MAILER_FROM_ADDRESS", "App <noreply@example.com>")
50
+
51
+ layout "mailer"
52
+
53
+ helper ApplicationHelper, UsersHelper
54
+
55
+ private
56
+ def default_url_options
57
+ if Current.account
58
+ super.merge(script_name: Current.account.slug)
59
+ else
60
+ super
61
+ end
62
+ end
63
+ end
64
+ ```
65
+
66
+ ### Simple Mailer
67
+
68
+ ```ruby
69
+ # app/mailers/user_mailer.rb
70
+ class UserMailer < ApplicationMailer
71
+ def welcome(user)
72
+ @user = user
73
+ @login_url = new_session_url
74
+
75
+ mail to: @user.email, subject: "Welcome to #{app_name}!"
76
+ end
77
+
78
+ def password_reset(user, token)
79
+ @user = user
80
+ @token = token
81
+ @reset_url = edit_password_url(token: @token)
82
+
83
+ mail to: @user.email, subject: "Reset your password"
84
+ end
85
+
86
+ private
87
+ def app_name
88
+ "My App"
89
+ end
90
+ end
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Templates
96
+
97
+ ### HTML Template
98
+
99
+ ```erb
100
+ <%# app/views/user_mailer/welcome.html.erb %>
101
+
102
+ <h1>Welcome, <%= @user.name %>!</h1>
103
+
104
+ <p>Thanks for signing up. We're excited to have you on board.</p>
105
+
106
+ <p>
107
+ <%= link_to "Get Started", @login_url, class: "button" %>
108
+ </p>
109
+
110
+ <p>
111
+ If you have any questions, just reply to this email.
112
+ </p>
113
+
114
+ <p>
115
+ Thanks,<br>
116
+ The Team
117
+ </p>
118
+ ```
119
+
120
+ ### Plain Text Template
121
+
122
+ ```erb
123
+ <%# app/views/user_mailer/welcome.text.erb %>
124
+
125
+ Welcome, <%= @user.name %>!
126
+
127
+ Thanks for signing up. We're excited to have you on board.
128
+
129
+ Get started: <%= @login_url %>
130
+
131
+ If you have any questions, just reply to this email.
132
+
133
+ Thanks,
134
+ The Team
135
+ ```
136
+
137
+ ### Mailer Layout
138
+
139
+ ```erb
140
+ <%# app/views/layouts/mailer.html.erb %>
141
+
142
+ <!DOCTYPE html>
143
+ <html>
144
+ <head>
145
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
146
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
147
+ <style>
148
+ body { font-family: Arial, sans-serif; line-height: 1.6; }
149
+ .button { background: #007bff; color: white; padding: 10px 20px; text-decoration: none; }
150
+ </style>
151
+ </head>
152
+ <body>
153
+ <%= yield %>
154
+
155
+ <hr>
156
+ <p style="font-size: 12px; color: #666;">
157
+ You're receiving this email because you have an account at My App.
158
+ </p>
159
+ </body>
160
+ </html>
161
+ ```
162
+
163
+ ---
164
+
165
+ ## Mailer Methods
166
+
167
+ ### With Attachments
168
+
169
+ ```ruby
170
+ def invoice(user, invoice)
171
+ @user = user
172
+ @invoice = invoice
173
+
174
+ attachments["invoice-#{@invoice.id}.pdf"] = @invoice.to_pdf
175
+ attachments.inline["logo.png"] = File.read(Rails.root.join("app/assets/images/logo.png"))
176
+
177
+ mail to: @user.email, subject: "Invoice ##{@invoice.id}"
178
+ end
179
+ ```
180
+
181
+ ### With CC/BCC
182
+
183
+ ```ruby
184
+ def notification(user, admin)
185
+ @user = user
186
+
187
+ mail(
188
+ to: @user.email,
189
+ cc: admin.email,
190
+ bcc: "notifications@example.com",
191
+ subject: "Important Update"
192
+ )
193
+ end
194
+ ```
195
+
196
+ ### With Custom Headers
197
+
198
+ ```ruby
199
+ def custom_email(user)
200
+ @user = user
201
+
202
+ headers["X-Priority"] = "1"
203
+ headers["X-Mailer"] = "MyApp Mailer"
204
+
205
+ mail to: @user.email, subject: "Custom Email"
206
+ end
207
+ ```
208
+
209
+ ---
210
+
211
+ ## Sending Emails
212
+
213
+ ### Immediate Delivery
214
+
215
+ ```ruby
216
+ # Sends immediately
217
+ UserMailer.welcome(user).deliver_now
218
+ ```
219
+
220
+ ### Background Delivery
221
+
222
+ ```ruby
223
+ # Enqueues job
224
+ UserMailer.welcome(user).deliver_later
225
+
226
+ # With delay
227
+ UserMailer.welcome(user).deliver_later(wait: 1.hour)
228
+
229
+ # At specific time
230
+ UserMailer.welcome(user).deliver_later(wait_until: Time.current + 2.hours)
231
+ ```
232
+
233
+ ### From Models
234
+
235
+ ```ruby
236
+ class User < ApplicationRecord
237
+ after_create :send_welcome_email
238
+
239
+ private
240
+ def send_welcome_email
241
+ UserMailer.welcome(self).deliver_later
242
+ end
243
+ end
244
+ ```
245
+
246
+ ---
247
+
248
+ ## Mailer Previews
249
+
250
+ ### Preview Class
251
+
252
+ ```ruby
253
+ # test/mailers/previews/user_mailer_preview.rb
254
+ class UserMailerPreview < ActionMailer::Preview
255
+ def welcome
256
+ UserMailer.welcome(User.first)
257
+ end
258
+
259
+ def password_reset
260
+ user = User.first
261
+ token = "sample-token-123"
262
+ UserMailer.password_reset(user, token)
263
+ end
264
+ end
265
+
266
+ # Visit: http://localhost:3000/rails/mailers
267
+ ```
268
+
269
+ ---
270
+
271
+ ## Testing Mailers
272
+
273
+ ### Basic Mailer Test
274
+
275
+ ```ruby
276
+ class UserMailerTest < ActionMailer::TestCase
277
+ test "welcome email" do
278
+ user = users(:david)
279
+
280
+ email = UserMailer.welcome(user)
281
+
282
+ assert_emails 1 do
283
+ email.deliver_now
284
+ end
285
+
286
+ assert_equal [user.email], email.to
287
+ assert_equal "Welcome to My App!", email.subject
288
+ assert_match user.name, email.body.encoded
289
+ assert_match "Get Started", email.body.encoded
290
+ end
291
+
292
+ test "welcome email includes login URL" do
293
+ user = users(:david)
294
+
295
+ email = UserMailer.welcome(user)
296
+
297
+ assert_match new_session_url, email.body.encoded
298
+ end
299
+
300
+ test "email has both HTML and text parts" do
301
+ user = users(:david)
302
+
303
+ email = UserMailer.welcome(user)
304
+
305
+ assert_equal 2, email.parts.size
306
+ assert_equal "text/plain", email.text_part.content_type
307
+ assert_equal "text/html", email.html_part.content_type
308
+ end
309
+ end
310
+ ```
311
+
312
+ ---
313
+
314
+ ## Summary
315
+
316
+ - **Structure**: Like controllers, thin and focused
317
+ - **Templates**: HTML + plain text versions
318
+ - **Layouts**: DRY up common email structure
319
+ - **Delivery**: Immediate or background (deliver_later)
320
+ - **Testing**: Test content, recipients, attachments
321
+ - **Previews**: Preview emails in development
@@ -0,0 +1,268 @@
1
+ ---
2
+ name: rails-migrations
3
+ description: "Rails database migrations: tables, columns, indexes, foreign keys, and best practices"
4
+ group: rails
5
+ ---
6
+
7
+ # Migrations Guide
8
+
9
+ Comprehensive guide for Rails database migrations.
10
+
11
+ ---
12
+
13
+ ## File Structure
14
+
15
+ ```
16
+ db/migrate/
17
+ ├── 20250101120000_create_cards.rb
18
+ ├── 20250102130000_add_status_to_cards.rb
19
+ └── 20250103140000_create_index_on_cards_board_id.rb
20
+ ```
21
+
22
+ ---
23
+
24
+ ## Basic Migration Patterns
25
+
26
+ ### Create Table
27
+
28
+ ```ruby
29
+ class CreateCards < ActiveRecord::Migration[8.0]
30
+ def change
31
+ create_table :cards, id: :uuid do |t|
32
+ t.uuid :account_id, null: false
33
+ t.uuid :board_id, null: false
34
+ t.uuid :creator_id, null: false
35
+
36
+ t.integer :number, null: false
37
+ t.string :title
38
+ t.text :description
39
+ t.string :status, default: "draft", null: false
40
+ t.string :color
41
+
42
+ t.timestamps
43
+
44
+ t.index ["account_id", "number"], unique: true
45
+ t.index ["board_id"]
46
+ t.index ["creator_id"]
47
+ t.index ["status"]
48
+ end
49
+ end
50
+ end
51
+ ```
52
+
53
+ ### Add Column
54
+
55
+ ```ruby
56
+ class AddPublishedAtToCards < ActiveRecord::Migration[8.0]
57
+ def change
58
+ add_column :cards, :published_at, :datetime
59
+ add_index :cards, :published_at
60
+ end
61
+ end
62
+ ```
63
+
64
+ ### Remove Column
65
+
66
+ ```ruby
67
+ class RemoveDeprecatedFieldFromCards < ActiveRecord::Migration[8.0]
68
+ def change
69
+ remove_column :cards, :deprecated_field, :string
70
+ end
71
+ end
72
+ ```
73
+
74
+ ### Change Column
75
+
76
+ ```ruby
77
+ class ChangeCardsTitleToText < ActiveRecord::Migration[8.0]
78
+ def change
79
+ change_column :cards, :title, :text
80
+ end
81
+ end
82
+
83
+ # Or with up/down for safety
84
+ class ChangeCardsTitleToText < ActiveRecord::Migration[8.0]
85
+ def up
86
+ change_column :cards, :title, :text
87
+ end
88
+
89
+ def down
90
+ change_column :cards, :title, :string
91
+ end
92
+ end
93
+ ```
94
+
95
+ ### Rename Column
96
+
97
+ ```ruby
98
+ class RenameCardsDescToDescription < ActiveRecord::Migration[8.0]
99
+ def change
100
+ rename_column :cards, :desc, :description
101
+ end
102
+ end
103
+ ```
104
+
105
+ ---
106
+
107
+ ## Index Patterns
108
+
109
+ ### Single Column Index
110
+
111
+ ```ruby
112
+ add_index :cards, :board_id
113
+ add_index :cards, :status
114
+ ```
115
+
116
+ ### Composite Index
117
+
118
+ ```ruby
119
+ add_index :cards, [:account_id, :number], unique: true
120
+ add_index :cards, [:board_id, :status]
121
+ ```
122
+
123
+ ### Named Index
124
+
125
+ ```ruby
126
+ add_index :cards, :email, unique: true, name: "idx_cards_on_email"
127
+ ```
128
+
129
+ ### Partial Index (PostgreSQL)
130
+
131
+ ```ruby
132
+ add_index :cards, :published_at, where: "status = 'published'"
133
+ ```
134
+
135
+ ### Remove Index
136
+
137
+ ```ruby
138
+ remove_index :cards, :board_id
139
+ remove_index :cards, name: "idx_cards_on_email"
140
+ ```
141
+
142
+ ---
143
+
144
+ ## Foreign Keys
145
+
146
+ ```ruby
147
+ class AddForeignKeys < ActiveRecord::Migration[8.0]
148
+ def change
149
+ add_foreign_key :cards, :boards
150
+ add_foreign_key :cards, :accounts
151
+ add_foreign_key :cards, :users, column: :creator_id
152
+ end
153
+ end
154
+ ```
155
+
156
+ ---
157
+
158
+ ## Data Migrations
159
+
160
+ ### Backfill Data
161
+
162
+ ```ruby
163
+ class BackfillCardNumbers < ActiveRecord::Migration[8.0]
164
+ def up
165
+ Card.where(number: nil).find_each do |card|
166
+ card.update!(number: card.account.increment!(:cards_count).cards_count)
167
+ end
168
+ end
169
+
170
+ def down
171
+ # Usually no rollback for data migrations
172
+ end
173
+ end
174
+ ```
175
+
176
+ ---
177
+
178
+ ## UUID Primary Keys
179
+
180
+ ```ruby
181
+ class EnableUuidExtension < ActiveRecord::Migration[8.0]
182
+ def change
183
+ enable_extension "pgcrypto" # PostgreSQL
184
+ end
185
+ end
186
+
187
+ class CreateCardsWithUuid < ActiveRecord::Migration[8.0]
188
+ def change
189
+ create_table :cards, id: :uuid do |t|
190
+ t.uuid :account_id, null: false
191
+ t.timestamps
192
+ end
193
+ end
194
+ end
195
+ ```
196
+
197
+ ---
198
+
199
+ ## Reversible Migrations
200
+
201
+ ```ruby
202
+ class AddCheckConstraint < ActiveRecord::Migration[8.0]
203
+ def change
204
+ reversible do |dir|
205
+ dir.up do
206
+ execute <<-SQL
207
+ ALTER TABLE cards
208
+ ADD CONSTRAINT check_positive_number
209
+ CHECK (number > 0)
210
+ SQL
211
+ end
212
+
213
+ dir.down do
214
+ execute <<-SQL
215
+ ALTER TABLE cards
216
+ DROP CONSTRAINT check_positive_number
217
+ SQL
218
+ end
219
+ end
220
+ end
221
+ end
222
+ ```
223
+
224
+ ---
225
+
226
+ ## Best Practices
227
+
228
+ ### ✅ DO
229
+
230
+ 1. **Add indexes for foreign keys**
231
+ ```ruby
232
+ t.uuid :board_id, null: false
233
+ t.index [:board_id]
234
+ ```
235
+
236
+ 2. **Use null constraints**
237
+ ```ruby
238
+ t.string :title, null: false
239
+ ```
240
+
241
+ 3. **Add default values**
242
+ ```ruby
243
+ t.string :status, default: "draft", null: false
244
+ ```
245
+
246
+ 4. **Use change when possible**
247
+ ```ruby
248
+ def change
249
+ add_column :cards, :color, :string
250
+ end
251
+ ```
252
+
253
+ ### ❌ DON'T
254
+
255
+ 1. **Modify old migrations** - Create new ones
256
+ 2. **Remove columns in production without deprecation**
257
+ 3. **Change column types without considering data loss**
258
+
259
+ ---
260
+
261
+ ## Summary
262
+
263
+ - **Versioned**: Each migration is timestamped
264
+ - **Reversible**: Use `change` when possible
265
+ - **Indexes**: Add for foreign keys and frequently queried columns
266
+ - **Constraints**: Use null, default, unique appropriately
267
+ - **Data Migrations**: Separate from schema migrations
268
+ - **UUID Keys**: Use for distributed systems