spec-lite 1.1.0 → 1.1.2
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/README.md +72 -21
- package/bin/cli.js +1 -1
- package/package.json +1 -1
- package/skills/code-review-and-quality/SKILL.md +347 -0
- package/skills/performance-optimization/SKILL.md +350 -0
- package/skills/review/SKILL.md +30 -0
- package/skills/security-and-hardening/SKILL.md +349 -0
- package/skills/spec-brownfield-component/SKILL.md +266 -0
- package/skills/spec-brownfield-feature/SKILL.md +239 -0
- package/skills/spec-brownfield-init/SKILL.md +312 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: security-and-hardening
|
|
3
|
+
description: Hardens code against vulnerabilities. Use when handling user input, authentication, data storage, or external integrations. Use when building any feature that accepts untrusted data, manages user sessions, or interacts with third-party services.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Security and Hardening
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Security-first development practices for web applications. Treat every external input as hostile, every secret as sacred, and every authorization check as mandatory. Security isn't a phase — it's a constraint on every line of code that touches user data, authentication, or external systems.
|
|
11
|
+
|
|
12
|
+
## When to Use
|
|
13
|
+
|
|
14
|
+
- Building anything that accepts user input
|
|
15
|
+
- Implementing authentication or authorization
|
|
16
|
+
- Storing or transmitting sensitive data
|
|
17
|
+
- Integrating with external APIs or services
|
|
18
|
+
- Adding file uploads, webhooks, or callbacks
|
|
19
|
+
- Handling payment or PII data
|
|
20
|
+
|
|
21
|
+
## The Three-Tier Boundary System
|
|
22
|
+
|
|
23
|
+
### Always Do (No Exceptions)
|
|
24
|
+
|
|
25
|
+
- **Validate all external input** at the system boundary (API routes, form handlers)
|
|
26
|
+
- **Parameterize all database queries** — never concatenate user input into SQL
|
|
27
|
+
- **Encode output** to prevent XSS (use framework auto-escaping, don't bypass it)
|
|
28
|
+
- **Use HTTPS** for all external communication
|
|
29
|
+
- **Hash passwords** with bcrypt/scrypt/argon2 (never store plaintext)
|
|
30
|
+
- **Set security headers** (CSP, HSTS, X-Frame-Options, X-Content-Type-Options)
|
|
31
|
+
- **Use httpOnly, secure, sameSite cookies** for sessions
|
|
32
|
+
- **Run `npm audit`** (or equivalent) before every release
|
|
33
|
+
|
|
34
|
+
### Ask First (Requires Human Approval)
|
|
35
|
+
|
|
36
|
+
- Adding new authentication flows or changing auth logic
|
|
37
|
+
- Storing new categories of sensitive data (PII, payment info)
|
|
38
|
+
- Adding new external service integrations
|
|
39
|
+
- Changing CORS configuration
|
|
40
|
+
- Adding file upload handlers
|
|
41
|
+
- Modifying rate limiting or throttling
|
|
42
|
+
- Granting elevated permissions or roles
|
|
43
|
+
|
|
44
|
+
### Never Do
|
|
45
|
+
|
|
46
|
+
- **Never commit secrets** to version control (API keys, passwords, tokens)
|
|
47
|
+
- **Never log sensitive data** (passwords, tokens, full credit card numbers)
|
|
48
|
+
- **Never trust client-side validation** as a security boundary
|
|
49
|
+
- **Never disable security headers** for convenience
|
|
50
|
+
- **Never use `eval()` or `innerHTML`** with user-provided data
|
|
51
|
+
- **Never store sessions in client-accessible storage** (localStorage for auth tokens)
|
|
52
|
+
- **Never expose stack traces** or internal error details to users
|
|
53
|
+
|
|
54
|
+
## OWASP Top 10 Prevention
|
|
55
|
+
|
|
56
|
+
### 1. Injection (SQL, NoSQL, OS Command)
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// BAD: SQL injection via string concatenation
|
|
60
|
+
const query = `SELECT * FROM users WHERE id = '${userId}'`;
|
|
61
|
+
|
|
62
|
+
// GOOD: Parameterized query
|
|
63
|
+
const user = await db.query('SELECT * FROM users WHERE id = $1', [userId]);
|
|
64
|
+
|
|
65
|
+
// GOOD: ORM with parameterized input
|
|
66
|
+
const user = await prisma.user.findUnique({ where: { id: userId } });
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2. Broken Authentication
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// Password hashing
|
|
73
|
+
import { hash, compare } from 'bcrypt';
|
|
74
|
+
|
|
75
|
+
const SALT_ROUNDS = 12;
|
|
76
|
+
const hashedPassword = await hash(plaintext, SALT_ROUNDS);
|
|
77
|
+
const isValid = await compare(plaintext, hashedPassword);
|
|
78
|
+
|
|
79
|
+
// Session management
|
|
80
|
+
app.use(session({
|
|
81
|
+
secret: process.env.SESSION_SECRET, // From environment, not code
|
|
82
|
+
resave: false,
|
|
83
|
+
saveUninitialized: false,
|
|
84
|
+
cookie: {
|
|
85
|
+
httpOnly: true, // Not accessible via JavaScript
|
|
86
|
+
secure: true, // HTTPS only
|
|
87
|
+
sameSite: 'lax', // CSRF protection
|
|
88
|
+
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
|
89
|
+
},
|
|
90
|
+
}));
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 3. Cross-Site Scripting (XSS)
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
// BAD: Rendering user input as HTML
|
|
97
|
+
element.innerHTML = userInput;
|
|
98
|
+
|
|
99
|
+
// GOOD: Use framework auto-escaping (React does this by default)
|
|
100
|
+
return <div>{userInput}</div>;
|
|
101
|
+
|
|
102
|
+
// If you MUST render HTML, sanitize first
|
|
103
|
+
import DOMPurify from 'dompurify';
|
|
104
|
+
const clean = DOMPurify.sanitize(userInput);
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 4. Broken Access Control
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
// Always check authorization, not just authentication
|
|
111
|
+
app.patch('/api/tasks/:id', authenticate, async (req, res) => {
|
|
112
|
+
const task = await taskService.findById(req.params.id);
|
|
113
|
+
|
|
114
|
+
// Check that the authenticated user owns this resource
|
|
115
|
+
if (task.ownerId !== req.user.id) {
|
|
116
|
+
return res.status(403).json({
|
|
117
|
+
error: { code: 'FORBIDDEN', message: 'Not authorized to modify this task' }
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Proceed with update
|
|
122
|
+
const updated = await taskService.update(req.params.id, req.body);
|
|
123
|
+
return res.json(updated);
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 5. Security Misconfiguration
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// Security headers (use helmet for Express)
|
|
131
|
+
import helmet from 'helmet';
|
|
132
|
+
app.use(helmet());
|
|
133
|
+
|
|
134
|
+
// Content Security Policy
|
|
135
|
+
app.use(helmet.contentSecurityPolicy({
|
|
136
|
+
directives: {
|
|
137
|
+
defaultSrc: ["'self'"],
|
|
138
|
+
scriptSrc: ["'self'"],
|
|
139
|
+
styleSrc: ["'self'", "'unsafe-inline'"], // Tighten if possible
|
|
140
|
+
imgSrc: ["'self'", 'data:', 'https:'],
|
|
141
|
+
connectSrc: ["'self'"],
|
|
142
|
+
},
|
|
143
|
+
}));
|
|
144
|
+
|
|
145
|
+
// CORS — restrict to known origins
|
|
146
|
+
app.use(cors({
|
|
147
|
+
origin: process.env.ALLOWED_ORIGINS?.split(',') || 'http://localhost:3000',
|
|
148
|
+
credentials: true,
|
|
149
|
+
}));
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 6. Sensitive Data Exposure
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
// Never return sensitive fields in API responses
|
|
156
|
+
function sanitizeUser(user: UserRecord): PublicUser {
|
|
157
|
+
const { passwordHash, resetToken, ...publicFields } = user;
|
|
158
|
+
return publicFields;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Use environment variables for secrets
|
|
162
|
+
const API_KEY = process.env.STRIPE_API_KEY;
|
|
163
|
+
if (!API_KEY) throw new Error('STRIPE_API_KEY not configured');
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Input Validation Patterns
|
|
167
|
+
|
|
168
|
+
### Schema Validation at Boundaries
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
import { z } from 'zod';
|
|
172
|
+
|
|
173
|
+
const CreateTaskSchema = z.object({
|
|
174
|
+
title: z.string().min(1).max(200).trim(),
|
|
175
|
+
description: z.string().max(2000).optional(),
|
|
176
|
+
priority: z.enum(['low', 'medium', 'high']).default('medium'),
|
|
177
|
+
dueDate: z.string().datetime().optional(),
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Validate at the route handler
|
|
181
|
+
app.post('/api/tasks', async (req, res) => {
|
|
182
|
+
const result = CreateTaskSchema.safeParse(req.body);
|
|
183
|
+
if (!result.success) {
|
|
184
|
+
return res.status(422).json({
|
|
185
|
+
error: {
|
|
186
|
+
code: 'VALIDATION_ERROR',
|
|
187
|
+
message: 'Invalid input',
|
|
188
|
+
details: result.error.flatten(),
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
// result.data is now typed and validated
|
|
193
|
+
const task = await taskService.create(result.data);
|
|
194
|
+
return res.status(201).json(task);
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### File Upload Safety
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
// Restrict file types and sizes
|
|
202
|
+
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp'];
|
|
203
|
+
const MAX_SIZE = 5 * 1024 * 1024; // 5MB
|
|
204
|
+
|
|
205
|
+
function validateUpload(file: UploadedFile) {
|
|
206
|
+
if (!ALLOWED_TYPES.includes(file.mimetype)) {
|
|
207
|
+
throw new ValidationError('File type not allowed');
|
|
208
|
+
}
|
|
209
|
+
if (file.size > MAX_SIZE) {
|
|
210
|
+
throw new ValidationError('File too large (max 5MB)');
|
|
211
|
+
}
|
|
212
|
+
// Don't trust the file extension — check magic bytes if critical
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Triaging npm audit Results
|
|
217
|
+
|
|
218
|
+
Not all audit findings require immediate action. Use this decision tree:
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
npm audit reports a vulnerability
|
|
222
|
+
├── Severity: critical or high
|
|
223
|
+
│ ├── Is the vulnerable code reachable in your app?
|
|
224
|
+
│ │ ├── YES --> Fix immediately (update, patch, or replace the dependency)
|
|
225
|
+
│ │ └── NO (dev-only dep, unused code path) --> Fix soon, but not a blocker
|
|
226
|
+
│ └── Is a fix available?
|
|
227
|
+
│ ├── YES --> Update to the patched version
|
|
228
|
+
│ └── NO --> Check for workarounds, consider replacing the dependency, or add to allowlist with a review date
|
|
229
|
+
├── Severity: moderate
|
|
230
|
+
│ ├── Reachable in production? --> Fix in the next release cycle
|
|
231
|
+
│ └── Dev-only? --> Fix when convenient, track in backlog
|
|
232
|
+
└── Severity: low
|
|
233
|
+
└── Track and fix during regular dependency updates
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Key questions:**
|
|
237
|
+
- Is the vulnerable function actually called in your code path?
|
|
238
|
+
- Is the dependency a runtime dependency or dev-only?
|
|
239
|
+
- Is the vulnerability exploitable given your deployment context (e.g., a server-side vulnerability in a client-only app)?
|
|
240
|
+
|
|
241
|
+
When you defer a fix, document the reason and set a review date.
|
|
242
|
+
|
|
243
|
+
## Rate Limiting
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
import rateLimit from 'express-rate-limit';
|
|
247
|
+
|
|
248
|
+
// General API rate limit
|
|
249
|
+
app.use('/api/', rateLimit({
|
|
250
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
251
|
+
max: 100, // 100 requests per window
|
|
252
|
+
standardHeaders: true,
|
|
253
|
+
legacyHeaders: false,
|
|
254
|
+
}));
|
|
255
|
+
|
|
256
|
+
// Stricter limit for auth endpoints
|
|
257
|
+
app.use('/api/auth/', rateLimit({
|
|
258
|
+
windowMs: 15 * 60 * 1000,
|
|
259
|
+
max: 10, // 10 attempts per 15 minutes
|
|
260
|
+
}));
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Secrets Management
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
.env files:
|
|
267
|
+
├── .env.example → Committed (template with placeholder values)
|
|
268
|
+
├── .env → NOT committed (contains real secrets)
|
|
269
|
+
└── .env.local → NOT committed (local overrides)
|
|
270
|
+
|
|
271
|
+
.gitignore must include:
|
|
272
|
+
.env
|
|
273
|
+
.env.local
|
|
274
|
+
.env.*.local
|
|
275
|
+
*.pem
|
|
276
|
+
*.key
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**Always check before committing:**
|
|
280
|
+
```bash
|
|
281
|
+
# Check for accidentally staged secrets
|
|
282
|
+
git diff --cached | grep -i "password\|secret\|api_key\|token"
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## Security Review Checklist
|
|
286
|
+
|
|
287
|
+
```markdown
|
|
288
|
+
### Authentication
|
|
289
|
+
- [ ] Passwords hashed with bcrypt/scrypt/argon2 (salt rounds ≥ 12)
|
|
290
|
+
- [ ] Session tokens are httpOnly, secure, sameSite
|
|
291
|
+
- [ ] Login has rate limiting
|
|
292
|
+
- [ ] Password reset tokens expire
|
|
293
|
+
|
|
294
|
+
### Authorization
|
|
295
|
+
- [ ] Every endpoint checks user permissions
|
|
296
|
+
- [ ] Users can only access their own resources
|
|
297
|
+
- [ ] Admin actions require admin role verification
|
|
298
|
+
|
|
299
|
+
### Input
|
|
300
|
+
- [ ] All user input validated at the boundary
|
|
301
|
+
- [ ] SQL queries are parameterized
|
|
302
|
+
- [ ] HTML output is encoded/escaped
|
|
303
|
+
|
|
304
|
+
### Data
|
|
305
|
+
- [ ] No secrets in code or version control
|
|
306
|
+
- [ ] Sensitive fields excluded from API responses
|
|
307
|
+
- [ ] PII encrypted at rest (if applicable)
|
|
308
|
+
|
|
309
|
+
### Infrastructure
|
|
310
|
+
- [ ] Security headers configured (CSP, HSTS, etc.)
|
|
311
|
+
- [ ] CORS restricted to known origins
|
|
312
|
+
- [ ] Dependencies audited for vulnerabilities
|
|
313
|
+
- [ ] Error messages don't expose internals
|
|
314
|
+
```
|
|
315
|
+
## See Also
|
|
316
|
+
|
|
317
|
+
For detailed security checklists and pre-commit verification steps, see `references/security-checklist.md`.
|
|
318
|
+
|
|
319
|
+
## Common Rationalizations
|
|
320
|
+
|
|
321
|
+
| Rationalization | Reality |
|
|
322
|
+
|---|---|
|
|
323
|
+
| "This is an internal tool, security doesn't matter" | Internal tools get compromised. Attackers target the weakest link. |
|
|
324
|
+
| "We'll add security later" | Security retrofitting is 10x harder than building it in. Add it now. |
|
|
325
|
+
| "No one would try to exploit this" | Automated scanners will find it. Security by obscurity is not security. |
|
|
326
|
+
| "The framework handles security" | Frameworks provide tools, not guarantees. You still need to use them correctly. |
|
|
327
|
+
| "It's just a prototype" | Prototypes become production. Security habits from day one. |
|
|
328
|
+
|
|
329
|
+
## Red Flags
|
|
330
|
+
|
|
331
|
+
- User input passed directly to database queries, shell commands, or HTML rendering
|
|
332
|
+
- Secrets in source code or commit history
|
|
333
|
+
- API endpoints without authentication or authorization checks
|
|
334
|
+
- Missing CORS configuration or wildcard (`*`) origins
|
|
335
|
+
- No rate limiting on authentication endpoints
|
|
336
|
+
- Stack traces or internal errors exposed to users
|
|
337
|
+
- Dependencies with known critical vulnerabilities
|
|
338
|
+
|
|
339
|
+
## Verification
|
|
340
|
+
|
|
341
|
+
After implementing security-relevant code:
|
|
342
|
+
|
|
343
|
+
- [ ] `npm audit` shows no critical or high vulnerabilities
|
|
344
|
+
- [ ] No secrets in source code or git history
|
|
345
|
+
- [ ] All user input validated at system boundaries
|
|
346
|
+
- [ ] Authentication and authorization checked on every protected endpoint
|
|
347
|
+
- [ ] Security headers present in response (check with browser DevTools)
|
|
348
|
+
- [ ] Error responses don't expose internal details
|
|
349
|
+
- [ ] Rate limiting active on auth endpoints
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: spec-brownfield-component
|
|
3
|
+
description: Discover components từ code, generate crd.md + cdd.md cho từng component, điền Component Index vào prd.md. Phải chạy /spec-brownfield-init trước.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# spec-brownfield-component
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Tạo `specs/main/component/{C-XXX}-{component-name}/crd.md` và `cdd.md` cho các components trong brownfield project.
|
|
11
|
+
|
|
12
|
+
Skill này **tự discover** component boundaries từ code (không dựa vào init), sau đó scan sâu từng component để extract responsibilities, entities, public interface, business rules. Khi hoàn thành, **điền Component Index vào prd.md** và **đề xuất Shared Entities cascade vào domain.md**.
|
|
13
|
+
|
|
14
|
+
## When to Use
|
|
15
|
+
|
|
16
|
+
- Sau khi `/spec-brownfield-init` đã chạy và `prd.md` tồn tại
|
|
17
|
+
- Cần generate component artifacts cho brownfield project
|
|
18
|
+
|
|
19
|
+
## When NOT to Use
|
|
20
|
+
|
|
21
|
+
- `prd.md` chưa có → chạy `/spec-brownfield-init` trước
|
|
22
|
+
- Greenfield project → component artifacts mọc dần qua cascade từ integrations (`/spec-new`)
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Process
|
|
27
|
+
|
|
28
|
+
### Bước 1: Load context và xác định path
|
|
29
|
+
|
|
30
|
+
**Path:** Nếu có ARGUMENT là path → dùng làm root. Nếu không → CWD.
|
|
31
|
+
|
|
32
|
+
**Load:**
|
|
33
|
+
- `specs/main/prd.md` — kiểm tra tồn tại; đọc problem statement và NFRs để hiểu business context
|
|
34
|
+
- `specs/main/domain.md` — đọc Glossary để reuse business terms
|
|
35
|
+
|
|
36
|
+
Nếu `prd.md` không tồn tại → dừng:
|
|
37
|
+
> `prd.md` chưa có nội dung. Hãy chạy `/spec-brownfield-init` trước.
|
|
38
|
+
|
|
39
|
+
**Attachments:**
|
|
40
|
+
> Bạn có tài liệu bổ sung nào không? (API docs, architecture diagrams, design docs)
|
|
41
|
+
> Nhập `không` để bỏ qua.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
### Bước 2: Discover component boundaries
|
|
46
|
+
|
|
47
|
+
Scan directory structure để tìm component candidates. Thử các patterns sau theo thứ tự:
|
|
48
|
+
|
|
49
|
+
1. `src/modules/*/` — modular structure (NestJS, v.v.)
|
|
50
|
+
2. `src/*/` — flat module structure (nếu có `service` hoặc `controller` bên trong)
|
|
51
|
+
3. `services/*/` — microservices
|
|
52
|
+
4. `packages/*/` — monorepo packages
|
|
53
|
+
5. `apps/*/` — monorepo apps
|
|
54
|
+
|
|
55
|
+
Với mỗi candidate directory, xác nhận đây là component bằng cách kiểm tra có ít nhất một trong: `*controller*`, `*service*`, `*repository*`, `*handler*`, `*module*` file bên trong.
|
|
56
|
+
|
|
57
|
+
Ghi lại cho mỗi component candidate:
|
|
58
|
+
- Directory path
|
|
59
|
+
- File count
|
|
60
|
+
- Patterns detected: controller / service / repository / event publisher / consumer
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
### Bước 3: Confirm component list
|
|
65
|
+
|
|
66
|
+
Trình bày kết quả discovery cho user confirm:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
Tôi phát hiện {N} components từ codebase:
|
|
70
|
+
[C-001] auth → src/modules/auth/ (15 files: controller+service+repo)
|
|
71
|
+
[C-002] user → src/modules/user/ (12 files: controller+service+repo)
|
|
72
|
+
[C-003] payment → src/modules/payment/ (8 files: service+repo)
|
|
73
|
+
[C-004] notification → src/modules/notification/ (6 files: service+event)
|
|
74
|
+
|
|
75
|
+
Điều chỉnh nếu cần:
|
|
76
|
+
- Đổi tên: "C-002 → profile" hoặc nhập tên khác
|
|
77
|
+
- Bỏ component: "bỏ C-004"
|
|
78
|
+
- Thêm component: "thêm reporting tại src/reporting/"
|
|
79
|
+
- Merge: "merge C-001 và C-002 thành auth-user"
|
|
80
|
+
|
|
81
|
+
Confirm danh sách? [y] hoặc nhập điều chỉnh:
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Sau khi confirm, assign C-XXX IDs theo thứ tự tăng dần.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
### Bước 4: Scan sâu từng component
|
|
89
|
+
|
|
90
|
+
Với mỗi component, đọc code files bên trong để extract:
|
|
91
|
+
|
|
92
|
+
**4a. Responsibilities:**
|
|
93
|
+
- Public method names trong service files
|
|
94
|
+
- Class-level comments / JSDoc nếu có
|
|
95
|
+
- Export declarations
|
|
96
|
+
|
|
97
|
+
**4b. Owned Entities:**
|
|
98
|
+
- Entity / model files trong component directory
|
|
99
|
+
- Decorators: `@Entity()` (TypeORM), Prisma model blocks, `@Schema()` (Mongoose), SQLAlchemy models
|
|
100
|
+
- Migration files gắn với component
|
|
101
|
+
|
|
102
|
+
**4c. Public Interface:**
|
|
103
|
+
- Controller files: HTTP method + path mỗi endpoint
|
|
104
|
+
- Event publisher: `@EventPattern`, `eventEmitter.emit()`, message queue producers
|
|
105
|
+
- Exported service methods nếu là shared library
|
|
106
|
+
|
|
107
|
+
**4d. Dependencies:**
|
|
108
|
+
- Import statements referencing other component directories
|
|
109
|
+
- Injectable services từ other modules
|
|
110
|
+
- HTTP client calls đến other services
|
|
111
|
+
|
|
112
|
+
**4e. Business Rules:**
|
|
113
|
+
- Validation decorators (`@Min`, `@Max`, `@IsEmail`, custom validators)
|
|
114
|
+
- Guard conditions trong service (`if (!user.isActive) throw ...`)
|
|
115
|
+
- Domain events và điều kiện trigger
|
|
116
|
+
- Comments có từ khóa: "must", "should", "only", "never", "không được"
|
|
117
|
+
|
|
118
|
+
**4f. Internal design (cho cdd.md):**
|
|
119
|
+
- Module structure: list sub-modules / file groups
|
|
120
|
+
- ORM/storage usage: repository pattern, raw queries, cache decorators
|
|
121
|
+
- Internal service/repository interface contracts
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
### Bước 5: Identify cross-component entities
|
|
126
|
+
|
|
127
|
+
Trong quá trình scan, ghi nhận các entities được **import hoặc reference từ nhiều hơn 1 component** → đây là candidate Shared Entities cho `domain.md`.
|
|
128
|
+
|
|
129
|
+
Ví dụ: `Order` được dùng trong `payment/` và `inventory/` → candidate Shared Entity, cần xác định owner.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### Bước 6: Hỏi clarification — batch
|
|
134
|
+
|
|
135
|
+
Sau khi scan xong tất cả components, hỏi một lần — tối đa **5 câu tổng cộng**. Ưu tiên những điểm ảnh hưởng đến component boundaries hoặc entity ownership.
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
Cần làm rõ {N} điểm:
|
|
139
|
+
|
|
140
|
+
[C-001 auth]
|
|
141
|
+
Q1. Method `validateToken` được call từ cả auth/ và user/ —
|
|
142
|
+
đây là public interface hay internal helper?
|
|
143
|
+
[public / internal / skip]
|
|
144
|
+
|
|
145
|
+
[cross-component]
|
|
146
|
+
Q2. Entity `Order` được dùng trong payment/ và inventory/ —
|
|
147
|
+
component nào sở hữu?
|
|
148
|
+
[payment / inventory / skip]
|
|
149
|
+
|
|
150
|
+
Trả lời hoặc "skip all" để đánh dấu NEEDS_CLARIFY hết:
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
### Bước 7: Generate artifacts
|
|
156
|
+
|
|
157
|
+
Với mỗi component, generate 2 files dùng templates tương ứng:
|
|
158
|
+
|
|
159
|
+
**`crd.md`** — WHAT (dùng `.claude/templates/main/component/crd-template.md`):
|
|
160
|
+
- Role, Responsibilities, Owned Entities, Public Interface, Business Rules, Domain Events, Dependencies
|
|
161
|
+
|
|
162
|
+
**`cdd.md`** — HOW (dùng `.claude/templates/main/component/cdd-template.md`):
|
|
163
|
+
- Module structure, Internal data flow, Storage & persistence, Internal interfaces, Technical decisions
|
|
164
|
+
|
|
165
|
+
**Nguyên tắc:**
|
|
166
|
+
- Mọi phần không detect được → NEEDS_CLARIFY, không bịa
|
|
167
|
+
- Business rules detect được từ code → đánh dấu `(detected from code)`
|
|
168
|
+
- crd.md không chứa internal design; cdd.md không chứa public interface
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
### Bước 8: Cascade Proposals — domain.md
|
|
173
|
+
|
|
174
|
+
Nếu có Shared Entities được phát hiện ở Bước 5:
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
178
|
+
SHARED ENTITIES DETECTED
|
|
179
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
180
|
+
Các entities sau được dùng bởi nhiều components.
|
|
181
|
+
Đề xuất thêm vào specs/main/domain.md (Shared Entities):
|
|
182
|
+
|
|
183
|
+
Entity: Order (Owner: C-003-payment)
|
|
184
|
+
Dùng bởi: C-003-payment, C-005-inventory
|
|
185
|
+
Fields detected: id, userId, items[], status, totalAmount
|
|
186
|
+
|
|
187
|
+
Entity: User (Owner: C-001-auth)
|
|
188
|
+
Dùng bởi: C-001-auth, C-002-user, C-004-notification
|
|
189
|
+
Fields detected: id, email, role, isActive
|
|
190
|
+
|
|
191
|
+
Apply cascade proposals vào domain.md? [y/n/edit]
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Nếu `y` → update `specs/main/domain.md` Shared Entities section.
|
|
195
|
+
Nếu `edit` → hỏi chỉnh sửa từng entity trước khi apply.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
### Bước 9: Review và save artifacts
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
Sẽ tạo {N} components ({2N} files):
|
|
203
|
+
✓ specs/main/component/C-001-auth/crd.md (3 NEEDS_CLARIFY)
|
|
204
|
+
✓ specs/main/component/C-001-auth/cdd.md (1 NEEDS_CLARIFY)
|
|
205
|
+
✓ specs/main/component/C-002-user/crd.md (0 NEEDS_CLARIFY)
|
|
206
|
+
✓ specs/main/component/C-002-user/cdd.md (2 NEEDS_CLARIFY)
|
|
207
|
+
...
|
|
208
|
+
|
|
209
|
+
Ghi tất cả? [y] hoặc review từng file trước [r]:
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
- `y` → ghi tất cả
|
|
213
|
+
- `r` → hiển thị lần lượt từng file, user confirm / skip / edit từng cái
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
### Bước 10: Điền Component Index vào prd.md
|
|
218
|
+
|
|
219
|
+
Sau khi artifacts đã được ghi, update `specs/main/prd.md` Component Index với danh sách đã confirm:
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
Sẽ điền Component Index vào prd.md:
|
|
223
|
+
C-001 | auth | Xác thực và phân quyền người dùng | active
|
|
224
|
+
C-002 | user | Quản lý thông tin người dùng | active
|
|
225
|
+
C-003 | payment | Xử lý thanh toán và giao dịch | active
|
|
226
|
+
C-004 | notification | Gửi thông báo qua email/push | active
|
|
227
|
+
|
|
228
|
+
Apply? [y/n]
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
### Bước 11: Summary
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
✓ Generated {N} components:
|
|
237
|
+
specs/main/component/C-001-auth/ (crd.md + cdd.md)
|
|
238
|
+
specs/main/component/C-002-user/ (crd.md + cdd.md)
|
|
239
|
+
...
|
|
240
|
+
|
|
241
|
+
✓ prd.md Component Index: {N} entries đã được điền
|
|
242
|
+
|
|
243
|
+
{nếu có} ✓ domain.md Shared Entities: {N} entities đã được cascade
|
|
244
|
+
|
|
245
|
+
NEEDS_CLARIFY summary — {total} items:
|
|
246
|
+
C-001-auth: {n} — [business rules, domain events, ...]
|
|
247
|
+
C-002-user: {n} — [entity ownership, ...]
|
|
248
|
+
...
|
|
249
|
+
|
|
250
|
+
Bước tiếp theo:
|
|
251
|
+
/spec-brownfield-feature → discover features, generate frd.md + fdd.md, điền Feature Index
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Verification
|
|
257
|
+
|
|
258
|
+
- [ ] Mỗi component có đủ crd.md + cdd.md
|
|
259
|
+
- [ ] crd.md không chứa internal design (đã ở cdd.md)
|
|
260
|
+
- [ ] cdd.md không chứa public interface (đã ở crd.md)
|
|
261
|
+
- [ ] Mọi entity trong crd.md có đúng 1 owner component (hoặc NEEDS_CLARIFY)
|
|
262
|
+
- [ ] Dependencies chỉ reference C-XXX IDs, không reference file paths
|
|
263
|
+
- [ ] Business rules detect được đánh dấu `(detected from code)`
|
|
264
|
+
- [ ] prd.md Component Index đã được update
|
|
265
|
+
- [ ] Shared Entities đã được cascade vào domain.md (nếu có)
|
|
266
|
+
- [ ] NEEDS_CLARIFY items được list đầy đủ trong summary
|