red64-cli 0.3.0 → 0.6.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 (95) hide show
  1. package/README.md +194 -338
  2. package/dist/cli/parseArgs.d.ts.map +1 -1
  3. package/dist/cli/parseArgs.js +5 -13
  4. package/dist/cli/parseArgs.js.map +1 -1
  5. package/dist/components/init/types.d.ts +0 -2
  6. package/dist/components/init/types.d.ts.map +1 -1
  7. package/dist/components/screens/HelpScreen.d.ts.map +1 -1
  8. package/dist/components/screens/HelpScreen.js +0 -2
  9. package/dist/components/screens/HelpScreen.js.map +1 -1
  10. package/dist/components/screens/InitScreen.d.ts.map +1 -1
  11. package/dist/components/screens/InitScreen.js +5 -8
  12. package/dist/components/screens/InitScreen.js.map +1 -1
  13. package/dist/components/screens/StartScreen.d.ts.map +1 -1
  14. package/dist/components/screens/StartScreen.js +29 -8
  15. package/dist/components/screens/StartScreen.js.map +1 -1
  16. package/dist/components/screens/StatusScreen.d.ts.map +1 -1
  17. package/dist/components/screens/StatusScreen.js +16 -1
  18. package/dist/components/screens/StatusScreen.js.map +1 -1
  19. package/dist/services/AgentInvoker.d.ts.map +1 -1
  20. package/dist/services/AgentInvoker.js +76 -37
  21. package/dist/services/AgentInvoker.js.map +1 -1
  22. package/dist/services/ClaudeErrorDetector.d.ts +1 -1
  23. package/dist/services/ClaudeErrorDetector.d.ts.map +1 -1
  24. package/dist/services/ClaudeErrorDetector.js +1 -0
  25. package/dist/services/ClaudeErrorDetector.js.map +1 -1
  26. package/dist/services/ClaudeHealthCheck.d.ts +7 -0
  27. package/dist/services/ClaudeHealthCheck.d.ts.map +1 -1
  28. package/dist/services/ClaudeHealthCheck.js +76 -12
  29. package/dist/services/ClaudeHealthCheck.js.map +1 -1
  30. package/dist/services/ConfigService.d.ts +1 -0
  31. package/dist/services/ConfigService.d.ts.map +1 -1
  32. package/dist/services/ConfigService.js.map +1 -1
  33. package/dist/services/DockerRunner.js +1 -1
  34. package/dist/services/DockerRunner.js.map +1 -1
  35. package/dist/services/PhaseExecutor.d.ts.map +1 -1
  36. package/dist/services/PhaseExecutor.js +2 -1
  37. package/dist/services/PhaseExecutor.js.map +1 -1
  38. package/dist/services/TaskRunner.d.ts.map +1 -1
  39. package/dist/services/TaskRunner.js +2 -1
  40. package/dist/services/TaskRunner.js.map +1 -1
  41. package/dist/services/index.d.ts +1 -1
  42. package/dist/services/index.d.ts.map +1 -1
  43. package/dist/services/index.js +1 -1
  44. package/dist/services/index.js.map +1 -1
  45. package/dist/types/index.d.ts +4 -3
  46. package/dist/types/index.d.ts.map +1 -1
  47. package/dist/types/index.js.map +1 -1
  48. package/framework/stacks/c/code-quality.md +326 -0
  49. package/framework/stacks/c/coding-style.md +347 -0
  50. package/framework/stacks/c/conventions.md +513 -0
  51. package/framework/stacks/c/error-handling.md +350 -0
  52. package/framework/stacks/c/feedback.md +158 -0
  53. package/framework/stacks/c/memory-safety.md +408 -0
  54. package/framework/stacks/c/tech.md +122 -0
  55. package/framework/stacks/c/testing.md +472 -0
  56. package/framework/stacks/cpp/code-quality.md +282 -0
  57. package/framework/stacks/cpp/coding-style.md +363 -0
  58. package/framework/stacks/cpp/conventions.md +420 -0
  59. package/framework/stacks/cpp/error-handling.md +264 -0
  60. package/framework/stacks/cpp/feedback.md +104 -0
  61. package/framework/stacks/cpp/memory-safety.md +351 -0
  62. package/framework/stacks/cpp/tech.md +160 -0
  63. package/framework/stacks/cpp/testing.md +323 -0
  64. package/framework/stacks/java/code-quality.md +357 -0
  65. package/framework/stacks/java/coding-style.md +400 -0
  66. package/framework/stacks/java/conventions.md +437 -0
  67. package/framework/stacks/java/error-handling.md +408 -0
  68. package/framework/stacks/java/feedback.md +180 -0
  69. package/framework/stacks/java/tech.md +126 -0
  70. package/framework/stacks/java/testing.md +485 -0
  71. package/framework/stacks/javascript/async-patterns.md +216 -0
  72. package/framework/stacks/javascript/code-quality.md +182 -0
  73. package/framework/stacks/javascript/coding-style.md +293 -0
  74. package/framework/stacks/javascript/conventions.md +268 -0
  75. package/framework/stacks/javascript/error-handling.md +216 -0
  76. package/framework/stacks/javascript/feedback.md +80 -0
  77. package/framework/stacks/javascript/tech.md +114 -0
  78. package/framework/stacks/javascript/testing.md +209 -0
  79. package/framework/stacks/loco/code-quality.md +156 -0
  80. package/framework/stacks/loco/coding-style.md +247 -0
  81. package/framework/stacks/loco/error-handling.md +225 -0
  82. package/framework/stacks/loco/feedback.md +35 -0
  83. package/framework/stacks/loco/loco.md +342 -0
  84. package/framework/stacks/loco/structure.md +193 -0
  85. package/framework/stacks/loco/tech.md +129 -0
  86. package/framework/stacks/loco/testing.md +211 -0
  87. package/framework/stacks/rust/code-quality.md +370 -0
  88. package/framework/stacks/rust/coding-style.md +475 -0
  89. package/framework/stacks/rust/conventions.md +430 -0
  90. package/framework/stacks/rust/error-handling.md +399 -0
  91. package/framework/stacks/rust/feedback.md +152 -0
  92. package/framework/stacks/rust/memory-safety.md +398 -0
  93. package/framework/stacks/rust/tech.md +121 -0
  94. package/framework/stacks/rust/testing.md +528 -0
  95. package/package.json +14 -2
