red64-cli 0.1.0 → 0.3.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 (125) hide show
  1. package/README.md +1 -2
  2. package/dist/cli/parseArgs.d.ts.map +1 -1
  3. package/dist/cli/parseArgs.js +5 -0
  4. package/dist/cli/parseArgs.js.map +1 -1
  5. package/dist/components/init/CompleteStep.d.ts.map +1 -1
  6. package/dist/components/init/CompleteStep.js +2 -2
  7. package/dist/components/init/CompleteStep.js.map +1 -1
  8. package/dist/components/init/TestCheckStep.d.ts +16 -0
  9. package/dist/components/init/TestCheckStep.d.ts.map +1 -0
  10. package/dist/components/init/TestCheckStep.js +120 -0
  11. package/dist/components/init/TestCheckStep.js.map +1 -0
  12. package/dist/components/init/index.d.ts +1 -0
  13. package/dist/components/init/index.d.ts.map +1 -1
  14. package/dist/components/init/index.js +1 -0
  15. package/dist/components/init/index.js.map +1 -1
  16. package/dist/components/init/types.d.ts +9 -0
  17. package/dist/components/init/types.d.ts.map +1 -1
  18. package/dist/components/screens/InitScreen.d.ts.map +1 -1
  19. package/dist/components/screens/InitScreen.js +69 -6
  20. package/dist/components/screens/InitScreen.js.map +1 -1
  21. package/dist/components/screens/ListScreen.d.ts.map +1 -1
  22. package/dist/components/screens/ListScreen.js +28 -3
  23. package/dist/components/screens/ListScreen.js.map +1 -1
  24. package/dist/components/screens/StartScreen.d.ts.map +1 -1
  25. package/dist/components/screens/StartScreen.js +212 -13
  26. package/dist/components/screens/StartScreen.js.map +1 -1
  27. package/dist/components/ui/ArtifactsSidebar.d.ts +19 -0
  28. package/dist/components/ui/ArtifactsSidebar.d.ts.map +1 -0
  29. package/dist/components/ui/ArtifactsSidebar.js +51 -0
  30. package/dist/components/ui/ArtifactsSidebar.js.map +1 -0
  31. package/dist/components/ui/FeatureSidebar.d.ts.map +1 -1
  32. package/dist/components/ui/FeatureSidebar.js +1 -1
  33. package/dist/components/ui/FeatureSidebar.js.map +1 -1
  34. package/dist/components/ui/index.d.ts +1 -0
  35. package/dist/components/ui/index.d.ts.map +1 -1
  36. package/dist/components/ui/index.js +1 -0
  37. package/dist/components/ui/index.js.map +1 -1
  38. package/dist/services/ClaudeErrorDetector.js +3 -3
  39. package/dist/services/ClaudeErrorDetector.js.map +1 -1
  40. package/dist/services/ConfigService.d.ts +1 -0
  41. package/dist/services/ConfigService.d.ts.map +1 -1
  42. package/dist/services/ConfigService.js.map +1 -1
  43. package/dist/services/ProjectDetector.d.ts +28 -0
  44. package/dist/services/ProjectDetector.d.ts.map +1 -0
  45. package/dist/services/ProjectDetector.js +236 -0
  46. package/dist/services/ProjectDetector.js.map +1 -0
  47. package/dist/services/TestRunner.d.ts +46 -0
  48. package/dist/services/TestRunner.d.ts.map +1 -0
  49. package/dist/services/TestRunner.js +85 -0
  50. package/dist/services/TestRunner.js.map +1 -0
  51. package/dist/services/index.d.ts +2 -0
  52. package/dist/services/index.d.ts.map +1 -1
  53. package/dist/services/index.js +2 -0
  54. package/dist/services/index.js.map +1 -1
  55. package/dist/types/index.d.ts +13 -0
  56. package/dist/types/index.d.ts.map +1 -1
  57. package/dist/types/index.js.map +1 -1
  58. package/framework/.red64/settings/templates/specs/gap-analysis.md +163 -0
  59. package/framework/agents/claude/.claude/agents/red64/spec-impl.md +131 -2
  60. package/framework/agents/claude/.claude/agents/red64/validate-gap.md +13 -7
  61. package/framework/agents/claude/.claude/commands/red64/spec-impl.md +24 -0
  62. package/framework/agents/claude/.claude/commands/red64/validate-gap.md +4 -0
  63. package/framework/agents/codex/.codex/agents/red64/spec-impl.md +131 -2
  64. package/framework/agents/codex/.codex/agents/red64/validate-gap.md +13 -7
  65. package/framework/agents/codex/.codex/commands/red64/spec-impl.md +24 -0
  66. package/framework/agents/codex/.codex/commands/red64/validate-gap.md +4 -0
  67. package/framework/stacks/generic/feedback.md +80 -0
  68. package/framework/stacks/nextjs/accessibility.md +437 -0
  69. package/framework/stacks/nextjs/api.md +431 -0
  70. package/framework/stacks/nextjs/coding-style.md +282 -0
  71. package/framework/stacks/nextjs/commenting.md +226 -0
  72. package/framework/stacks/nextjs/components.md +411 -0
  73. package/framework/stacks/nextjs/conventions.md +333 -0
  74. package/framework/stacks/nextjs/css.md +310 -0
  75. package/framework/stacks/nextjs/error-handling.md +442 -0
  76. package/framework/stacks/nextjs/feedback.md +124 -0
  77. package/framework/stacks/nextjs/migrations.md +332 -0
  78. package/framework/stacks/nextjs/models.md +362 -0
  79. package/framework/stacks/nextjs/queries.md +410 -0
  80. package/framework/stacks/nextjs/responsive.md +338 -0
  81. package/framework/stacks/nextjs/tech-stack.md +177 -0
  82. package/framework/stacks/nextjs/test-writing.md +475 -0
  83. package/framework/stacks/nextjs/validation.md +467 -0
  84. package/framework/stacks/python/api.md +468 -0
  85. package/framework/stacks/python/authentication.md +342 -0
  86. package/framework/stacks/python/code-quality.md +283 -0
  87. package/framework/stacks/python/code-refactoring.md +315 -0
  88. package/framework/stacks/python/coding-style.md +462 -0
  89. package/framework/stacks/python/conventions.md +399 -0
  90. package/framework/stacks/python/error-handling.md +512 -0
  91. package/framework/stacks/python/feedback.md +92 -0
  92. package/framework/stacks/python/implement-ai-llm.md +468 -0
  93. package/framework/stacks/python/migrations.md +388 -0
  94. package/framework/stacks/python/models.md +399 -0
  95. package/framework/stacks/python/python.md +232 -0
  96. package/framework/stacks/python/queries.md +451 -0
  97. package/framework/stacks/python/structure.md +245 -58
  98. package/framework/stacks/python/tech.md +92 -35
  99. package/framework/stacks/python/testing.md +380 -0
  100. package/framework/stacks/python/validation.md +471 -0
  101. package/framework/stacks/rails/authentication.md +176 -0
  102. package/framework/stacks/rails/code-quality.md +287 -0
  103. package/framework/stacks/rails/code-refactoring.md +299 -0
  104. package/framework/stacks/rails/feedback.md +130 -0
  105. package/framework/stacks/rails/implement-ai-llm-with-rubyllm.md +342 -0
  106. package/framework/stacks/rails/rails.md +301 -0
  107. package/framework/stacks/rails/rails8-best-practices.md +498 -0
  108. package/framework/stacks/rails/rails8-css.md +573 -0
  109. package/framework/stacks/rails/structure.md +140 -0
  110. package/framework/stacks/rails/tech.md +108 -0
  111. package/framework/stacks/react/code-quality.md +521 -0
  112. package/framework/stacks/react/components.md +625 -0
  113. package/framework/stacks/react/data-fetching.md +586 -0
  114. package/framework/stacks/react/feedback.md +110 -0
  115. package/framework/stacks/react/forms.md +694 -0
  116. package/framework/stacks/react/performance.md +640 -0
  117. package/framework/stacks/react/product.md +22 -9
  118. package/framework/stacks/react/state-management.md +472 -0
  119. package/framework/stacks/react/structure.md +351 -44
  120. package/framework/stacks/react/tech.md +219 -30
  121. package/framework/stacks/react/testing.md +690 -0
  122. package/package.json +1 -1
  123. package/framework/stacks/node/product.md +0 -27
  124. package/framework/stacks/node/structure.md +0 -82
  125. package/framework/stacks/node/tech.md +0 -63
