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,804 @@
1
+ ---
2
+ name: rails-routes
3
+ description: "Rails routing: RESTful resources, actions as resources, nested routes, and constraints"
4
+ group: rails
5
+ ---
6
+
7
+ # Routes
8
+
9
+ Comprehensive patterns and best practices for Rails routing.
10
+
11
+ ---
12
+
13
+ ## Core Philosophy
14
+
15
+ 1. **RESTful resources** - Everything is a resource
16
+ 2. **Actions as resources** - Model toggles/actions as singleton resources
17
+ 3. **Zero custom actions** - No `post :custom_action`
18
+ 4. **Nested resources** - Show relationships in URLs
19
+ 5. **Clean URLs** - Use `scope module:` to organize without URL clutter
20
+
21
+ ---
22
+
23
+ ## Basic Resource Routing
24
+
25
+ ### Standard CRUD Resources
26
+
27
+ ```ruby
28
+ resources :boards
29
+ # Generates:
30
+ # GET /boards → boards#index
31
+ # GET /boards/new → boards#new
32
+ # POST /boards → boards#create
33
+ # GET /boards/:id → boards#show
34
+ # GET /boards/:id/edit → boards#edit
35
+ # PATCH /boards/:id → boards#update
36
+ # DELETE /boards/:id → boards#destroy
37
+ ```
38
+
39
+ ### Limit Actions
40
+
41
+ ```ruby
42
+ resources :boards, only: %i[ index show create ]
43
+ resources :exports, only: %i[ create show ]
44
+ resources :tags, only: :index
45
+
46
+ resources :boards, except: %i[ destroy ]
47
+ ```
48
+
49
+ ### Singleton Resources
50
+
51
+ ```ruby
52
+ resource :session # No :id in URL
53
+ # Generates:
54
+ # GET /session/new → sessions#new
55
+ # POST /session → sessions#create
56
+ # GET /session → sessions#show
57
+ # GET /session/edit → sessions#edit
58
+ # PATCH /session → sessions#update
59
+ # DELETE /session → sessions#destroy
60
+ ```
61
+
62
+ ---
63
+
64
+ ## The Core Pattern: Actions as Resources
65
+
66
+ ### ❌ Bad - Custom Actions
67
+
68
+ ```ruby
69
+ resources :cards do
70
+ post :close
71
+ post :reopen
72
+ post :gild
73
+ post :ungild
74
+ post :pin
75
+ delete :unpin
76
+ end
77
+ ```
78
+
79
+ ### ✅ Good - Actions as Resources
80
+
81
+ ```ruby
82
+ resources :cards do
83
+ scope module: :cards do
84
+ resource :closure # POST = close, DELETE = reopen
85
+ resource :goldness # POST = gild, DELETE = ungild
86
+ resource :pin # POST = pin, DELETE = unpin
87
+ end
88
+ end
89
+
90
+ # URLs:
91
+ # POST /cards/:card_id/closure → Cards::ClosuresController#create
92
+ # DELETE /cards/:card_id/closure → Cards::ClosuresController#destroy
93
+ # POST /cards/:card_id/goldness → Cards::GoldnessesController#create
94
+ # DELETE /cards/:card_id/goldness → Cards::GoldnessesController#destroy
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Nested Resources
100
+
101
+ ### Basic Nesting
102
+
103
+ ```ruby
104
+ resources :boards do
105
+ resources :cards
106
+ end
107
+
108
+ # Generates:
109
+ # GET /boards/:board_id/cards → cards#index
110
+ # POST /boards/:board_id/cards → cards#create
111
+ # GET /boards/:board_id/cards/:id → cards#show
112
+ ```
113
+
114
+ ### Shallow Nesting
115
+
116
+ ```ruby
117
+ resources :boards do
118
+ resources :cards, shallow: true
119
+ end
120
+
121
+ # Generates:
122
+ # POST /boards/:board_id/cards → cards#create (nested)
123
+ # GET /cards/:id → cards#show (shallow)
124
+ # PATCH /cards/:id → cards#update (shallow)
125
+ # DELETE /cards/:id → cards#destroy (shallow)
126
+
127
+ # Why? Because cards/:id is enough to identify the resource
128
+ ```
129
+
130
+ ### Multiple Levels
131
+
132
+ ```ruby
133
+ resources :boards do
134
+ resources :cards do
135
+ resources :comments
136
+ end
137
+ end
138
+
139
+ # But consider shallow nesting or limiting depth
140
+ resources :boards do
141
+ resources :cards, shallow: true do
142
+ resources :comments, shallow: true
143
+ end
144
+ end
145
+ ```
146
+
147
+ ---
148
+
149
+ ## Organization with scope module:
150
+
151
+ ### Keep URLs Clean
152
+
153
+ ```ruby
154
+ # Without scope module: (repetitive)
155
+ resources :cards do
156
+ resource :closure, controller: "cards/closures"
157
+ resource :goldness, controller: "cards/goldnesses"
158
+ resource :pin, controller: "cards/pins"
159
+ end
160
+
161
+ # With scope module: (clean)
162
+ resources :cards do
163
+ scope module: :cards do
164
+ resource :closure
165
+ resource :goldness
166
+ resource :pin
167
+
168
+ resources :comments
169
+ resources :taggings
170
+ end
171
+ end
172
+
173
+ # URLs: /cards/:card_id/closure
174
+ # Controllers: Cards::ClosuresController
175
+ ```
176
+
177
+ ### Nested Scope Modules
178
+
179
+ ```ruby
180
+ resources :cards do
181
+ scope module: :cards do
182
+ resources :comments do
183
+ # Reactions belong to comments
184
+ resources :reactions, module: :comments
185
+ end
186
+ end
187
+ end
188
+
189
+ # URL: /cards/:card_id/comments/:comment_id/reactions
190
+ # Controller: Cards::Comments::ReactionsController
191
+ ```
192
+
193
+ ---
194
+
195
+ ## Namespace vs Scope Module
196
+
197
+ ### namespace (affects both URL and controller)
198
+
199
+ ```ruby
200
+ namespace :admin do
201
+ resources :users
202
+ end
203
+
204
+ # URL: /admin/users
205
+ # Controller: Admin::UsersController
206
+ ```
207
+
208
+ ### scope module (controller only)
209
+
210
+ ```ruby
211
+ scope module: :cards do
212
+ resource :closure
213
+ end
214
+
215
+ # URL: /closure
216
+ # Controller: Cards::ClosuresController
217
+ ```
218
+
219
+ ### scope path (URL only)
220
+
221
+ ```ruby
222
+ scope path: :admin do
223
+ resources :users
224
+ end
225
+
226
+ # URL: /admin/users
227
+ # Controller: UsersController
228
+ ```
229
+
230
+ ---
231
+
232
+ ## Collection vs Member Routes
233
+
234
+ ### Member Routes (act on specific resource)
235
+
236
+ ```ruby
237
+ resources :boards do
238
+ member do
239
+ post :archive # POST /boards/:id/archive
240
+ end
241
+ end
242
+
243
+ # Better: Model as resource
244
+ resources :boards do
245
+ resource :archive
246
+ end
247
+ ```
248
+
249
+ ### Collection Routes (act on collection)
250
+
251
+ ```ruby
252
+ resources :cards do
253
+ collection do
254
+ get :search # GET /cards/search
255
+ end
256
+ end
257
+
258
+ # Or use namespace
259
+ namespace :cards do
260
+ resources :searches
261
+ end
262
+ ```
263
+
264
+ ---
265
+
266
+ ## Custom Routes
267
+
268
+ ### Root Route
269
+
270
+ ```ruby
271
+ root "events#index"
272
+ ```
273
+
274
+ ### Simple Routes
275
+
276
+ ```ruby
277
+ get "about", to: "pages#about"
278
+ post "contact", to: "contacts#create"
279
+
280
+ # With constraints
281
+ get "signup", to: "signups#new", constraints: { subdomain: "www" }
282
+ ```
283
+
284
+ ### Direct Routes
285
+
286
+ ```ruby
287
+ # Create helper method with custom URL
288
+ direct :published_board do |board, options|
289
+ route_for :public_board, board.publication.key
290
+ end
291
+
292
+ # Usage in views/controllers:
293
+ published_board_url(@board)
294
+ # => /b/abc123xyz
295
+ ```
296
+
297
+ ### Resolve Routes (Polymorphic)
298
+
299
+ ```ruby
300
+ # Map model to specific route
301
+ resolve "Event" do |event, options|
302
+ polymorphic_url(event.eventable, options)
303
+ end
304
+
305
+ resolve "Comment" do |comment, options|
306
+ options[:anchor] = ActionView::RecordIdentifier.dom_id(comment)
307
+ route_for :card, comment.card, options
308
+ end
309
+
310
+ # Usage:
311
+ url_for(@event)
312
+ # => Routes to event.eventable (e.g., card_url(@event.eventable))
313
+
314
+ link_to "View", @comment
315
+ # => Routes to card with anchor #comment_123
316
+ ```
317
+
318
+ ---
319
+
320
+ ## RESTful Route Patterns
321
+
322
+ ### Resource Routes (Complete Example)
323
+
324
+ ```ruby
325
+ resources :cards do
326
+ scope module: :cards do
327
+ # Singleton resources (toggles/state)
328
+ resource :board # Moving to different board
329
+ resource :closure # Closing/reopening
330
+ resource :column # Moving to column
331
+ resource :goldness # Gilding/ungilding
332
+ resource :image # Uploading/removing image
333
+ resource :not_now # Postponing
334
+ resource :pin # Pinning/unpinning
335
+ resource :publish # Publishing drafts
336
+ resource :triage # Moving to/from triage
337
+ resource :watch # Watching/unwatching
338
+
339
+ # Collection resources (CRUD)
340
+ resources :assignments
341
+ resources :steps
342
+ resources :taggings
343
+ resources :comments do
344
+ resources :reactions, module: :comments
345
+ end
346
+ end
347
+ end
348
+ ```
349
+
350
+ ### Boards Routes
351
+
352
+ ```ruby
353
+ resources :boards do
354
+ scope module: :boards do
355
+ # Singleton resources
356
+ resource :subscriptions
357
+ resource :involvement
358
+ resource :publication
359
+ resource :entropy
360
+
361
+ # Nested namespace
362
+ namespace :columns do
363
+ resource :not_now
364
+ resource :stream
365
+ resource :closed
366
+ end
367
+
368
+ resources :columns
369
+ end
370
+
371
+ # Only create action for cards (cards belong to boards)
372
+ resources :cards, only: :create
373
+
374
+ resources :webhooks do
375
+ scope module: :webhooks do
376
+ resource :activation, only: :create
377
+ end
378
+ end
379
+ end
380
+ ```
381
+
382
+ ---
383
+
384
+ ## Route Constraints
385
+
386
+ ### Parameter Constraints
387
+
388
+ ```ruby
389
+ resources :boards, constraints: { id: /\d+/ }
390
+
391
+ get "/:username", to: "profiles#show",
392
+ constraints: { username: /[a-zA-Z0-9_]+/ }
393
+ ```
394
+
395
+ ### Request Constraints
396
+
397
+ ```ruby
398
+ get "mobile", to: "pages#mobile",
399
+ constraints: { user_agent: /Mobile|Tablet/ }
400
+
401
+ namespace :admin, constraints: { subdomain: "admin" } do
402
+ resources :users
403
+ end
404
+ ```
405
+
406
+ ### Custom Constraint Classes
407
+
408
+ ```ruby
409
+ class AdminConstraint
410
+ def matches?(request)
411
+ user_id = request.session[:user_id]
412
+ user = User.find_by(id: user_id)
413
+ user && user.admin?
414
+ end
415
+ end
416
+
417
+ namespace :admin, constraints: AdminConstraint.new do
418
+ resources :users
419
+ end
420
+ ```
421
+
422
+ ---
423
+
424
+ ## Route Concerns
425
+
426
+ ### Define Reusable Route Patterns
427
+
428
+ ```ruby
429
+ concern :commentable do
430
+ resources :comments
431
+ end
432
+
433
+ concern :likeable do
434
+ resource :like
435
+ end
436
+
437
+ # Use in resources
438
+ resources :posts, concerns: %i[ commentable likeable ]
439
+ resources :articles, concerns: %i[ commentable likeable ]
440
+
441
+ # Equivalent to:
442
+ resources :posts do
443
+ resources :comments
444
+ resource :like
445
+ end
446
+ ```
447
+
448
+ ---
449
+
450
+ ## Scope and Path Helpers
451
+
452
+ ### Custom Path Names
453
+
454
+ ```ruby
455
+ resources :boards, path: "collections"
456
+ # URL: /collections
457
+ # Helpers: boards_path, board_path
458
+
459
+ resource :session, path: "login"
460
+ # URL: /login
461
+ # Helper: session_path
462
+ ```
463
+
464
+ ### Custom Helper Names
465
+
466
+ ```ruby
467
+ resources :boards, as: :collections
468
+ # URL: /boards
469
+ # Helpers: collections_path, collection_path
470
+ ```
471
+
472
+ ---
473
+
474
+ ## Route Helpers
475
+
476
+ ### Path vs URL Helpers
477
+
478
+ ```ruby
479
+ boards_path # => "/boards"
480
+ boards_url # => "http://example.com/boards"
481
+
482
+ board_path(@board) # => "/boards/123"
483
+ board_url(@board) # => "http://example.com/boards/123"
484
+
485
+ new_board_path # => "/boards/new"
486
+ edit_board_path(@board) # => "/boards/123/edit"
487
+ ```
488
+
489
+ ### Nested Resource Helpers
490
+
491
+ ```ruby
492
+ board_cards_path(@board) # => "/boards/123/cards"
493
+ board_card_path(@board, @card) # => "/boards/123/cards/456"
494
+
495
+ # With shallow: true
496
+ card_path(@card) # => "/cards/456"
497
+ ```
498
+
499
+ ### Custom Parameters
500
+
501
+ ```ruby
502
+ board_path(@board, format: :json) # => "/boards/123.json"
503
+ boards_path(page: 2, per: 25) # => "/boards?page=2&per=25"
504
+ card_path(@card, anchor: "comments") # => "/cards/123#comments"
505
+ ```
506
+
507
+ ---
508
+
509
+ ## Multi-Tenancy with URL Path Scoping
510
+
511
+ ### URL Structure
512
+
513
+ ```ruby
514
+ # Middleware extracts account_id from URL
515
+ # /{account_id}/boards/...
516
+
517
+ # In routes.rb - routes are defined normally
518
+ resources :boards
519
+ # But URLs become: /{account_id}/boards
520
+
521
+ # How it works:
522
+ # 1. Middleware (AccountSlug::Extractor) extracts account_id
523
+ # 2. Moves slug from PATH_INFO to SCRIPT_NAME
524
+ # 3. Rails thinks it's "mounted" at /{account_id}
525
+ # 4. All URL helpers automatically include account_id
526
+ ```
527
+
528
+ ### Untenanted Routes
529
+
530
+ ```ruby
531
+ # For routes outside account context (login, signup)
532
+ scope :untenanted do
533
+ resource :session
534
+ resource :signup
535
+ end
536
+
537
+ # Or in controller:
538
+ def new
539
+ untenanted do
540
+ redirect_to new_session_url
541
+ end
542
+ end
543
+ ```
544
+
545
+ ---
546
+
547
+ ## Redirect Routes
548
+
549
+ ### Simple Redirects
550
+
551
+ ```ruby
552
+ get "/old-path", to: redirect("/new-path")
553
+ get "/old-path", to: redirect("/new-path", status: 301) # Permanent
554
+
555
+ # With parameters
556
+ get "/articles/:id", to: redirect("/posts/%{id}")
557
+ ```
558
+
559
+ ### Dynamic Redirects
560
+
561
+ ```ruby
562
+ # Legacy URLs
563
+ get "/collections/:collection_id/cards/:id",
564
+ to: redirect { |params, request|
565
+ "#{request.script_name}/cards/#{params[:id]}"
566
+ }
567
+
568
+ get "/collections/:id",
569
+ to: redirect { |params, request|
570
+ "#{request.script_name}/boards/#{params[:id]}"
571
+ }
572
+ ```
573
+
574
+ ---
575
+
576
+ ## API Versioning
577
+
578
+ ### Namespace Versioning
579
+
580
+ ```ruby
581
+ namespace :api do
582
+ namespace :v1 do
583
+ resources :boards
584
+ resources :cards
585
+ end
586
+
587
+ namespace :v2 do
588
+ resources :boards
589
+ resources :cards
590
+ end
591
+ end
592
+
593
+ # URLs: /api/v1/boards, /api/v2/boards
594
+ ```
595
+
596
+ ### Header-Based Versioning
597
+
598
+ ```ruby
599
+ # Use constraints
600
+ scope module: :api do
601
+ scope module: :v1, constraints: ApiVersionConstraint.new(1) do
602
+ resources :boards
603
+ end
604
+
605
+ scope module: :v2, constraints: ApiVersionConstraint.new(2, default: true) do
606
+ resources :boards
607
+ end
608
+ end
609
+
610
+ # ApiVersionConstraint class
611
+ class ApiVersionConstraint
612
+ def initialize(version, default: false)
613
+ @version = version
614
+ @default = default
615
+ end
616
+
617
+ def matches?(request)
618
+ @default || request.headers["X-API-Version"] == "v#{@version}"
619
+ end
620
+ end
621
+ ```
622
+
623
+ ---
624
+
625
+ ## Testing Routes
626
+
627
+ ### Route Tests
628
+
629
+ ```ruby
630
+ # test/routing/cards_routing_test.rb
631
+ class CardsRoutingTest < ActionDispatch::IntegrationTest
632
+ test "routes to cards#create" do
633
+ assert_routing(
634
+ { method: :post, path: "/boards/1/cards" },
635
+ { controller: "cards", action: "create", board_id: "1" }
636
+ )
637
+ end
638
+
639
+ test "routes to closure#create" do
640
+ assert_routing(
641
+ { method: :post, path: "/cards/1/closure" },
642
+ { controller: "cards/closures", action: "create", card_id: "1" }
643
+ )
644
+ end
645
+ end
646
+ ```
647
+
648
+ ### Route Helper Tests
649
+
650
+ ```ruby
651
+ test "board_path generates correct path" do
652
+ board = boards(:writebook)
653
+ assert_equal "/boards/#{board.id}", board_path(board)
654
+ end
655
+
656
+ test "card_closure_path generates correct path" do
657
+ card = cards(:logo)
658
+ assert_equal "/cards/#{card.number}/closure", card_closure_path(card)
659
+ end
660
+ ```
661
+
662
+ ---
663
+
664
+ ## Debugging Routes
665
+
666
+ ### Rails Console
667
+
668
+ ```ruby
669
+ # List all routes
670
+ Rails.application.routes.routes.map(&:path).map(&:spec).sort.uniq
671
+
672
+ # Find route by helper
673
+ Rails.application.routes.url_helpers.boards_path
674
+ # => "/boards"
675
+
676
+ # Find routes matching pattern
677
+ Rails.application.routes.routes.select { |r| r.path.spec.to_s =~ /cards/ }
678
+
679
+ # Route recognition
680
+ Rails.application.routes.recognize_path("/boards/123")
681
+ # => { controller: "boards", action: "show", id: "123" }
682
+ ```
683
+
684
+ ### bin/rails routes
685
+
686
+ ```bash
687
+ # All routes
688
+ bin/rails routes
689
+
690
+ # Filter by controller
691
+ bin/rails routes -c boards
692
+
693
+ # Filter by pattern
694
+ bin/rails routes -g closure
695
+
696
+ # Expanded format
697
+ bin/rails routes --expanded
698
+ ```
699
+
700
+ ---
701
+
702
+ ## Best Practices
703
+
704
+ ### ✅ DO
705
+
706
+ 1. **Use RESTful resources** - Standard CRUD actions
707
+ 2. **Model actions as resources** - Singleton resources for toggles
708
+ 3. **Use scope module:** - Keep URLs clean, controllers namespaced
709
+ 4. **Shallow nesting** - Avoid deep nesting
710
+ 5. **Limit actions** - Only include needed actions
711
+ 6. **Use concerns** - DRY up repeated patterns
712
+ 7. **Test routes** - Ensure correct routing
713
+
714
+ ### ❌ DON'T
715
+
716
+ 1. **Custom actions** - Use resources instead
717
+ 2. **Deep nesting** - More than 2 levels
718
+ 3. **Generic member/collection** - Use resources
719
+ 4. **Mixing namespace and scope module** - Confusing
720
+ 5. **Too many routes** - Keep focused
721
+
722
+ ---
723
+
724
+ ## Complete Example: Production Routes
725
+
726
+ ```ruby
727
+ Rails.application.routes.draw do
728
+ root "events#index"
729
+
730
+ # Account settings
731
+ namespace :account do
732
+ resource :join_code
733
+ resource :settings
734
+ resource :entropy
735
+ resources :exports, only: %i[ create show ]
736
+ end
737
+
738
+ # Boards
739
+ resources :boards do
740
+ scope module: :boards do
741
+ resource :publication
742
+ resource :entropy
743
+
744
+ namespace :columns do
745
+ resource :not_now
746
+ resource :stream
747
+ resource :closed
748
+ end
749
+
750
+ resources :columns
751
+ end
752
+
753
+ resources :cards, only: :create
754
+ resources :webhooks
755
+ end
756
+
757
+ # Cards (main resource)
758
+ resources :cards do
759
+ scope module: :cards do
760
+ # Singleton resources (state toggles)
761
+ resource :closure
762
+ resource :goldness
763
+ resource :pin
764
+ resource :publish
765
+
766
+ # Collections
767
+ resources :comments do
768
+ resources :reactions, module: :comments
769
+ end
770
+ resources :assignments
771
+ resources :taggings
772
+ end
773
+ end
774
+
775
+ # Session
776
+ resource :session do
777
+ scope module: :sessions do
778
+ resource :magic_link
779
+ resource :menu
780
+ end
781
+ end
782
+
783
+ # Direct routes
784
+ direct :published_board do |board|
785
+ route_for :public_board, board.publication.key
786
+ end
787
+
788
+ # Polymorphic resolution
789
+ resolve "Event" do |event, options|
790
+ polymorphic_url(event.eventable, options)
791
+ end
792
+ end
793
+ ```
794
+
795
+ ---
796
+
797
+ ## Summary
798
+
799
+ - **RESTful Resources**: Everything is a resource
800
+ - **Actions as Resources**: Use singleton resources, not custom actions
801
+ - **Organization**: Use `scope module:` to keep URLs clean
802
+ - **Nesting**: Shallow nesting for deep relationships
803
+ - **Zero Custom Actions**: Model everything as resources
804
+ - **Clean URLs**: Predictable, standard REST patterns