@@ -0,0 +1,225 @@
1
+ # Error Handling
2
+
3
+ Error handling patterns for Loco applications. Leverage Rust's type system and Loco's error infrastructure for predictable, user-friendly error responses.
4
+
5
+ ---
6
+
7
+ ## Philosophy
8
+
9
+ - **Use `Result<T>` everywhere**: No panics in production code
10
+ - **`?` operator for propagation**: Flat, readable error chains
11
+ - **Typed errors for domain logic**: Custom error enums for business rules
12
+ - **Consistent HTTP error responses**: Map errors to appropriate status codes
13
+ - **Log context, not secrets**: Structured logging with tracing spans
14
+
15
+ ---
16
+
17
+ ## Loco Error Types
18
+
19
+ ### Built-in Error Hierarchy
20
+
21
+ Loco provides `loco_rs::Error` as the primary error type. Controllers return `Result<impl IntoResponse>` which auto-maps errors to HTTP responses.
22
+
23
+ ```rust
24
+ use loco_rs::prelude::*;
25
+
26
+ // Loco maps these automatically:
27
+ // - ModelError::EntityNotFound -> 404
28
+ // - Error::Unauthorized -> 401
29
+ // - Error::BadRequest -> 400
30
+ // - Error::InternalServerError -> 500
31
+ ```
32
+
33
+ ### Model Errors
34
+
35
+ Use `ModelResult<T>` for model-layer operations:
36
+
37
+ ```rust
38
+ impl super::_entities::users::ActiveModel {
39
+ pub async fn find_by_email(db: &DatabaseConnection, email: &str) -> ModelResult<Self> {
40
+ let user = users::Entity::find()
41
+ .filter(users::Column::Email.eq(email))
42
+ .one(db)
43
+ .await?;
44
+ user.ok_or_else(|| ModelError::EntityNotFound)
45
+ }
46
+ }
47
+ ```
48
+
49
+ ---
50
+
51
+ ## Controller Error Handling
52
+
53
+ ### Pattern: Early Returns with `?`
54
+
55
+ ```rust
56
+ // GOOD: Flat, each line can fail independently
57
+ async fn update(
58
+ auth: auth::JWT,
59
+ State(ctx): State<AppContext>,
60
+ Path(id): Path<i32>,
61
+ Json(params): Json<UpdatePostParams>,
62
+ ) -> Result<Json<PostResponse>> {
63
+ let user = users::Model::find_by_pid(&ctx.db, &auth.claims.pid).await?;
64
+ let post = posts::Model::find_by_id_and_user(&ctx.db, id, user.id).await?;
65
+ let updated = post.update(&ctx.db, &params).await?;
66
+ Ok(Json(PostResponse::from(updated)))
67
+ }
68
+
69
+ // BAD: Nested error handling
70
+ async fn update(/* ... */) -> Result<Json<PostResponse>> {
71
+ match users::Model::find_by_pid(&ctx.db, &auth.claims.pid).await {
72
+ Ok(user) => match posts::Model::find_by_id(&ctx.db, id).await {
73
+ Ok(Some(post)) => {
74
+ if post.user_id == user.id {
75
+ // deeply nested...
76
+ }
77
+ }
78
+ // more nesting...
79
+ }
80
+ }
81
+ }
82
+ ```
83
+
84
+ ### Custom Error Responses
85
+
86
+ For domain-specific errors, return explicit HTTP responses:
87
+
88
+ ```rust
89
+ async fn publish(
90
+ State(ctx): State<AppContext>,
91
+ Path(id): Path<i32>,
92
+ ) -> Result<Json<PostResponse>> {
93
+ let post = posts::Entity::find_by_id(id)
94
+ .one(&ctx.db)
95
+ .await?
96
+ .ok_or_else(|| Error::NotFound)?;
97
+
98
+ if post.status == "published" {
99
+ return Err(Error::BadRequest("Post is already published".into()));
100
+ }
101
+
102
+ let published = post.publish(&ctx.db).await?;
103
+ Ok(Json(PostResponse::from(published)))
104
+ }
105
+ ```
106
+
107
+ ---
108
+
109
+ ## Validation Errors
110
+
111
+ ### Using `validator` Crate
112
+
113
+ Return structured validation errors with field-level detail:
114
+
115
+ ```rust
116
+ use loco_rs::controller::views::json_validate::JsonValidateWithMessage;
117
+
118
+ async fn create(
119
+ State(ctx): State<AppContext>,
120
+ JsonValidateWithMessage(params): JsonValidateWithMessage<CreatePostParams>,
121
+ ) -> Result<Json<PostResponse>> {
122
+ // Validation happens automatically via extractor
123
+ // Returns 422 with field-level errors on failure
124
+ let post = posts::ActiveModel::create(&ctx.db, &params).await?;
125
+ Ok(Json(PostResponse::from(post)))
126
+ }
127
+ ```
128
+
129
+ ### Model-Level Validation
130
+
131
+ ```rust
132
+ impl Validatable for super::_entities::posts::ActiveModel {
133
+ fn validator(&self) -> Box<dyn Validate> {
134
+ Box::new(PostValidator {
135
+ title: self.title.clone().into_value().unwrap_or_default(),
136
+ })
137
+ }
138
+ }
139
+
140
+ // In model method:
141
+ pub async fn create(db: &DatabaseConnection, params: &CreatePostParams) -> ModelResult<Model> {
142
+ let mut item = ActiveModel { ..Default::default() };
143
+ item.title = Set(params.title.clone());
144
+ item.validate()?; // Returns validation errors before hitting DB
145
+ item.insert(db).await.map_err(|e| ModelError::from(e))
146
+ }
147
+ ```
148
+
149
+ ---
150
+
151
+ ## Worker Error Handling
152
+
153
+ Workers should handle errors gracefully and log context:
154
+
155
+ ```rust
156
+ async fn perform(&self, args: ReportArgs) -> Result<()> {
157
+ let user = users::Entity::find_by_id(args.user_id)
158
+ .one(&self.ctx.db)
159
+ .await?
160
+ .ok_or_else(|| {
161
+ tracing::error!(user_id = args.user_id, "User not found for report");
162
+ Error::NotFound
163
+ })?;
164
+
165
+ match generate_report(&user).await {
166
+ Ok(report) => {
167
+ tracing::info!(user_id = args.user_id, "Report generated");
168
+ Ok(())
169
+ }
170
+ Err(e) => {
171
+ tracing::error!(user_id = args.user_id, error = %e, "Report generation failed");
172
+ Err(e.into())
173
+ }
174
+ }
175
+ }
176
+ ```
177
+
178
+ ---
179
+
180
+ ## Logging with Context
181
+
182
+ ### Structured Tracing
183
+
184
+ ```rust
185
+ use tracing;
186
+
187
+ // GOOD: Structured fields for queryability
188
+ tracing::info!(user_id = %user.id, action = "login", "User authenticated");
189
+ tracing::error!(post_id = id, error = %e, "Failed to publish post");
190
+
191
+ // BAD: Unstructured string interpolation
192
+ tracing::info!("User {} logged in", user.id);
193
+ ```
194
+
195
+ ### Span Context in Controllers
196
+
197
+ ```rust
198
+ async fn create(
199
+ State(ctx): State<AppContext>,
200
+ Json(params): Json<CreatePostParams>,
201
+ ) -> Result<Json<PostResponse>> {
202
+ let _span = tracing::info_span!("create_post", title = %params.title).entered();
203
+ // All logs within this scope include the span context
204
+ let post = posts::ActiveModel::create(&ctx.db, &params).await?;
205
+ Ok(Json(PostResponse::from(post)))
206
+ }
207
+ ```
208
+
209
+ ---
210
+
211
+ ## Anti-Patterns
212
+
213
+ | Anti-Pattern | Problem | Correct Approach |
214
+ |---|---|---|
215
+ | `.unwrap()` in handlers | Panics crash the request | Use `?` or `.ok_or_else()` |
216
+ | Swallowing errors silently | Hides bugs, makes debugging impossible | Log and propagate with `?` |
217
+ | Generic "Internal Error" for everything | Poor DX for API consumers | Map to specific HTTP status codes |
218
+ | Logging sensitive data | Security risk | Use structured fields, redact secrets |
219
+ | Validation in controllers | Logic duplication across endpoints | Use `Validatable` trait on models |
220
+ | `Box<dyn Error>` as return type | Loses type information | Use Loco's `Error` / `ModelError` types |
221
+ | Ignoring worker failures | Silent data loss | Log errors with context, use retry policies |
222
+
223
+ ---
224
+
225
+ _Rust's type system makes error handling explicit. Use `?` for propagation, typed errors for domain logic, and structured tracing for observability._
@@ -0,0 +1,35 @@
1
+ # Feedback Guidelines
2
+
3
+ When reviewing or generating code for a Loco application, apply these checks.
4
+
5
+ ---
6
+
7
+ ## Always Check
8
+
9
+ 1. **Fat models, slim controllers**: Is business logic in models, not controllers?
10
+ 2. **Generator compliance**: Were new files created via `cargo loco generate`?
11
+ 3. **Entity files untouched**: Are `_entities/` files unmodified?
12
+ 4. **Config per environment**: Are dangerous flags disabled in production config?
13
+ 5. **No `.unwrap()` in handlers**: All error paths handled with `?`?
14
+ 6. **Worker isolation**: Workers self-contained with serializable args?
15
+ 7. **Validation on models**: `Validatable` implemented for user-input models?
16
+ 8. **View structs for responses**: Not returning raw SeaORM entities?
17
+
18
+ ## Red Flags
19
+
20
+ - Direct SQL in controllers
21
+ - `dangerously_*` flags in `config/production.yaml`
22
+ - Editing files in `src/models/_entities/`
23
+ - Workers referencing controller state
24
+ - Missing `down()` in migrations
25
+ - Hardcoded configuration values
26
+ - `.unwrap()` or `.expect()` in request handlers
27
+
28
+ ## Encourage
29
+
30
+ - Using generators for all scaffolding
31
+ - Domain methods on models with descriptive names
32
+ - Structured tracing with typed fields
33
+ - Snapshot testing with `insta`
34
+ - Request tests for all controller endpoints
35
+ - Tagged workers for job categorization
@@ -0,0 +1,342 @@
1
+ # Loco Framework Conventions
2
+
3
+ Conventions and patterns for Loco -- the Rust on Rails framework. Convention over configuration; decisions are made for you.
4
+
5
+ ---
6
+
7
+ ## Framework Stack
8
+
9
+ ### Core Technologies
10
+ - **Loco**: Rails-inspired Rust web framework
11
+ - **Axum**: HTTP routing and Tower middleware
12
+ - **SeaORM**: Async ActiveRecord-style ORM
13
+ - **Tokio**: Async runtime
14
+ - **sidekiq-rs / Tokio**: Background workers (Redis-backed or in-process)
15
+ - **JWT**: Stateless authentication (built-in)
16
+
17
+ ### Database
18
+ - **PostgreSQL** for production (recommended)
19
+ - **SQLite** for development and lightweight deployments
20
+ - **MySQL** supported
21
+
22
+ ---
23
+
24
+ ## Application Architecture
25
+
26
+ ### MVC Patterns
27
+
28
+ **Models** (`src/models/`)
29
+ - Fat models, slim controllers: domain logic lives on models
30
+ - Two-file pattern: `_entities/model.rs` (auto-generated, read-only) + `model.rs` (your ActiveRecord logic)
31
+ - Implement domain operations as methods on `ActiveModel`
32
+ - `User::create` creates a user; `user.buy(product)` buys a product
33
+
34
+ ```rust
35
+ // Pattern: Domain logic on the model
36
+ impl super::_entities::users::ActiveModel {
37
+ pub async fn find_by_email(db: &DatabaseConnection, email: &str) -> ModelResult<Self> {
38
+ let user = users::Entity::find()
39
+ .filter(users::Column::Email.eq(email))
40
+ .one(db)
41
+ .await?;
42
+ user.ok_or_else(|| ModelError::EntityNotFound)
43
+ }
44
+
45
+ pub async fn verify_password(&self, password: &str) -> ModelResult<bool> {
46
+ Ok(hash::verify_password(password, &self.password_hash))
47
+ }
48
+ }
49
+ ```
50
+
51
+ **Controllers** (`src/controllers/`)
52
+ - Thin controllers: validate input, call model, return view
53
+ - Use Axum extractors for request data
54
+ - Return `Result<impl IntoResponse>` consistently
55
+ - Group routes by resource with `.prefix()`
56
+
57
+ ```rust
58
+ // Pattern: Thin controller delegating to model
59
+ async fn create(
60
+ State(ctx): State<AppContext>,
61
+ Json(params): Json<CreatePostParams>,
62
+ ) -> Result<Json<PostResponse>> {
63
+ let post = models::posts::ActiveModel::create(&ctx.db, &params).await?;
64
+ Ok(Json(PostResponse::from(post)))
65
+ }
66
+
67
+ pub fn routes() -> Routes {
68
+ Routes::new()
69
+ .prefix("posts")
70
+ .add("/", post(create))
71
+ .add("/", get(list))
72
+ .add("/:id", get(show))
73
+ .add("/:id", put(update))
74
+ .add("/:id", delete(destroy))
75
+ }
76
+ ```
77
+
78
+ **Views** (`src/views/`)
79
+ - JSON serialization structs for API responses
80
+ - Keep response shaping separate from models
81
+ - Use `serde::Serialize` derive
82
+
83
+ ```rust
84
+ // Pattern: View struct for API response
85
+ #[derive(Serialize)]
86
+ pub struct PostResponse {
87
+ pub id: i32,
88
+ pub title: String,
89
+ pub content: String,
90
+ pub created_at: DateTime,
91
+ }
92
+
93
+ impl From<posts::Model> for PostResponse {
94
+ fn from(post: posts::Model) -> Self {
95
+ Self {
96
+ id: post.id,
97
+ title: post.title,
98
+ content: post.content,
99
+ created_at: post.created_at,
100
+ }
101
+ }
102
+ }
103
+ ```
104
+
105
+ ---
106
+
107
+ ## Business Logic Patterns
108
+
109
+ ### Fat Models
110
+ Place domain operations on models. This enables reuse across controllers, workers, and tasks:
111
+
112
+ ```rust
113
+ // src/models/users.rs
114
+ impl super::_entities::users::ActiveModel {
115
+ pub async fn register(db: &DatabaseConnection, params: &RegisterParams) -> ModelResult<Self> {
116
+ // Validation, password hashing, email verification token
117
+ // All in one place, testable via model tests
118
+ }
119
+
120
+ pub async fn reset_password(db: &DatabaseConnection, token: &str, new_password: &str) -> ModelResult<()> {
121
+ // Token verification, password update
122
+ }
123
+ }
124
+ ```
125
+
126
+ ### When to Extract Services
127
+ Extract standalone service modules only when logic:
128
+ - Spans multiple unrelated models
129
+ - Integrates external APIs
130
+ - Has no natural "home" model
131
+
132
+ Place in `src/services/` (create as needed).
133
+
134
+ ---
135
+
136
+ ## Background Workers
137
+
138
+ ### Worker Definition
139
+ Implement `BackgroundWorker` trait with `perform` function:
140
+
141
+ ```rust
142
+ #[async_trait]
143
+ impl BackgroundWorker<ReportArgs> for ReportWorker {
144
+ fn build(ctx: &AppContext) -> Self {
145
+ Self { ctx: ctx.clone() }
146
+ }
147
+
148
+ async fn perform(&self, args: ReportArgs) -> Result<()> {
149
+ // Job logic -- self-contained, no shared controller state
150
+ Ok(())
151
+ }
152
+ }
153
+ ```
154
+
155
+ ### Enqueue Jobs
156
+ ```rust
157
+ ReportWorker::perform_later(&ctx, ReportArgs { user_id: 42 }).await?;
158
+ ```
159
+
160
+ ### Worker Modes
161
+ - **BackgroundAsync**: In-process Tokio tasks (development, single-server)
162
+ - **BackgroundQueue**: Redis/Postgres/SQLite backed (production, horizontal scaling)
163
+ - **ForegroundBlocking**: Synchronous (testing only)
164
+
165
+ ### Anti-Pattern: Shared State
166
+ Workers must be self-contained. Do not share state between controllers and workers -- workers may run in separate processes.
167
+
168
+ ---
169
+
170
+ ## Tasks
171
+
172
+ CLI-invokable operations for administrative work:
173
+
174
+ ```rust
175
+ #[async_trait]
176
+ impl Task for SeedData {
177
+ fn task(&self) -> TaskInfo {
178
+ TaskInfo {
179
+ name: "seed_data".to_string(),
180
+ detail: "Seed the database with initial data".to_string(),
181
+ }
182
+ }
183
+
184
+ async fn run(&self, app_context: &AppContext, vars: &task::Vars) -> Result<()> {
185
+ // Business logic -- no manual DB access needed
186
+ Ok(())
187
+ }
188
+ }
189
+ ```
190
+
191
+ Run with: `cargo loco task seed_data`
192
+
193
+ ---
194
+
195
+ ## Authentication
196
+
197
+ ### Built-in JWT Auth (SaaS Starter)
198
+ Loco's SaaS starter includes complete auth flow:
199
+ - `POST /api/auth/register` -- registration with email verification
200
+ - `POST /api/auth/login` -- returns JWT token
201
+ - `POST /api/auth/forgot` / `POST /api/auth/reset` -- password reset
202
+ - `GET /api/auth/verify` -- email verification
203
+
204
+ ### Protecting Routes
205
+ Use `auth::JWT` extractor for authenticated endpoints:
206
+
207
+ ```rust
208
+ async fn current(
209
+ auth: auth::JWT,
210
+ State(ctx): State<AppContext>,
211
+ ) -> Result<Json<UserResponse>> {
212
+ let user = users::Model::find_by_pid(&ctx.db, &auth.claims.pid).await?;
213
+ Ok(Json(UserResponse::from(user)))
214
+ }
215
+ ```
216
+
217
+ ### Token Configuration
218
+ ```yaml
219
+ auth:
220
+ jwt:
221
+ secret: <change-in-production>
222
+ expiration: 604800 # seconds
223
+ location:
224
+ from: Bearer # or Cookie, Query
225
+ ```
226
+
227
+ Support multiple auth methods via fallback chains (Bearer -> Cookie -> Query).
228
+
229
+ ---
230
+
231
+ ## Database Conventions
232
+
233
+ ### Migrations
234
+ Migration names auto-detect operations:
235
+ - `CreatePosts` -- new table
236
+ - `AddNameToUsers` -- column addition
237
+ - `RemoveAgeFromUsers` -- column removal
238
+ - `AddUserRefToPosts` -- foreign key
239
+ - `CreateJoinTableUsersAndGroups` -- link table
240
+
241
+ ```bash
242
+ cargo loco generate model posts title:string! content:text user:references
243
+ ```
244
+
245
+ Field suffixes: `!` = NOT NULL, `^` = UNIQUE, none = nullable.
246
+
247
+ ### Entity Generation
248
+ After migrations, regenerate entities:
249
+ ```bash
250
+ cargo loco db entities
251
+ ```
252
+
253
+ Never edit files in `src/models/_entities/` -- they are overwritten.
254
+
255
+ ### Seeding
256
+ Define fixtures in `src/fixtures/` as YAML. Load via `Hooks::seed()`:
257
+ ```bash
258
+ cargo loco db seed --reset
259
+ ```
260
+
261
+ ---
262
+
263
+ ## Configuration Environments
264
+
265
+ ### Development (`config/development.yaml`)
266
+ ```yaml
267
+ database:
268
+ auto_migrate: true
269
+ dangerously_truncate: false
270
+ dangerously_recreate: false
271
+ ```
272
+
273
+ ### Test (`config/test.yaml`)
274
+ ```yaml
275
+ database:
276
+ dangerously_truncate: true # Clean slate per test run
277
+ ```
278
+
279
+ ### Production (`config/production.yaml`)
280
+ - Disable all `dangerously_*` flags
281
+ - Set secure JWT secret
282
+ - Configure Redis URI for queue workers
283
+ - Set appropriate log level and format (JSON recommended)
284
+ - Run `cargo loco doctor --production` to validate
285
+
286
+ ---
287
+
288
+ ## Deployment
289
+
290
+ ### Single Binary
291
+ Build and deploy -- no Rust toolchain needed on server:
292
+ ```bash
293
+ cargo build --release
294
+ # Copy binary + config/ to server
295
+ ./myapp start
296
+ ```
297
+
298
+ ### Docker
299
+ ```bash
300
+ cargo loco generate deployment # Generates Dockerfile
301
+ docker build -t myapp .
302
+ docker run -p 5150:5150 myapp
303
+ ```
304
+
305
+ ### Health Check
306
+ ```bash
307
+ cargo loco doctor --production
308
+ ```
309
+
310
+ ---
311
+
312
+ ## Generators
313
+
314
+ Use generators to maintain consistency:
315
+
316
+ | Generator | Command | Creates |
317
+ |-----------|---------|---------|
318
+ | Model | `cargo loco generate model <name> <fields>` | Migration + entity + model file |
319
+ | Controller | `cargo loco generate controller <name>` | Controller + test file |
320
+ | Scaffold | `cargo loco generate scaffold <name> <fields>` | Full CRUD (model + controller + view + tests) |
321
+ | Worker | `cargo loco generate worker <name>` | Worker file |
322
+ | Task | `cargo loco generate task <name>` | Task file |
323
+ | Deployment | `cargo loco generate deployment` | Docker/Nginx/Shuttle configs |
324
+
325
+ ---
326
+
327
+ ## Anti-Patterns to Avoid
328
+
329
+ | Anti-Pattern | Problem | Correct Approach |
330
+ |---|---|---|
331
+ | Logic in controllers | Untestable, not reusable | Move domain logic to model methods |
332
+ | Editing `_entities/` files | Overwritten on regeneration | Add logic in `models/<name>.rs` impl blocks |
333
+ | Shared controller/worker state | Breaks horizontal scaling | Workers are self-contained with serializable args |
334
+ | `dangerously_*` flags in production | Data loss risk | Only enable in dev/test environments |
335
+ | Skipping generators | Inconsistent structure | Use `cargo loco generate` for all scaffolding |
336
+ | Raw SQL in controllers | Bypass ORM protections | Use SeaORM queries in model methods |
337
+ | Hardcoded config values | Environment-specific failures | Use `config/*.yaml` per environment |
338
+ | Nullable everything | Weak data integrity | Use `!` suffix for required fields in generators |
339
+
340
+ ---
341
+
342
+ _Loco is "Rust on Rails" -- lean into conventions, use generators, keep models fat and controllers thin._