@@ -0,0 +1,342 @@
1
+ # AI/LLM Implementation with ruby_llm
2
+
3
+ Project memory for implementing AI-powered features using the ruby_llm gem in MediaPulse.
4
+
5
+ ---
6
+
7
+ ## Overview
8
+
9
+ MediaPulse uses the `ruby_llm` gem as a unified interface for multi-provider LLM access. The architecture separates concerns into configuration, client factory, and domain-specific services.
10
+
11
+ ---
12
+
13
+ ## Architecture Layers
14
+
15
+ ### 1. Configuration (`config/initializers/ruby_llm.rb`)
16
+ Global setup for ruby_llm gem with API keys and defaults.
17
+
18
+ ```ruby
19
+ # Pattern: Configure in initializer, credentials from Rails.credentials
20
+ RubyLLM.configure do |config|
21
+ config.anthropic_api_key = Rails.application.credentials.dig(:anthropic, :api_key)
22
+ config.openai_api_key = Rails.application.credentials.dig(:openai, :api_key)
23
+ config.default_model = "claude-sonnet-4-20250514"
24
+ config.max_retries = 3
25
+ config.retry_interval = 0.5
26
+ config.retry_backoff_factor = 2
27
+ config.request_timeout = 120
28
+ end
29
+ ```
30
+
31
+ ### 2. Client Factory (`app/services/llm_client_factory.rb`)
32
+ Provider abstraction with task-specific model selection.
33
+
34
+ ```ruby
35
+ # Pattern: Factory methods for different use cases
36
+ LlmClientFactory.extraction_client # Claude Haiku - fast, cost-effective
37
+ LlmClientFactory.generation_client # Claude Sonnet - creative, quality
38
+ ```
39
+
40
+ ### 3. Service Layer (`app/services/llm_service.rb`)
41
+ Unified interface for AI operations with error mapping.
42
+
43
+ ```ruby
44
+ # Pattern: Domain methods with consistent return types
45
+ LlmService.generate_ideas(prompt:, system_prompt:, model:)
46
+ LlmService.generate_content(prompt:, system_prompt:, model:)
47
+ LlmService.stream(prompt:, system_prompt:, model:, &block)
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Model Selection Strategy
53
+
54
+ | Use Case | Model | Rationale |
55
+ |----------|-------|-----------|
56
+ | **Extraction** (semantic, entities) | Claude Haiku | Fast, cost-effective for structured extraction |
57
+ | **Generation** (ideas, content) | Claude Sonnet | Higher quality for creative tasks |
58
+ | **Embeddings** | OpenAI text-embedding-3-small | Cost-effective vector generation |
59
+
60
+ ```ruby
61
+ # Constants for model IDs (avoid magic strings)
62
+ HAIKU_MODEL = "claude-haiku-3-5-20241022"
63
+ SONNET_MODEL = "claude-sonnet-4-20250514"
64
+ ```
65
+
66
+ ---
67
+
68
+ ## Response Handling Pattern
69
+
70
+ Use immutable Data objects for LLM responses:
71
+
72
+ ```ruby
73
+ # Pattern: Immutable value objects for responses
74
+ LlmResponse = Data.define(:content, :model, :input_tokens, :output_tokens)
75
+
76
+ # Usage in service
77
+ def build_response(response)
78
+ LlmResponse.new(
79
+ content: response.content,
80
+ model: response.model_id,
81
+ input_tokens: response.input_tokens,
82
+ output_tokens: response.output_tokens
83
+ )
84
+ end
85
+ ```
86
+
87
+ ---
88
+
89
+ ## Prompt Engineering Patterns
90
+
91
+ ### System Prompts
92
+ Define persona and output format constraints:
93
+
94
+ ```ruby
95
+ # Pattern: Constants for reusable system prompts
96
+ SYSTEM_PROMPT = <<~PROMPT
97
+ You are an expert content strategist. Analyze the provided source material and generate
98
+ compelling content ideas. For each idea, provide:
99
+ 1. A clear, engaging title
100
+ 2. A brief summary (2-3 sentences)
101
+ 3. The source indices that support this idea
102
+
103
+ Return your response as JSON array with objects containing: title, summary, source_indices
104
+ PROMPT
105
+ ```
106
+
107
+ ### JSON Output Requests
108
+ Request structured output with explicit format:
109
+
110
+ ```ruby
111
+ # Pattern: Explicit JSON schema in prompt
112
+ def build_prompt(content, source_type)
113
+ <<~PROMPT
114
+ Analyze the following #{source_type} content and extract semantic information.
115
+
116
+ Return a JSON object with exactly these fields:
117
+ - summary: A 2-3 sentence summary
118
+ - entities: Array of objects with "name" and "type" keys
119
+ - claims: Array of strings representing key assertions
120
+ - confidence: A number between 0.0 and 1.0
121
+
122
+ Respond with ONLY valid JSON, no markdown formatting or explanation.
123
+
124
+ CONTENT:
125
+ #{content}
126
+ PROMPT
127
+ end
128
+ ```
129
+
130
+ ### Content Truncation
131
+ Handle token limits gracefully:
132
+
133
+ ```ruby
134
+ # Pattern: Truncate before sending to LLM
135
+ MAX_CONTENT_TOKENS = 8000
136
+ CHARS_PER_TOKEN = 4
137
+
138
+ def truncate_content(content)
139
+ max_chars = MAX_CONTENT_TOKENS * CHARS_PER_TOKEN
140
+ return content if content.length <= max_chars
141
+ content[0, max_chars] + "\n\n[Content truncated for processing]"
142
+ end
143
+ ```
144
+
145
+ ---
146
+
147
+ ## JSON Response Parsing
148
+
149
+ LLMs may wrap JSON in markdown code fences:
150
+
151
+ ```ruby
152
+ # Pattern: Extract JSON from potential markdown wrapping
153
+ def extract_json(content)
154
+ content = content.strip
155
+ if content.start_with?("```")
156
+ content = content.sub(/\A```\w*\n?/, "")
157
+ content = content.sub(/\n?```\z/, "")
158
+ end
159
+ content.strip
160
+ end
161
+
162
+ # Pattern: Graceful fallback for parsing failures
163
+ def parse_response(response_content)
164
+ json_content = extract_json(response_content)
165
+ parsed = JSON.parse(json_content)
166
+ return {} unless parsed.is_a?(Hash)
167
+ parsed.transform_keys(&:to_sym)
168
+ rescue JSON::ParserError => e
169
+ Rails.logger.warn("Failed to parse JSON response: #{e.message}")
170
+ {}
171
+ end
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Error Handling
177
+
178
+ ### Error Hierarchy
179
+ Map ruby_llm errors to domain-specific errors:
180
+
181
+ ```ruby
182
+ # Pattern: Custom error hierarchy
183
+ class LlmService
184
+ class Error < StandardError; end
185
+ class ConfigurationError < Error; end
186
+ class AuthenticationError < Error; end
187
+ class RateLimitError < Error; end
188
+ class ApiError < Error; end
189
+ end
190
+
191
+ # Pattern: Map provider errors to domain errors
192
+ rescue RubyLLM::UnauthorizedError => e
193
+ raise AuthenticationError, e.message
194
+ rescue RubyLLM::RateLimitError => e
195
+ raise RateLimitError, e.message
196
+ rescue RubyLLM::ConfigurationError => e
197
+ raise ConfigurationError, e.message
198
+ rescue RubyLLM::Error => e
199
+ raise ApiError, e.message
200
+ ```
201
+
202
+ ### Validation Before API Calls
203
+
204
+ ```ruby
205
+ # Pattern: Fail fast with clear messages
206
+ def validate_api_key!
207
+ api_key = Rails.application.credentials.dig(:anthropic, :api_key)
208
+ return if api_key.present?
209
+
210
+ raise ConfigurationError,
211
+ "Anthropic API key not configured. Add to credentials: anthropic.api_key"
212
+ end
213
+ ```
214
+
215
+ ---
216
+
217
+ ## Testing LLM Services
218
+
219
+ ### Mocking ruby_llm Calls
220
+
221
+ ```ruby
222
+ # Pattern: Stub RubyLLM.chat for unit tests
223
+ test "generate_ideas returns LlmResponse" do
224
+ mock_response = Minitest::Mock.new
225
+ mock_response.expect :content, "Generated content"
226
+ mock_response.expect :model_id, "claude-sonnet-4-20250514"
227
+ mock_response.expect :input_tokens, 150
228
+ mock_response.expect :output_tokens, 200
229
+
230
+ mock_chat = Minitest::Mock.new
231
+ mock_chat.expect :with_instructions, mock_chat, [String]
232
+ mock_chat.expect :ask, mock_response, [String]
233
+
234
+ RubyLLM.stub :chat, mock_chat do
235
+ result = LlmService.generate_ideas(prompt: "Test", system_prompt: "Expert")
236
+ assert_instance_of LlmResponse, result
237
+ end
238
+ end
239
+ ```
240
+
241
+ ### Stubbing Credentials in Tests
242
+
243
+ ```ruby
244
+ # Pattern: Stub credentials for test isolation
245
+ def stub_anthropic_api_key
246
+ credentials_mock = Class.new do
247
+ def dig(*keys)
248
+ keys == [:anthropic, :api_key] ? "test-api-key" : nil
249
+ end
250
+ end.new
251
+ Rails.application.instance_variable_set(:@credentials, credentials_mock)
252
+ end
253
+
254
+ def reset_credentials_stub
255
+ Rails.application.instance_variable_set(:@credentials, nil)
256
+ end
257
+ ```
258
+
259
+ ### Testing Error Mapping
260
+
261
+ ```ruby
262
+ # Pattern: Verify error mapping with mock errors
263
+ test "maps RubyLLM::RateLimitError to RateLimitError" do
264
+ error_response = OpenStruct.new(body: "Rate limit exceeded")
265
+ mock_chat = Minitest::Mock.new
266
+ mock_chat.expect :ask, nil do
267
+ raise RubyLLM::RateLimitError.new(error_response)
268
+ end
269
+
270
+ RubyLLM.stub :chat, mock_chat do
271
+ assert_raises(LlmService::RateLimitError) do
272
+ LlmService.generate_ideas(prompt: "Test")
273
+ end
274
+ end
275
+ end
276
+ ```
277
+
278
+ ---
279
+
280
+ ## Credential Management
281
+
282
+ Store API keys in Rails encrypted credentials:
283
+
284
+ ```bash
285
+ # Edit credentials
286
+ bin/rails credentials:edit
287
+
288
+ # Structure
289
+ anthropic:
290
+ api_key: sk-ant-...
291
+ openai:
292
+ api_key: sk-...
293
+ ```
294
+
295
+ Access pattern:
296
+ ```ruby
297
+ Rails.application.credentials.dig(:anthropic, :api_key)
298
+ ```
299
+
300
+ ---
301
+
302
+ ## Domain-Specific Services
303
+
304
+ Create focused services for specific AI tasks:
305
+
306
+ | Service | Purpose | Model |
307
+ |---------|---------|-------|
308
+ | `SemanticProcessing::SemanticExtractor` | Extract entities, claims, topics | Haiku |
309
+ | `IdeaGenerationService` | Generate content ideas from sources | Sonnet |
310
+ | `ContentGenerationService` | Create platform-specific content | Sonnet |
311
+ | `EmbeddingService` | Vector embeddings for similarity | OpenAI |
312
+
313
+ Each follows the pattern:
314
+ - Class method `call` for invocation
315
+ - Dependency injection for testability
316
+ - Result objects for outcomes
317
+ - Error propagation to caller
318
+
319
+ ---
320
+
321
+ ## Quick Reference
322
+
323
+ ### Creating a New LLM Service
324
+
325
+ 1. Define class in `app/services/`
326
+ 2. Use `LlmClientFactory` for client (extraction or generation)
327
+ 3. Build prompt with explicit JSON schema
328
+ 4. Parse response with JSON extraction helper
329
+ 5. Return immutable result object
330
+ 6. Map errors to domain-specific types
331
+
332
+ ### Adding Tests
333
+
334
+ 1. Stub `RubyLLM.chat` with mock
335
+ 2. Stub credentials if validating API keys
336
+ 3. Test success path with mock responses
337
+ 4. Test error paths with raised exceptions
338
+ 5. Verify response structure and types
339
+
340
+ ---
341
+
342
+ _Focus on patterns, not exhaustive API documentation. See ruby_llm gem docs for full API reference._
@@ -0,0 +1,301 @@
1
+ # Ruby on Rails Conventions
2
+
3
+ Project memory for Rails 8.1 patterns and conventions in MediaPulse.
4
+
5
+ ---
6
+
7
+ ## Framework Stack
8
+
9
+ ### Core Technologies
10
+ - **Rails 8.1** with `config.load_defaults 8.1`
11
+ - **Hotwire**: Turbo + Stimulus for reactive UI without heavy JavaScript
12
+ - **Propshaft**: Modern asset pipeline (not Sprockets)
13
+ - **Importmap**: ESM-based JavaScript, no bundler required
14
+ - **Solid Queue**: Database-backed job processing (replaces Sidekiq/Redis)
15
+ - **Solid Cache**: Database-backed caching
16
+ - **Kamal**: Docker-based deployment
17
+
18
+ ### Database
19
+ - **SQLite3** for development (production-ready with proper config)
20
+ - Separate databases for queue, cache, and cable (see `db/*_schema.rb`)
21
+
22
+ ---
23
+
24
+ ## Application Architecture
25
+
26
+ ### MVC Patterns
27
+
28
+ **Models** (`app/models/`)
29
+ - Inherit from `ApplicationRecord` (abstract base class)
30
+ - Keep models focused on data and associations
31
+ - Use validations, scopes, and callbacks appropriately
32
+ - Extract complex business logic to service objects
33
+
34
+ ```ruby
35
+ # Pattern: Lean models with clear responsibilities
36
+ class Content < ApplicationRecord
37
+ belongs_to :user
38
+ has_many :versions, dependent: :destroy
39
+
40
+ validates :title, presence: true
41
+ validates :status, inclusion: { in: %w[draft published] }
42
+
43
+ scope :published, -> { where(status: "published") }
44
+ scope :by_user, ->(user) { where(user: user) }
45
+ end
46
+ ```
47
+
48
+ **Controllers** (`app/controllers/`)
49
+ - Inherit from `ApplicationController`
50
+ - Use `allow_browser versions: :modern` (Rails 8.1 default)
51
+ - Keep actions thin, delegate to models/services
52
+ - Use strong parameters for all user input
53
+
54
+ ```ruby
55
+ # Pattern: RESTful actions with strong parameters
56
+ class ContentsController < ApplicationController
57
+ def create
58
+ @content = Current.user.contents.build(content_params)
59
+ if @content.save
60
+ redirect_to @content, notice: "Created successfully"
61
+ else
62
+ render :new, status: :unprocessable_entity
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def content_params
69
+ params.require(:content).permit(:title, :body, :status)
70
+ end
71
+ end
72
+ ```
73
+
74
+ **Views** (`app/views/`)
75
+ - Use Hotwire/Turbo for dynamic updates
76
+ - Prefer partials for reusable components
77
+ - Use Stimulus controllers for JavaScript behavior
78
+
79
+ ---
80
+
81
+ ## Business Logic Patterns
82
+
83
+ ### Service Objects
84
+ Place complex operations in `app/services/`. Use when logic spans multiple models or involves external services.
85
+
86
+ ```ruby
87
+ # app/services/content_generator.rb
88
+ class ContentGenerator
89
+ def initialize(user:, prompt:)
90
+ @user = user
91
+ @prompt = prompt
92
+ end
93
+
94
+ def call
95
+ # Complex AI generation logic
96
+ Result.new(success: true, content: generated_content)
97
+ end
98
+
99
+ private
100
+
101
+ attr_reader :user, :prompt
102
+ end
103
+ ```
104
+
105
+ ### Query Objects
106
+ For complex queries, use `app/queries/` or model scopes.
107
+
108
+ ```ruby
109
+ # Pattern: Chainable scopes over query objects for simple cases
110
+ Content.published.by_user(user).where("created_at > ?", 1.week.ago)
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Background Jobs
116
+
117
+ ### Solid Queue Conventions
118
+ - Jobs inherit from `ApplicationJob`
119
+ - Configure adapter in production: `config.active_job.queue_adapter = :solid_queue`
120
+ - Use appropriate queue names for priority
121
+
122
+ ```ruby
123
+ # app/jobs/generate_content_job.rb
124
+ class GenerateContentJob < ApplicationJob
125
+ queue_as :default
126
+
127
+ retry_on StandardError, wait: :polynomially_longer, attempts: 3
128
+ discard_on ActiveJob::DeserializationError
129
+
130
+ def perform(content_id)
131
+ content = Content.find(content_id)
132
+ ContentGenerator.new(content: content).call
133
+ end
134
+ end
135
+ ```
136
+
137
+ ---
138
+
139
+ ## API Patterns
140
+
141
+ ### JSON Responses
142
+ Use Jbuilder for JSON APIs. Keep API logic in dedicated controllers.
143
+
144
+ ```ruby
145
+ # app/controllers/api/v1/base_controller.rb
146
+ module Api
147
+ module V1
148
+ class BaseController < ApplicationController
149
+ skip_before_action :verify_authenticity_token
150
+ before_action :authenticate_api_request
151
+ end
152
+ end
153
+ end
154
+ ```
155
+
156
+ ### Turbo Streams
157
+ For real-time updates within the app, prefer Turbo Streams over custom APIs.
158
+
159
+ ```ruby
160
+ # Pattern: Broadcast updates via Turbo
161
+ respond_to do |format|
162
+ format.html { redirect_to @content }
163
+ format.turbo_stream
164
+ end
165
+ ```
166
+
167
+ ---
168
+
169
+ ## Database Conventions
170
+
171
+ ### Migrations
172
+ - Use descriptive, timestamped migration names
173
+ - Always include `null: false` for required fields
174
+ - Add indexes for foreign keys and frequently queried columns
175
+ - Use `references` with `foreign_key: true`
176
+
177
+ ```ruby
178
+ # Pattern: Complete migration with constraints
179
+ class CreateContents < ActiveRecord::Migration[8.1]
180
+ def change
181
+ create_table :contents do |t|
182
+ t.references :user, null: false, foreign_key: true
183
+ t.string :title, null: false
184
+ t.text :body
185
+ t.string :status, null: false, default: "draft"
186
+ t.timestamps
187
+ end
188
+
189
+ add_index :contents, [:user_id, :status]
190
+ end
191
+ end
192
+ ```
193
+
194
+ ### Schema
195
+ - Schema file is `db/schema.rb` (default)
196
+ - Separate schema files for queue/cache/cable databases
197
+
198
+ ---
199
+
200
+ ## Testing Approach
201
+
202
+ ### Minitest (Default)
203
+ - Tests in `test/` directory
204
+ - Use fixtures for test data
205
+ - Parallel test execution enabled
206
+
207
+ ```ruby
208
+ # Pattern: Model test with fixtures
209
+ class ContentTest < ActiveSupport::TestCase
210
+ test "validates title presence" do
211
+ content = Content.new(title: nil)
212
+ assert_not content.valid?
213
+ assert_includes content.errors[:title], "can't be blank"
214
+ end
215
+ end
216
+ ```
217
+
218
+ ### System Tests
219
+ - Use Capybara with Selenium
220
+ - Test user flows end-to-end
221
+
222
+ ```ruby
223
+ # test/system/contents_test.rb
224
+ class ContentsTest < ApplicationSystemTestCase
225
+ test "creating a content" do
226
+ visit new_content_path
227
+ fill_in "Title", with: "Test Content"
228
+ click_on "Create"
229
+ assert_text "Created successfully"
230
+ end
231
+ end
232
+ ```
233
+
234
+ ---
235
+
236
+ ## Security Practices
237
+
238
+ ### Built-in Protections
239
+ - CSRF protection enabled by default
240
+ - Strong parameters required
241
+ - Parameter filtering for sensitive data (see `filter_parameter_logging.rb`)
242
+
243
+ ### Credentials
244
+ - Use `bin/rails credentials:edit` for secrets
245
+ - Never commit unencrypted secrets
246
+ - Access via `Rails.application.credentials.dig(:key, :subkey)`
247
+
248
+ ### Security Tools
249
+ - **Brakeman**: Static security analysis (`bundle exec brakeman`)
250
+ - **bundler-audit**: Gem vulnerability scanning
251
+
252
+ ---
253
+
254
+ ## Code Style
255
+
256
+ ### Rubocop Configuration
257
+ - Using `rubocop-rails-omakase` (Rails default style)
258
+ - Run with `bundle exec rubocop`
259
+
260
+ ### Naming Conventions
261
+ - Models: singular, CamelCase (`Content`, `UserProfile`)
262
+ - Controllers: plural, CamelCase + Controller (`ContentsController`)
263
+ - Tables: plural, snake_case (`contents`, `user_profiles`)
264
+ - Jobs: descriptive + Job (`GenerateContentJob`)
265
+ - Services: action-oriented (`ContentGenerator`, `WorkflowExecutor`)
266
+
267
+ ---
268
+
269
+ ## Performance
270
+
271
+ ### Caching
272
+ - Fragment caching with Solid Cache in production
273
+ - Use `cache` helper in views for expensive partials
274
+ - Russian doll caching with `touch: true` on associations
275
+
276
+ ### N+1 Prevention
277
+ - Use `includes` for eager loading
278
+ - Monitor with bullet gem in development (optional)
279
+
280
+ ```ruby
281
+ # Pattern: Eager load associations
282
+ Content.includes(:user, :versions).published
283
+ ```
284
+
285
+ ---
286
+
287
+ ## Directory Conventions
288
+
289
+ ```
290
+ app/
291
+ controllers/ # Request handling
292
+ models/ # ActiveRecord models
293
+ views/ # ERB templates + Turbo Streams
294
+ jobs/ # Background jobs (Solid Queue)
295
+ services/ # Business logic (create as needed)
296
+ helpers/ # View helpers
297
+ javascript/ # Stimulus controllers
298
+ mailers/ # Email delivery
299
+ ```
300
+
301
+ Extend with `app/services/`, `app/queries/` as complexity grows.