sdlc-framework 1.0.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.
- package/LICENSE +21 -0
- package/README.md +321 -0
- package/bin/install.js +193 -0
- package/package.json +39 -0
- package/src/commands/close.md +200 -0
- package/src/commands/debug.md +124 -0
- package/src/commands/fast.md +149 -0
- package/src/commands/fix.md +104 -0
- package/src/commands/help.md +144 -0
- package/src/commands/hotfix.md +99 -0
- package/src/commands/impl.md +142 -0
- package/src/commands/init.md +93 -0
- package/src/commands/milestone.md +136 -0
- package/src/commands/pause.md +115 -0
- package/src/commands/research.md +136 -0
- package/src/commands/resume.md +103 -0
- package/src/commands/review.md +195 -0
- package/src/commands/spec.md +164 -0
- package/src/commands/status.md +118 -0
- package/src/commands/verify.md +153 -0
- package/src/references/clarification-strategy.md +352 -0
- package/src/references/engineering-laws.md +374 -0
- package/src/references/loop-phases.md +331 -0
- package/src/references/playwright-testing.md +298 -0
- package/src/references/prompt-detection.md +264 -0
- package/src/references/sub-agent-strategy.md +260 -0
- package/src/rules/commands.md +180 -0
- package/src/rules/style.md +354 -0
- package/src/rules/templates.md +238 -0
- package/src/rules/workflows.md +314 -0
- package/src/templates/HANDOFF.md +121 -0
- package/src/templates/LAWS.md +521 -0
- package/src/templates/PROJECT.md +112 -0
- package/src/templates/REVIEW.md +145 -0
- package/src/templates/ROADMAP.md +101 -0
- package/src/templates/SPEC.md +231 -0
- package/src/templates/STATE.md +106 -0
- package/src/templates/SUMMARY.md +126 -0
- package/src/workflows/close-phase.md +189 -0
- package/src/workflows/debug-flow.md +302 -0
- package/src/workflows/fast-forward.md +340 -0
- package/src/workflows/fix-findings.md +235 -0
- package/src/workflows/hotfix-flow.md +190 -0
- package/src/workflows/impl-phase.md +229 -0
- package/src/workflows/init-project.md +249 -0
- package/src/workflows/milestone-management.md +169 -0
- package/src/workflows/pause-work.md +153 -0
- package/src/workflows/research.md +219 -0
- package/src/workflows/resume-project.md +159 -0
- package/src/workflows/review-phase.md +337 -0
- package/src/workflows/spec-phase.md +379 -0
- package/src/workflows/transition-phase.md +203 -0
- package/src/workflows/verify-phase.md +280 -0
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
# Engineering Laws Template
|
|
2
|
+
|
|
3
|
+
This template defines the engineering laws configuration stored at `.sdlc/LAWS.md`. Laws are enforced during `/sdlc:review`. Violations at the configured severity level block loop closure. This is the backbone of quality enforcement — every line of code passes through these laws before it ships.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
```markdown
|
|
8
|
+
# Engineering Laws
|
|
9
|
+
|
|
10
|
+
These laws are enforced at /sdlc:review. Violations at configured severity block loop closure.
|
|
11
|
+
|
|
12
|
+
## Configuration
|
|
13
|
+
|
|
14
|
+
| Law | Severity | Description |
|
|
15
|
+
|-----|----------|-------------|
|
|
16
|
+
| SOLID | error | Single Responsibility, Open/Closed, Liskov, Interface Segregation, Dependency Inversion |
|
|
17
|
+
| DRY | error | Don't Repeat Yourself — search before create, no duplicate logic |
|
|
18
|
+
| YAGNI | error | You Ain't Gonna Need It — only build what's in the spec |
|
|
19
|
+
| CLEAN_CODE | error | Max 40 lines/function, max 3 params, max 3 nesting levels |
|
|
20
|
+
| SECURITY | error | No hardcoded secrets, input validation, parameterized queries |
|
|
21
|
+
| TESTING | error | Every behavior change needs tests, meaningful assertions |
|
|
22
|
+
| NAMING | warning | Descriptive names, no abbreviations, self-documenting |
|
|
23
|
+
| ERROR_HANDLING | error | No empty catch, domain-specific exceptions, no swallowed errors |
|
|
24
|
+
|
|
25
|
+
Severity levels: error (blocks /sdlc:close) | warning (reported, doesn't block) | info (noted only)
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## SOLID — Structural Integrity
|
|
30
|
+
|
|
31
|
+
### What It Means
|
|
32
|
+
|
|
33
|
+
SOLID is five principles that keep code flexible and maintainable. Each class, module, or function should have one reason to change (S), be extendable without editing existing code (O), be replaceable by subtypes without breaking anything (L), not force consumers to depend on methods they do not use (I), and depend on abstractions rather than concrete implementations (D).
|
|
34
|
+
|
|
35
|
+
### What the Reviewer Checks
|
|
36
|
+
|
|
37
|
+
- **S — Single Responsibility:** Does each class/module/function do exactly one thing? Does it have exactly one reason to change?
|
|
38
|
+
- **O — Open/Closed:** Can new behavior be added by extending (new class, new handler) rather than modifying existing code?
|
|
39
|
+
- **L — Liskov Substitution:** Can any subclass/implementation replace its parent/interface without the caller knowing?
|
|
40
|
+
- **I — Interface Segregation:** Are interfaces small and focused? Does any consumer import methods it never calls?
|
|
41
|
+
- **D — Dependency Inversion:** Do high-level modules depend on abstractions? Are concrete implementations injected, not instantiated?
|
|
42
|
+
|
|
43
|
+
### Violations and Fixes
|
|
44
|
+
|
|
45
|
+
**Violation — God class:**
|
|
46
|
+
```typescript
|
|
47
|
+
// BAD: UserService handles auth, profile, notifications, billing
|
|
48
|
+
class UserService {
|
|
49
|
+
login() { /* ... */ }
|
|
50
|
+
updateProfile() { /* ... */ }
|
|
51
|
+
sendNotification() { /* ... */ }
|
|
52
|
+
processPayment() { /* ... */ }
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Fix — Split by responsibility:**
|
|
57
|
+
```typescript
|
|
58
|
+
// GOOD: Each service owns one domain
|
|
59
|
+
class AuthService { login() { /* ... */ } }
|
|
60
|
+
class ProfileService { updateProfile() { /* ... */ } }
|
|
61
|
+
class NotificationService { sendNotification() { /* ... */ } }
|
|
62
|
+
class BillingService { processPayment() { /* ... */ } }
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Violation — Concrete dependency:**
|
|
66
|
+
```typescript
|
|
67
|
+
// BAD: Controller creates its own service
|
|
68
|
+
class UserController {
|
|
69
|
+
private service = new UserService(new PostgresRepo())
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Fix — Inject abstraction:**
|
|
74
|
+
```typescript
|
|
75
|
+
// GOOD: Depend on interface, inject via constructor
|
|
76
|
+
class UserController {
|
|
77
|
+
constructor(private readonly userService: UserServiceInterface) {}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Why It Matters
|
|
82
|
+
|
|
83
|
+
Code that violates SOLID becomes rigid. One change breaks unrelated features. Testing requires mocking the entire world. New developers are afraid to touch anything. Eventually the team rewrites instead of extending, wasting weeks or months.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## DRY — No Duplicate Logic
|
|
88
|
+
|
|
89
|
+
### What It Means
|
|
90
|
+
|
|
91
|
+
Every piece of knowledge should have a single, authoritative representation in the codebase. Before writing any new code, search for existing implementations. If you find one, use it. If you need a variation, extend the existing code rather than duplicating it.
|
|
92
|
+
|
|
93
|
+
### What the Reviewer Checks
|
|
94
|
+
|
|
95
|
+
- Are there two or more functions/methods with the same logic (even if variable names differ)?
|
|
96
|
+
- Is the same validation logic written in multiple places?
|
|
97
|
+
- Are there multiple constants with the same value but different names?
|
|
98
|
+
- Did the developer search for existing helpers/utilities before writing new ones?
|
|
99
|
+
- Are there inline implementations of logic that already exists in a shared utility?
|
|
100
|
+
|
|
101
|
+
### Violations and Fixes
|
|
102
|
+
|
|
103
|
+
**Violation — Duplicated validation:**
|
|
104
|
+
```typescript
|
|
105
|
+
// In user.controller.ts
|
|
106
|
+
function validateEmail(email: string): boolean {
|
|
107
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// In contact.controller.ts (same logic, different file)
|
|
111
|
+
function isValidEmail(input: string): boolean {
|
|
112
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(input)
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Fix — Single shared utility:**
|
|
117
|
+
```typescript
|
|
118
|
+
// In src/common/utils/validation.ts
|
|
119
|
+
export function isValidEmail(email: string): boolean {
|
|
120
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Both controllers import from the same source
|
|
124
|
+
import { isValidEmail } from '@/common/utils/validation'
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Violation — Repeated error response shape:**
|
|
128
|
+
```typescript
|
|
129
|
+
// Inline in every controller method
|
|
130
|
+
return { status: 'error', message: err.message, code: 400 }
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Fix — Shared error factory:**
|
|
134
|
+
```typescript
|
|
135
|
+
// In src/common/errors/error-response.ts
|
|
136
|
+
export function createErrorResponse(message: string, code: number): ErrorResponse {
|
|
137
|
+
return { status: 'error', message, code }
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Why It Matters
|
|
142
|
+
|
|
143
|
+
Duplicated logic means duplicated bugs. Fix a bug in one place, the copy still has it. Over time, copies diverge — slightly different behavior in different places. Users see inconsistent results. Developers spend hours hunting down "which version is right."
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## YAGNI — Only Build What Is Specified
|
|
148
|
+
|
|
149
|
+
### What It Means
|
|
150
|
+
|
|
151
|
+
Do not build features, abstractions, or infrastructure that are not explicitly required by the current spec. "We might need this later" is not a justification. Build for today's requirements. If tomorrow needs something different, build it tomorrow.
|
|
152
|
+
|
|
153
|
+
### What the Reviewer Checks
|
|
154
|
+
|
|
155
|
+
- Does every file, function, and class trace back to a task in the spec?
|
|
156
|
+
- Are there abstract base classes with only one implementation?
|
|
157
|
+
- Are there configuration options nobody asked for?
|
|
158
|
+
- Are there API endpoints not in the spec?
|
|
159
|
+
- Are there database columns not required by any acceptance criterion?
|
|
160
|
+
- Is there "framework code" (plugin systems, event buses, middleware chains) when the spec asked for a simple function?
|
|
161
|
+
|
|
162
|
+
### Violations and Fixes
|
|
163
|
+
|
|
164
|
+
**Violation — Premature abstraction:**
|
|
165
|
+
```typescript
|
|
166
|
+
// Spec says "store users in PostgreSQL"
|
|
167
|
+
// Developer builds a generic repository pattern with adapters for
|
|
168
|
+
// PostgreSQL, MongoDB, Redis, and SQLite "just in case"
|
|
169
|
+
interface StorageAdapter<T> { /* ... */ }
|
|
170
|
+
class PostgresAdapter<T> implements StorageAdapter<T> { /* ... */ }
|
|
171
|
+
class MongoAdapter<T> implements StorageAdapter<T> { /* ... */ }
|
|
172
|
+
class RedisAdapter<T> implements StorageAdapter<T> { /* ... */ }
|
|
173
|
+
class SQLiteAdapter<T> implements StorageAdapter<T> { /* ... */ }
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Fix — Build what is needed:**
|
|
177
|
+
```typescript
|
|
178
|
+
// Just the PostgreSQL repository the spec requires
|
|
179
|
+
class UserRepository {
|
|
180
|
+
async findById(id: string): Promise<User | null> { /* ... */ }
|
|
181
|
+
async save(user: User): Promise<void> { /* ... */ }
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Violation — Unused configuration:**
|
|
186
|
+
```typescript
|
|
187
|
+
// Spec says nothing about rate limiting
|
|
188
|
+
export const config = {
|
|
189
|
+
rateLimitEnabled: true,
|
|
190
|
+
rateLimitWindow: 60000,
|
|
191
|
+
rateLimitMax: 100,
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**Fix — Remove it:**
|
|
196
|
+
```typescript
|
|
197
|
+
// If it is not in the spec, it does not exist
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Why It Matters
|
|
201
|
+
|
|
202
|
+
Unused code is not free. It must be read, understood, tested, and maintained. It creates false signals ("is this used? I better not delete it"). It adds complexity that slows down the team. The most maintainable code is code that does not exist.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## CLEAN_CODE — Readable and Maintainable
|
|
207
|
+
|
|
208
|
+
### What It Means
|
|
209
|
+
|
|
210
|
+
Code is read far more often than it is written. Clean code is small functions (max 40 lines), few parameters (max 3), shallow nesting (max 3 levels), and clear flow (guard clauses, early returns). If you need a comment to explain what code does, the code is not clean enough.
|
|
211
|
+
|
|
212
|
+
### What the Reviewer Checks
|
|
213
|
+
|
|
214
|
+
- **Function length:** Is any function longer than 40 lines? Count them.
|
|
215
|
+
- **Parameter count:** Does any function take more than 3 parameters? Group into an object.
|
|
216
|
+
- **Nesting depth:** Are there more than 3 levels of indentation? Use guard clauses.
|
|
217
|
+
- **Ternary chains:** Are there nested ternaries? Use if/else.
|
|
218
|
+
- **Comments explaining "what":** Is there a comment that describes what code does (not why)? Rename or restructure instead.
|
|
219
|
+
- **Dead code:** Are there commented-out blocks, unreachable branches, or unused variables?
|
|
220
|
+
|
|
221
|
+
### Violations and Fixes
|
|
222
|
+
|
|
223
|
+
**Violation — Deep nesting:**
|
|
224
|
+
```typescript
|
|
225
|
+
function processOrder(order: Order) {
|
|
226
|
+
if (order) {
|
|
227
|
+
if (order.items.length > 0) {
|
|
228
|
+
if (order.payment) {
|
|
229
|
+
if (order.payment.verified) {
|
|
230
|
+
// actual logic buried 4 levels deep
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**Fix — Guard clauses:**
|
|
239
|
+
```typescript
|
|
240
|
+
function processOrder(order: Order) {
|
|
241
|
+
if (!order) return
|
|
242
|
+
if (order.items.length === 0) return
|
|
243
|
+
if (!order.payment) return
|
|
244
|
+
if (!order.payment.verified) return
|
|
245
|
+
|
|
246
|
+
// actual logic at top level
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**Violation — Too many parameters:**
|
|
251
|
+
```typescript
|
|
252
|
+
function createUser(
|
|
253
|
+
name: string,
|
|
254
|
+
email: string,
|
|
255
|
+
age: number,
|
|
256
|
+
role: string,
|
|
257
|
+
department: string,
|
|
258
|
+
managerId: string,
|
|
259
|
+
) { /* ... */ }
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
**Fix — Parameter object:**
|
|
263
|
+
```typescript
|
|
264
|
+
interface CreateUserParams {
|
|
265
|
+
name: string
|
|
266
|
+
email: string
|
|
267
|
+
age: number
|
|
268
|
+
role: string
|
|
269
|
+
department: string
|
|
270
|
+
managerId: string
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function createUser(params: CreateUserParams) { /* ... */ }
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Why It Matters
|
|
277
|
+
|
|
278
|
+
Complex code is where bugs hide. Deep nesting makes it impossible to trace execution paths. Long functions do too many things and break in unexpected ways. Too many parameters mean the function has too many responsibilities. Clean code is debuggable code.
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## SECURITY — No Vulnerabilities
|
|
283
|
+
|
|
284
|
+
### What It Means
|
|
285
|
+
|
|
286
|
+
Security is not a feature — it is a constraint on all features. Never hardcode secrets. Always validate input from users, APIs, and files. Use parameterized queries for all database access. Sanitize file paths. Use established security middleware.
|
|
287
|
+
|
|
288
|
+
### What the Reviewer Checks
|
|
289
|
+
|
|
290
|
+
- **Hardcoded secrets:** Are there API keys, passwords, tokens, or connection strings in source files?
|
|
291
|
+
- **Input validation:** Is every user-facing input validated at the system boundary (controller, API handler, CLI parser)?
|
|
292
|
+
- **SQL injection:** Are all database queries parameterized? Is any query built with string concatenation?
|
|
293
|
+
- **Path traversal:** Are file paths sanitized? Can a user input `../../etc/passwd`?
|
|
294
|
+
- **Authentication/Authorization:** Are protected routes actually checking credentials? Is there a gap where unauthenticated access is possible?
|
|
295
|
+
- **.env files:** Are `.env` files in `.gitignore`? Are there example `.env.example` files instead?
|
|
296
|
+
- **Dependencies:** Are there known vulnerable dependencies?
|
|
297
|
+
|
|
298
|
+
### Violations and Fixes
|
|
299
|
+
|
|
300
|
+
**Violation — Hardcoded secret:**
|
|
301
|
+
```typescript
|
|
302
|
+
const API_KEY = 'sk-1234567890abcdef'
|
|
303
|
+
const dbUrl = 'postgres://admin:password@localhost:5432/mydb'
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
**Fix — Environment variables:**
|
|
307
|
+
```typescript
|
|
308
|
+
const API_KEY = process.env.API_KEY
|
|
309
|
+
const dbUrl = process.env.DATABASE_URL
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**Violation — SQL injection:**
|
|
313
|
+
```typescript
|
|
314
|
+
const query = `SELECT * FROM users WHERE email = '${userInput}'`
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
**Fix — Parameterized query:**
|
|
318
|
+
```typescript
|
|
319
|
+
const query = 'SELECT * FROM users WHERE email = $1'
|
|
320
|
+
const result = await db.query(query, [userInput])
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
**Violation — No input validation:**
|
|
324
|
+
```typescript
|
|
325
|
+
app.post('/upload', (req, res) => {
|
|
326
|
+
const filePath = req.body.path
|
|
327
|
+
fs.readFile(filePath, (err, data) => { /* ... */ })
|
|
328
|
+
})
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
**Fix — Path sanitization:**
|
|
332
|
+
```typescript
|
|
333
|
+
app.post('/upload', (req, res) => {
|
|
334
|
+
const safePath = path.resolve(UPLOAD_DIR, path.basename(req.body.path))
|
|
335
|
+
if (!safePath.startsWith(UPLOAD_DIR)) {
|
|
336
|
+
throw new ForbiddenException('Invalid file path')
|
|
337
|
+
}
|
|
338
|
+
fs.readFile(safePath, (err, data) => { /* ... */ })
|
|
339
|
+
})
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Why It Matters
|
|
343
|
+
|
|
344
|
+
Security vulnerabilities are not theoretical. They get exploited. Hardcoded secrets end up in git history and leak. SQL injection lets attackers read your entire database. Path traversal lets attackers read your server's filesystem. One vulnerability can destroy user trust permanently.
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## TESTING — Every Behavior Has a Test
|
|
349
|
+
|
|
350
|
+
### What It Means
|
|
351
|
+
|
|
352
|
+
If you change how code behaves, you write a test that proves the new behavior works. Tests are not optional. They are not "nice to have." They are the proof that your code does what the spec says. Without tests, the verification phase has nothing to verify.
|
|
353
|
+
|
|
354
|
+
### What the Reviewer Checks
|
|
355
|
+
|
|
356
|
+
- **Coverage:** Does every new/modified function have at least one test?
|
|
357
|
+
- **Meaningful assertions:** Do tests assert specific outcomes, not just "it didn't crash"?
|
|
358
|
+
- **Edge cases:** Are null, undefined, empty arrays, empty strings, and boundary values tested?
|
|
359
|
+
- **Test isolation:** Does each test run independently? No shared mutable state between tests?
|
|
360
|
+
- **Test naming:** Does each test name describe the scenario and expected outcome?
|
|
361
|
+
- **No implementation testing:** Do tests verify behavior (what), not implementation (how)?
|
|
362
|
+
|
|
363
|
+
### Violations and Fixes
|
|
364
|
+
|
|
365
|
+
**Violation — Test that asserts nothing meaningful:**
|
|
366
|
+
```typescript
|
|
367
|
+
test('createUser works', () => {
|
|
368
|
+
const result = createUser({ name: 'Alice', email: 'a@b.com' })
|
|
369
|
+
expect(result).toBeDefined()
|
|
370
|
+
})
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
**Fix — Meaningful assertions:**
|
|
374
|
+
```typescript
|
|
375
|
+
test('createUser returns user with generated ID and provided name', () => {
|
|
376
|
+
const result = createUser({ name: 'Alice', email: 'a@b.com' })
|
|
377
|
+
expect(result.id).toMatch(/^[0-9a-f-]{36}$/)
|
|
378
|
+
expect(result.name).toBe('Alice')
|
|
379
|
+
expect(result.email).toBe('a@b.com')
|
|
380
|
+
expect(result.createdAt).toBeInstanceOf(Date)
|
|
381
|
+
})
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
**Violation — Missing edge case test:**
|
|
385
|
+
```typescript
|
|
386
|
+
// Only tests the happy path
|
|
387
|
+
test('findUser returns user', () => {
|
|
388
|
+
const user = findUser('existing-id')
|
|
389
|
+
expect(user.name).toBe('Alice')
|
|
390
|
+
})
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
**Fix — Test the edges:**
|
|
394
|
+
```typescript
|
|
395
|
+
test('findUser returns user when ID exists', () => {
|
|
396
|
+
const user = findUser('existing-id')
|
|
397
|
+
expect(user.name).toBe('Alice')
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
test('findUser returns null when ID does not exist', () => {
|
|
401
|
+
const user = findUser('nonexistent-id')
|
|
402
|
+
expect(user).toBeNull()
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
test('findUser throws when ID is empty string', () => {
|
|
406
|
+
expect(() => findUser('')).toThrow(InvalidIdError)
|
|
407
|
+
})
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Why It Matters
|
|
411
|
+
|
|
412
|
+
Code without tests is code you cannot refactor. You cannot add features confidently because you do not know what you will break. Every change is a gamble. Tests are not about catching bugs today — they are about preventing regressions tomorrow and next month and next year.
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
## NAMING — Self-Documenting Code
|
|
417
|
+
|
|
418
|
+
### What It Means
|
|
419
|
+
|
|
420
|
+
Names are the primary documentation of code. A good name tells you what something does without reading the implementation. Use full words, not abbreviations. Name functions for their action, variables for their content, and types for their shape.
|
|
421
|
+
|
|
422
|
+
### What the Reviewer Checks
|
|
423
|
+
|
|
424
|
+
- **Abbreviations:** Are there names like `usr`, `btn`, `msg`, `cfg`, `ctx`? Use `user`, `button`, `message`, `config`, `context`.
|
|
425
|
+
- **Vague names:** Are there names like `data`, `result`, `info`, `item`, `temp`, `val`? Use specific names.
|
|
426
|
+
- **Boolean naming:** Do boolean variables read as questions? `isValid`, `hasPermission`, `canEdit` — not `valid`, `permission`, `edit`.
|
|
427
|
+
- **Function naming:** Do functions start with verbs? `calculateTotal`, `validateInput`, `fetchUser` — not `total`, `input`, `user`.
|
|
428
|
+
- **Consistency:** Is the same concept named the same way everywhere? Not `user` in one file and `account` in another for the same entity.
|
|
429
|
+
|
|
430
|
+
### Violations and Fixes
|
|
431
|
+
|
|
432
|
+
**Violation — Abbreviations and vague names:**
|
|
433
|
+
```typescript
|
|
434
|
+
function proc(d: any) {
|
|
435
|
+
const r = d.map((i: any) => i.v * i.q)
|
|
436
|
+
return r.reduce((a: number, b: number) => a + b, 0)
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
**Fix — Descriptive names:**
|
|
441
|
+
```typescript
|
|
442
|
+
function calculateOrderTotal(lineItems: LineItem[]): number {
|
|
443
|
+
const itemTotals = lineItems.map(item => item.price * item.quantity)
|
|
444
|
+
return itemTotals.reduce((sum, itemTotal) => sum + itemTotal, 0)
|
|
445
|
+
}
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Why It Matters
|
|
449
|
+
|
|
450
|
+
You read code 10x more than you write it. Every abbreviation costs 2 seconds of mental translation, multiplied by every developer who reads it, multiplied by every time they read it. Over a project's lifetime, bad names cost hundreds of hours.
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
## ERROR_HANDLING — No Silent Failures
|
|
455
|
+
|
|
456
|
+
### What It Means
|
|
457
|
+
|
|
458
|
+
When something goes wrong, the code must either handle it properly or tell someone about it. Empty catch blocks are banned — they hide bugs that surface weeks later in production. Use domain-specific exceptions that carry context. Never throw raw `Error` with a generic message.
|
|
459
|
+
|
|
460
|
+
### What the Reviewer Checks
|
|
461
|
+
|
|
462
|
+
- **Empty catch blocks:** Is there any `catch (e) {}` or `catch { }` in the codebase? This is unconditionally banned.
|
|
463
|
+
- **Swallowed exceptions:** Is there a catch block that logs but does not rethrow or return an error response?
|
|
464
|
+
- **Generic errors:** Are there `throw new Error('something went wrong')` instead of domain-specific exceptions?
|
|
465
|
+
- **Missing error handling:** Are there async operations without try/catch or .catch()?
|
|
466
|
+
- **Error messages:** Do error messages include enough context to diagnose the problem?
|
|
467
|
+
|
|
468
|
+
### Violations and Fixes
|
|
469
|
+
|
|
470
|
+
**Violation — Empty catch block:**
|
|
471
|
+
```typescript
|
|
472
|
+
try {
|
|
473
|
+
await saveUser(user)
|
|
474
|
+
} catch (e) {
|
|
475
|
+
// swallowed — user thinks save succeeded
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
**Fix — Handle or rethrow:**
|
|
480
|
+
```typescript
|
|
481
|
+
try {
|
|
482
|
+
await saveUser(user)
|
|
483
|
+
} catch (error) {
|
|
484
|
+
logger.error('Failed to save user', { userId: user.id, error })
|
|
485
|
+
throw new UserPersistenceError(`Failed to save user ${user.id}`, { cause: error })
|
|
486
|
+
}
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
**Violation — Generic error:**
|
|
490
|
+
```typescript
|
|
491
|
+
if (!user) {
|
|
492
|
+
throw new Error('Not found')
|
|
493
|
+
}
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
**Fix — Domain-specific exception:**
|
|
497
|
+
```typescript
|
|
498
|
+
if (!user) {
|
|
499
|
+
throw new UserNotFoundError(`User with ID ${userId} does not exist`)
|
|
500
|
+
}
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### Why It Matters
|
|
504
|
+
|
|
505
|
+
Swallowed errors are time bombs. The operation failed, but nothing reported it. Data is silently corrupted. Users see stale information. By the time someone notices, the root cause is buried under layers of subsequent operations. An error that screams immediately saves hours of debugging later.
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
---
|
|
509
|
+
|
|
510
|
+
## Field Documentation
|
|
511
|
+
|
|
512
|
+
### Severity Configuration
|
|
513
|
+
Each law has one of three severity levels:
|
|
514
|
+
- **error** — Violations of this law block `/sdlc:close`. The loop cannot complete until violations are fixed.
|
|
515
|
+
- **warning** — Violations are reported in the review but do not block loop closure. They should be addressed but are not critical.
|
|
516
|
+
- **info** — Violations are noted for awareness. No action required.
|
|
517
|
+
|
|
518
|
+
Severity can be changed per project to match team standards. For example, a prototype project might set TESTING to `warning` while a production API keeps it at `error`.
|
|
519
|
+
|
|
520
|
+
### Adding Custom Laws
|
|
521
|
+
Teams can add project-specific laws following the same format: name, severity, description, checks, examples, and rationale. Custom laws participate in the review process identically to built-in laws.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Project Context Template
|
|
2
|
+
|
|
3
|
+
This template defines the project context document stored at `.sdlc/PROJECT.md`. It is created during `/sdlc:init` and referenced throughout the lifecycle. Every field must be populated — incomplete project context leads to ambiguous specs.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
```markdown
|
|
8
|
+
# Project: {{PROJECT_NAME}}
|
|
9
|
+
|
|
10
|
+
## Description
|
|
11
|
+
|
|
12
|
+
{{PROJECT_DESCRIPTION}}
|
|
13
|
+
|
|
14
|
+
A 2-3 sentence summary of what this project does, who it serves, and what problem it solves.
|
|
15
|
+
|
|
16
|
+
## Tech Stack
|
|
17
|
+
|
|
18
|
+
| Layer | Technology | Version |
|
|
19
|
+
|-------|------------|---------|
|
|
20
|
+
| Language | {{LANGUAGE}} | {{VERSION}} |
|
|
21
|
+
| Framework | {{FRAMEWORK}} | {{VERSION}} |
|
|
22
|
+
| Database | {{DATABASE}} | {{VERSION}} |
|
|
23
|
+
| Package Manager | {{PACKAGE_MANAGER}} | {{VERSION}} |
|
|
24
|
+
| Test Runner | {{TEST_RUNNER}} | {{VERSION}} |
|
|
25
|
+
| Linter/Formatter | {{LINTER}} | {{VERSION}} |
|
|
26
|
+
| Build Tool | {{BUILD_TOOL}} | {{VERSION}} |
|
|
27
|
+
|
|
28
|
+
## Architecture Overview
|
|
29
|
+
|
|
30
|
+
{{ARCHITECTURE_DESCRIPTION}}
|
|
31
|
+
|
|
32
|
+
Describe the high-level architecture: monolith, microservices, serverless, etc.
|
|
33
|
+
Include key patterns: MVC, DDD, event-driven, CQRS, etc.
|
|
34
|
+
|
|
35
|
+
### Directory Structure
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
{{PROJECT_ROOT}}/
|
|
39
|
+
src/
|
|
40
|
+
{{DIRECTORY_LAYOUT}}
|
|
41
|
+
tests/
|
|
42
|
+
{{TEST_LAYOUT}}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Key Boundaries
|
|
46
|
+
|
|
47
|
+
- **Entry points:** {{ENTRY_POINTS}}
|
|
48
|
+
- **API layer:** {{API_LAYER}}
|
|
49
|
+
- **Business logic:** {{BUSINESS_LOGIC_LAYER}}
|
|
50
|
+
- **Data access:** {{DATA_ACCESS_LAYER}}
|
|
51
|
+
- **External integrations:** {{EXTERNAL_INTEGRATIONS}}
|
|
52
|
+
|
|
53
|
+
## Requirements
|
|
54
|
+
|
|
55
|
+
| ID | Requirement | Priority | Status |
|
|
56
|
+
|----|-------------|----------|--------|
|
|
57
|
+
| REQ-001 | {{REQUIREMENT_DESCRIPTION}} | {{high/medium/low}} | {{active/validated/invalidated}} |
|
|
58
|
+
| REQ-002 | {{REQUIREMENT_DESCRIPTION}} | {{high/medium/low}} | {{active/validated/invalidated}} |
|
|
59
|
+
|
|
60
|
+
### Status Definitions
|
|
61
|
+
|
|
62
|
+
- **active** — Requirement is current and guides development
|
|
63
|
+
- **validated** — Requirement has been implemented and verified
|
|
64
|
+
- **invalidated** — Requirement was dropped or replaced (include reason)
|
|
65
|
+
|
|
66
|
+
## Current State
|
|
67
|
+
|
|
68
|
+
- **Phase:** {{init/active/maintenance}}
|
|
69
|
+
- **Last milestone completed:** {{MILESTONE_NAME or "None"}}
|
|
70
|
+
- **Active milestone:** {{MILESTONE_NAME or "None"}}
|
|
71
|
+
- **Known issues:** {{COUNT or "None"}}
|
|
72
|
+
- **Test coverage:** {{PERCENTAGE or "Unknown"}}
|
|
73
|
+
|
|
74
|
+
## Constraints
|
|
75
|
+
|
|
76
|
+
| Constraint | Description | Impact |
|
|
77
|
+
|------------|-------------|--------|
|
|
78
|
+
| {{CONSTRAINT_NAME}} | {{WHAT_AND_WHY}} | {{HOW_IT_AFFECTS_DEVELOPMENT}} |
|
|
79
|
+
|
|
80
|
+
### Common Constraint Categories
|
|
81
|
+
|
|
82
|
+
- **Performance:** Response time limits, throughput requirements
|
|
83
|
+
- **Security:** Compliance requirements, data handling rules
|
|
84
|
+
- **Compatibility:** Browser support, API versioning, backward compatibility
|
|
85
|
+
- **Infrastructure:** Hosting limits, budget, CI/CD pipeline constraints
|
|
86
|
+
- **Team:** Skill level, availability, timezone considerations
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Field Documentation
|
|
92
|
+
|
|
93
|
+
### PROJECT_NAME
|
|
94
|
+
The canonical name of the project. Use the name from `package.json`, `Cargo.toml`, or equivalent manifest. If no manifest exists, use the root directory name.
|
|
95
|
+
|
|
96
|
+
### PROJECT_DESCRIPTION
|
|
97
|
+
Plain English summary. Avoid jargon. A new team member should understand what this project does after reading this field.
|
|
98
|
+
|
|
99
|
+
### Tech Stack Table
|
|
100
|
+
Every technology the project depends on for development and deployment. Include versions — version mismatches cause subtle bugs that waste hours.
|
|
101
|
+
|
|
102
|
+
### Architecture Overview
|
|
103
|
+
Not a design doc. A quick orientation so that anyone reading a spec knows where code lives and how layers interact.
|
|
104
|
+
|
|
105
|
+
### Requirements
|
|
106
|
+
Living document. Requirements move from `active` to `validated` when implemented and verified. They move to `invalidated` when scope changes — never delete them, mark them so there is an audit trail.
|
|
107
|
+
|
|
108
|
+
### Current State
|
|
109
|
+
Updated at the end of each loop closure. Provides instant orientation for session resume.
|
|
110
|
+
|
|
111
|
+
### Constraints
|
|
112
|
+
Hard limits that specs must respect. If a spec violates a constraint, the reviewer catches it. Constraints are not negotiable without explicit stakeholder approval.
|