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,209 @@
1
+ # Testing Patterns
2
+
3
+ Comprehensive Vitest patterns for modern Node.js projects.
4
+
5
+ ---
6
+
7
+ ## Philosophy
8
+
9
+ - **Fast feedback**: Unit tests run in milliseconds, no I/O
10
+ - **Realistic integration**: Test with real HTTP calls and database when it matters
11
+ - **Readable tests**: Each test tells a story with arrange-act-assert
12
+ - **Native ESM**: Vitest runs ESM natively, no transpilation step
13
+
14
+ ---
15
+
16
+ ## Test Organization
17
+
18
+ ```
19
+ tests/
20
+ setup.js # Global test setup
21
+ unit/
22
+ services/
23
+ user-service.test.js
24
+ utils/
25
+ validation.test.js
26
+ integration/
27
+ api/
28
+ users.test.js
29
+ repositories/
30
+ user-repo.test.js
31
+ fixtures/
32
+ users.js
33
+ ```
34
+
35
+ **Pattern**: Mirror `src/` structure. Suffix all test files with `.test.js`.
36
+
37
+ ---
38
+
39
+ ## Vitest Configuration
40
+
41
+ ```javascript
42
+ // vitest.config.js
43
+ import { defineConfig } from 'vitest/config';
44
+
45
+ export default defineConfig({
46
+ test: {
47
+ globals: true,
48
+ environment: 'node',
49
+ include: ['tests/**/*.test.js'],
50
+ coverage: {
51
+ provider: 'v8',
52
+ include: ['src/**/*.js'],
53
+ thresholds: { lines: 80, functions: 80, branches: 75 },
54
+ },
55
+ testTimeout: 5000,
56
+ hookTimeout: 10000,
57
+ },
58
+ });
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Basic Test Patterns
64
+
65
+ ```javascript
66
+ import { describe, it, expect } from 'vitest';
67
+ import { calculateTotal } from '../../src/services/pricing.js';
68
+
69
+ describe('calculateTotal', () => {
70
+ it('sums item prices', () => {
71
+ const items = [{ price: 1000 }, { price: 2500 }];
72
+ expect(calculateTotal(items)).toBe(3500);
73
+ });
74
+
75
+ it('returns 0 for empty array', () => {
76
+ expect(calculateTotal([])).toBe(0);
77
+ });
78
+ });
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Mocking
84
+
85
+ ### vi.fn() and vi.mock()
86
+
87
+ ```javascript
88
+ import { describe, it, expect, vi } from 'vitest';
89
+ import { createUser } from '../../src/services/user-service.js';
90
+
91
+ vi.mock('../../src/repositories/user-repo.js', () => ({
92
+ userRepo: { findByEmail: vi.fn(), save: vi.fn() },
93
+ }));
94
+
95
+ import { userRepo } from '../../src/repositories/user-repo.js';
96
+
97
+ describe('createUser', () => {
98
+ it('saves user when email is available', async () => {
99
+ userRepo.findByEmail.mockResolvedValue(null);
100
+ userRepo.save.mockResolvedValue({ id: 1, name: 'Alice' });
101
+
102
+ const user = await createUser({ name: 'Alice', email: 'alice@test.com', password: 'secret' });
103
+
104
+ expect(user.id).toBe(1);
105
+ expect(userRepo.save).toHaveBeenCalledOnce();
106
+ });
107
+
108
+ it('throws ConflictError when email exists', async () => {
109
+ userRepo.findByEmail.mockResolvedValue({ id: 99 });
110
+
111
+ await expect(
112
+ createUser({ name: 'Bob', email: 'taken@test.com', password: 'secret' }),
113
+ ).rejects.toThrow('Email already registered');
114
+ });
115
+ });
116
+ ```
117
+
118
+ ---
119
+
120
+ ## Testing Async Code
121
+
122
+ ```javascript
123
+ it('rejects with NotFoundError for missing user', async () => {
124
+ await expect(userService.getUser(999)).rejects.toThrow('User not found');
125
+ await expect(userService.getUser(999)).rejects.toBeInstanceOf(NotFoundError);
126
+ });
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Integration Testing with Supertest
132
+
133
+ ```javascript
134
+ import { describe, it, expect } from 'vitest';
135
+ import request from 'supertest';
136
+ import { app } from '../../src/app.js';
137
+
138
+ describe('POST /api/users', () => {
139
+ it('creates a new user', async () => {
140
+ const response = await request(app)
141
+ .post('/api/users')
142
+ .send({ name: 'Alice', email: 'alice@test.com', password: 'secure123' })
143
+ .expect(201);
144
+
145
+ expect(response.body.email).toBe('alice@test.com');
146
+ expect(response.body).not.toHaveProperty('password');
147
+ });
148
+
149
+ it('returns 422 for invalid data', async () => {
150
+ const response = await request(app)
151
+ .post('/api/users')
152
+ .send({ name: '' })
153
+ .expect(422);
154
+
155
+ expect(response.body.error.code).toBe('VALIDATION_ERROR');
156
+ });
157
+ });
158
+ ```
159
+
160
+ ---
161
+
162
+ ## Test Fixtures
163
+
164
+ ```javascript
165
+ // tests/fixtures/users.js
166
+ export function buildUser(overrides = {}) {
167
+ return {
168
+ id: 1,
169
+ name: 'Alice',
170
+ email: 'alice@test.com',
171
+ role: 'user',
172
+ isActive: true,
173
+ createdAt: new Date('2025-01-01'),
174
+ ...overrides,
175
+ };
176
+ }
177
+ ```
178
+
179
+ ---
180
+
181
+ ## Parameterized Tests
182
+
183
+ ```javascript
184
+ describe('isValidEmail', () => {
185
+ it.each([
186
+ ['user@example.com', true],
187
+ ['invalid', false],
188
+ ['', false],
189
+ ['@example.com', false],
190
+ ])('validates "%s" as %s', (email, expected) => {
191
+ expect(isValidEmail(email)).toBe(expected);
192
+ });
193
+ });
194
+ ```
195
+
196
+ ---
197
+
198
+ ## Test Commands
199
+
200
+ ```bash
201
+ npx vitest run tests/unit/ --reporter=dot # Unit tests, minimal output
202
+ npx vitest # Watch mode with HMR
203
+ npx vitest run --coverage # All tests + coverage
204
+ npx vitest run --reporter=junit --outputFile=results.xml # CI
205
+ ```
206
+
207
+ ---
208
+
209
+ _Tests document behavior. Each test should read as a specification of what the code does._
@@ -0,0 +1,156 @@
1
+ # Code Quality
2
+
3
+ Code quality standards and tooling for Loco applications. Automated enforcement where possible, conventions where not.
4
+
5
+ ---
6
+
7
+ ## Tooling
8
+
9
+ ### Required in CI
10
+
11
+ | Tool | Purpose | Command |
12
+ |------|---------|---------|
13
+ | `cargo fmt` | Code formatting | `cargo fmt -- --check` |
14
+ | `cargo clippy` | Linting | `cargo clippy -- -D warnings` |
15
+ | `cargo test` | Test suite | `cargo test` or `cargo nextest run` |
16
+ | `cargo audit` | Dependency vulnerabilities | `cargo audit` |
17
+ | `cargo loco doctor` | Environment validation | `cargo loco doctor --production` |
18
+
19
+ ### Recommended
20
+
21
+ | Tool | Purpose | Command |
22
+ |------|---------|---------|
23
+ | `cargo nextest` | Faster parallel tests | `cargo nextest run` |
24
+ | `cargo deny` | License and advisory checks | `cargo deny check` |
25
+ | `cargo machete` | Unused dependency detection | `cargo machete` |
26
+
27
+ ---
28
+
29
+ ## Formatting
30
+
31
+ Run `cargo fmt` and move on. Configure in `rustfmt.toml` if needed:
32
+
33
+ ```toml
34
+ # rustfmt.toml
35
+ edition = "2021"
36
+ max_width = 100
37
+ use_field_init_shorthand = true
38
+ ```
39
+
40
+ ---
41
+
42
+ ## Linting
43
+
44
+ ### Clippy Configuration
45
+
46
+ Deny warnings in CI, allow iterative development locally:
47
+
48
+ ```bash
49
+ # CI: strict
50
+ cargo clippy -- -D warnings
51
+
52
+ # Local: advisory
53
+ cargo clippy
54
+ ```
55
+
56
+ Common Clippy lints to enforce:
57
+ - `clippy::unwrap_used` -- no `.unwrap()` in production code
58
+ - `clippy::expect_used` -- prefer `?` or `.ok_or()`
59
+ - `clippy::large_enum_variant` -- box large variants
60
+ - `clippy::needless_pass_by_value` -- borrow instead of clone
61
+
62
+ ---
63
+
64
+ ## Code Review Checklist
65
+
66
+ ### Controllers
67
+ - [ ] Thin: validate, delegate, respond (under 15 lines)
68
+ - [ ] Uses Axum extractors for request data
69
+ - [ ] Returns `Result<impl IntoResponse>`
70
+ - [ ] Auth-protected routes use `auth::JWT` extractor
71
+
72
+ ### Models
73
+ - [ ] Domain logic lives here, not in controllers
74
+ - [ ] `_entities/` files are untouched
75
+ - [ ] Validation implemented via `Validatable` trait
76
+ - [ ] Queries use SeaORM, not raw SQL
77
+
78
+ ### Workers
79
+ - [ ] Self-contained, no shared controller state
80
+ - [ ] Args are `Serialize + Deserialize`
81
+ - [ ] Error handling with logging context
82
+ - [ ] Registered in `app.rs` `connect_workers`
83
+
84
+ ### Migrations
85
+ - [ ] Descriptive names matching auto-detection patterns
86
+ - [ ] `down()` implemented for rollback
87
+ - [ ] Required fields use NOT NULL
88
+ - [ ] Foreign keys and indexes added
89
+
90
+ ### Tests
91
+ - [ ] Model tests for domain logic
92
+ - [ ] Request tests for controller endpoints
93
+ - [ ] Auth tested on protected routes
94
+ - [ ] `ForegroundBlocking` mode in test config
95
+
96
+ ---
97
+
98
+ ## Dependency Management
99
+
100
+ ### Adding Dependencies
101
+
102
+ ```toml
103
+ # Cargo.toml -- pin major versions
104
+ [dependencies]
105
+ loco-rs = "0.x"
106
+ sea-orm = { version = "1", features = ["sqlx-postgres", "runtime-tokio-rustls"] }
107
+ serde = { version = "1", features = ["derive"] }
108
+ ```
109
+
110
+ ### Rules
111
+ - Pin major version, allow minor/patch updates
112
+ - Enable only needed feature flags
113
+ - Audit new dependencies: `cargo audit`, `cargo deny`
114
+ - Prefer well-maintained crates with recent activity
115
+ - Remove unused dependencies: `cargo machete`
116
+
117
+ ---
118
+
119
+ ## Performance Guidelines
120
+
121
+ ### Database
122
+ - Use `includes` / eager loading for associations (avoid N+1)
123
+ - Add indexes for frequently queried columns
124
+ - Configure connection pool sizes per environment
125
+ - Use `explain` for slow queries
126
+
127
+ ### HTTP
128
+ - Enable compression middleware in production
129
+ - Use Loco's built-in caching layer for repeated queries
130
+ - Return only needed fields in view structs (not full entities)
131
+
132
+ ### Workers
133
+ - Use `BackgroundQueue` (Redis/Postgres) for production horizontal scaling
134
+ - Tag workers for resource-intensive jobs
135
+ - Keep job args small -- pass IDs, not full objects
136
+
137
+ ---
138
+
139
+ ## Security
140
+
141
+ ### Built-in Protections
142
+ - JWT authentication with configurable token location
143
+ - Secure headers middleware (enabled by default)
144
+ - CORS configuration via YAML
145
+
146
+ ### Practices
147
+ - Never commit secrets -- use environment config or Loco's config system
148
+ - Rotate JWT secrets in production
149
+ - Enable `secure_headers` middleware with appropriate presets
150
+ - Validate all user input via `Validatable` or `JsonValidate`
151
+ - Use parameterized SeaORM queries (never string interpolation in queries)
152
+ - Run `cargo audit` in CI
153
+
154
+ ---
155
+
156
+ _Automate quality: fmt for formatting, clippy for linting, tests for correctness, audit for security. Human review for architecture and domain logic._
@@ -0,0 +1,247 @@
1
+ # Loco Coding Style
2
+
3
+ Coding style conventions for idiomatic Loco applications. Combines Rust best practices with Loco's Rails-inspired conventions.
4
+
5
+ ---
6
+
7
+ ## Philosophy
8
+
9
+ - **Convention over configuration**: Follow Loco's prescribed patterns
10
+ - **Fat models, slim controllers**: Business logic belongs on models
11
+ - **Use generators**: Scaffolding maintains consistency
12
+ - **Leverage the type system**: Encode invariants in types, not runtime checks
13
+ - **Clippy is law**: If Clippy warns, fix it
14
+
15
+ ---
16
+
17
+ ## Naming Conventions
18
+
19
+ ### Loco-Specific Naming
20
+
21
+ | Element | Convention | Example |
22
+ |---|---|---|
23
+ | Controller functions | snake_case verbs | `create`, `list`, `show`, `update`, `destroy` |
24
+ | Route prefixes | plural resource name | `"posts"`, `"users"`, `"auth"` |
25
+ | Model methods | domain verbs | `register`, `verify_email`, `reset_password` |
26
+ | Worker structs | PascalCase + Worker | `ReportWorker`, `EmailWorker` |
27
+ | Worker args | PascalCase + Args | `ReportArgs`, `EmailArgs` |
28
+ | Task structs | PascalCase | `SeedData`, `SyncUsers` |
29
+ | View structs | PascalCase + Response | `PostResponse`, `UserResponse` |
30
+ | Migration files | timestamp + description | `m20231001_000001_create_users` |
31
+
32
+ ### Standard Rust Naming
33
+
34
+ | Element | Convention | Example |
35
+ |---|---|---|
36
+ | Variables, functions | `snake_case` | `user_count`, `get_user` |
37
+ | Types, traits, enums | `PascalCase` | `UserService`, `Serialize` |
38
+ | Constants | `SCREAMING_SNAKE_CASE` | `MAX_RETRIES`, `DEFAULT_PORT` |
39
+ | Modules | `snake_case` | `user_service`, `auth_utils` |
40
+ | Lifetimes | Short lowercase with `'` | `'a`, `'ctx` |
41
+
42
+ ### Boolean Naming
43
+
44
+ Prefix with `is_`, `has_`, `can_`, `should_`:
45
+
46
+ ```rust
47
+ struct User {
48
+ is_active: bool,
49
+ has_verified_email: bool,
50
+ can_publish: bool,
51
+ }
52
+ ```
53
+
54
+ ---
55
+
56
+ ## Controller Style
57
+
58
+ ### Thin Controllers
59
+
60
+ ```rust
61
+ // GOOD: Delegates to model
62
+ async fn create(
63
+ State(ctx): State<AppContext>,
64
+ Json(params): Json<CreatePostParams>,
65
+ ) -> Result<Json<PostResponse>> {
66
+ let post = posts::ActiveModel::create(&ctx.db, &params).await?;
67
+ Ok(Json(PostResponse::from(post)))
68
+ }
69
+
70
+ // BAD: Logic in controller
71
+ async fn create(
72
+ State(ctx): State<AppContext>,
73
+ Json(params): Json<CreatePostParams>,
74
+ ) -> Result<Json<PostResponse>> {
75
+ // Validation logic here...
76
+ // Business rules here...
77
+ // Direct SQL here...
78
+ // Email sending here...
79
+ }
80
+ ```
81
+
82
+ ### Consistent Route Structure
83
+
84
+ ```rust
85
+ // GOOD: RESTful, predictable
86
+ pub fn routes() -> Routes {
87
+ Routes::new()
88
+ .prefix("posts")
89
+ .add("/", get(list))
90
+ .add("/", post(create))
91
+ .add("/:id", get(show))
92
+ .add("/:id", put(update))
93
+ .add("/:id", delete(destroy))
94
+ }
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Model Style
100
+
101
+ ### Domain Methods
102
+
103
+ ```rust
104
+ // GOOD: Rich domain model
105
+ impl super::_entities::users::ActiveModel {
106
+ pub async fn register(db: &DatabaseConnection, params: &RegisterParams) -> ModelResult<Self> {
107
+ // Complete workflow in one place
108
+ }
109
+
110
+ pub async fn find_by_email(db: &DatabaseConnection, email: &str) -> ModelResult<Self> {
111
+ // Reusable query
112
+ }
113
+ }
114
+
115
+ // BAD: Anemic model, logic scattered elsewhere
116
+ // (query in controller, validation in middleware, email in separate service)
117
+ ```
118
+
119
+ ### Validation
120
+
121
+ Use the `validator` crate with `Validatable` trait:
122
+
123
+ ```rust
124
+ impl Validatable for super::_entities::users::ActiveModel {
125
+ fn validator(&self) -> Box<dyn Validate> {
126
+ Box::new(UserValidator {
127
+ email: self.email.clone().into_value().unwrap_or_default(),
128
+ })
129
+ }
130
+ }
131
+
132
+ // Always validate before persistence
133
+ user.validate()?;
134
+ user.insert(db).await?;
135
+ ```
136
+
137
+ ---
138
+
139
+ ## Error Handling Style
140
+
141
+ ### Use `?` Operator Consistently
142
+
143
+ ```rust
144
+ // GOOD: Flat with ? operator
145
+ async fn publish(ctx: &AppContext, id: i32, user_id: i32) -> Result<Post> {
146
+ let post = posts::Entity::find_by_id(id)
147
+ .one(&ctx.db)
148
+ .await?
149
+ .ok_or_else(|| Error::NotFound)?;
150
+
151
+ if post.user_id != user_id {
152
+ return Err(Error::Unauthorized("Not your post".into()));
153
+ }
154
+
155
+ update_status(&ctx.db, id, Status::Published).await
156
+ }
157
+
158
+ // BAD: Deeply nested match/if chains
159
+ ```
160
+
161
+ ---
162
+
163
+ ## View Style
164
+
165
+ ### Explicit Response Structs
166
+
167
+ ```rust
168
+ // GOOD: Dedicated view struct with From impl
169
+ #[derive(Serialize)]
170
+ pub struct PostResponse {
171
+ pub id: i32,
172
+ pub title: String,
173
+ pub author_name: String, // Derived, not raw FK
174
+ }
175
+
176
+ impl PostResponse {
177
+ pub fn from_model_with_user(post: posts::Model, user: users::Model) -> Self {
178
+ Self {
179
+ id: post.id,
180
+ title: post.title,
181
+ author_name: user.name,
182
+ }
183
+ }
184
+ }
185
+
186
+ // BAD: Returning raw entity models as JSON
187
+ ```
188
+
189
+ ---
190
+
191
+ ## Worker Style
192
+
193
+ ### Self-Contained with Serializable Args
194
+
195
+ ```rust
196
+ // GOOD: Serializable args, no shared state
197
+ #[derive(Serialize, Deserialize)]
198
+ pub struct EmailArgs {
199
+ pub user_id: i32,
200
+ pub template: String,
201
+ }
202
+
203
+ #[async_trait]
204
+ impl BackgroundWorker<EmailArgs> for EmailWorker {
205
+ fn build(ctx: &AppContext) -> Self { Self { ctx: ctx.clone() } }
206
+
207
+ async fn perform(&self, args: EmailArgs) -> Result<()> {
208
+ let user = users::Entity::find_by_id(args.user_id)
209
+ .one(&self.ctx.db).await?
210
+ .ok_or(Error::NotFound)?;
211
+ // Send email using args.template
212
+ Ok(())
213
+ }
214
+ }
215
+
216
+ // BAD: Passing non-serializable types, referencing controller state
217
+ ```
218
+
219
+ ---
220
+
221
+ ## File and Type Size
222
+
223
+ | Element | Guideline |
224
+ |---|---|
225
+ | Controller function | Under 15 lines (validate, delegate, respond) |
226
+ | Model method | Under 30 lines of logic |
227
+ | View struct | Under 50 lines |
228
+ | Worker perform | Under 40 lines |
229
+ | Module file | Under 300 lines, max 500 |
230
+ | Parameters | Max 5 per function; use struct for more |
231
+
232
+ ---
233
+
234
+ ## Anti-Patterns
235
+
236
+ | Anti-Pattern | Problem | Correct Approach |
237
+ |---|---|---|
238
+ | Fat controllers | Untestable, unreusable logic | Move to model methods |
239
+ | `.unwrap()` in handlers | Panics crash the request | Use `?` and proper error types |
240
+ | Raw entity as response | Exposes internal schema | Use view structs with `From` |
241
+ | Business logic in workers | Duplicates model logic | Call model methods from workers |
242
+ | Ignoring generators | Inconsistent file placement | Always use `cargo loco generate` |
243
+ | `clone()` on large structs | Hidden performance cost | Borrow with `&` or use `Arc` |
244
+
245
+ ---
246
+
247
+ _Follow Loco conventions: generators for scaffolding, models for logic, controllers for routing, views for shaping. Let the framework guide structure._