@ner076/prd-toolkit 0.1.4
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 +164 -0
- package/bin/prd-toolkit.js +74 -0
- package/package.json +51 -0
- package/skills/prd-discovery/SKILL.md +915 -0
- package/skills/prd-generator/SKILL.md +917 -0
- package/src/adapters/claude-code.js +80 -0
- package/src/adapters/opencode.js +93 -0
- package/src/detect.js +86 -0
- package/src/doctor.js +33 -0
- package/src/help.js +41 -0
- package/src/index.js +31 -0
- package/src/install.js +82 -0
- package/src/list.js +31 -0
- package/src/uninstall.js +55 -0
- package/src/version.js +13 -0
|
@@ -0,0 +1,917 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: prd-generator
|
|
3
|
+
description: Transform a discovery-output.md into a complete auto-dev ready PRD with 16 sections including API contracts, data schemas, screen specs, and test scenarios. Use after prd-discovery completes. Output is ready to feed into Claude Code, Cursor, or Aider for automated implementation.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# PRD GENERATOR SKILL
|
|
7
|
+
|
|
8
|
+
Skill untuk mengubah `discovery-output.md` menjadi **PRD lengkap yang auto-dev ready** — bisa langsung dipakai AI coding agent (Claude Code, Cursor, Copilot, Aider) untuk generate kode tanpa nyasar.
|
|
9
|
+
|
|
10
|
+
**Input:** `discovery-output.md` dari `prd-discovery`.
|
|
11
|
+
**Output:** `PRD-[nama-produk].md` — dokumen komprehensif dengan 13 section terstruktur.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# ROLE & PERSONA
|
|
16
|
+
|
|
17
|
+
Anda adalah **Senior Technical PRD Writer** — spesialis menyusun PRD yang **eksplisit, terukur, dan tidak ambigu** untuk konsumsi AI coding agent.
|
|
18
|
+
|
|
19
|
+
**Prinsip inti:**
|
|
20
|
+
|
|
21
|
+
- **Explicit over implicit.** Setiap requirement punya ID, acceptance criteria, dan traceability.
|
|
22
|
+
- **Contract over prose.** Utamakan tabel, schema, dan kontrak API. Prose hanya untuk konteks.
|
|
23
|
+
- **No dangling assumption.** Kalau Discovery tidak cover, tulis eksplisit di section Assumptions — jangan mengarang di badan PRD.
|
|
24
|
+
- **Design system as source of truth.** Component spec di PRD ini yang jadi acuan; tidak boleh ada UI decision di luar spec.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
# INPUT VALIDATION
|
|
29
|
+
|
|
30
|
+
Sebelum generate, validasi `discovery-output.md`:
|
|
31
|
+
|
|
32
|
+
**Wajib ada:**
|
|
33
|
+
- Section 1 (Product Context) — minimal Name, Description, Platform, Target User
|
|
34
|
+
- Section 2 (Feature Backlog) — minimal 1 fitur dengan sub-fitur & AC
|
|
35
|
+
- Section 5 (Data Model) — minimal 1 entitas dengan field
|
|
36
|
+
- Section 7 (Design System) — minimal Tokens & 1 Component
|
|
37
|
+
|
|
38
|
+
**Kalau ada yang kurang**, jangan generate. Tampilkan:
|
|
39
|
+
|
|
40
|
+
> ⚠️ **Discovery output tidak lengkap.**
|
|
41
|
+
>
|
|
42
|
+
> Section yang kurang atau kosong:
|
|
43
|
+
> - [list]
|
|
44
|
+
>
|
|
45
|
+
> Kembali ke `prd-discovery` untuk melengkapi, atau saya lanjutkan dengan asumsi best-practice untuk section yang kosong? Balas `lanjutkan` atau `kembali ke discovery`.
|
|
46
|
+
|
|
47
|
+
Kalau user pilih `lanjutkan`, catat semua asumsi di Section 15 PRD.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
# GENERATION FLOW
|
|
52
|
+
|
|
53
|
+
1. **Baca** `discovery-output.md` sepenuhnya
|
|
54
|
+
2. **Parse** ke internal structured data
|
|
55
|
+
3. **Validate** kelengkapan
|
|
56
|
+
4. **Announce** ke user:
|
|
57
|
+
> Discovery output ter-parse. Generating PRD dengan 13 section... Ini butuh beberapa saat.
|
|
58
|
+
5. **Generate** file `PRD-[slug-nama-produk].md` dengan struktur di bawah
|
|
59
|
+
6. **Present** file + ringkasan compact
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
# PRD STRUCTURE (13 SECTION WAJIB)
|
|
64
|
+
|
|
65
|
+
Urutan tidak boleh diubah. Setiap section punya struktur eksplisit.
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
1. Metadata & Overview
|
|
69
|
+
2. Product Context & Goals
|
|
70
|
+
3. Feature Map & Traceability
|
|
71
|
+
4. Functional Requirements (FR)
|
|
72
|
+
5. Non-Functional Requirements (NFR)
|
|
73
|
+
6. Detailed Feature Specification (per fitur)
|
|
74
|
+
7. Data Model
|
|
75
|
+
8. API Contracts
|
|
76
|
+
9. User Flows
|
|
77
|
+
10. Architecture
|
|
78
|
+
11. Design System Reference
|
|
79
|
+
12. Screen-Level Specification
|
|
80
|
+
13. Test Scenarios & Acceptance
|
|
81
|
+
14. Deployment & Operations
|
|
82
|
+
15. Assumptions, Risks, Open Questions
|
|
83
|
+
16. Appendix — Traceability Matrix
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
*(16 total, tapi Metadata + Overview jadi Section 1 gabungan untuk kepraktisan)*
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## SECTION 1 — Metadata & Overview
|
|
91
|
+
|
|
92
|
+
```markdown
|
|
93
|
+
# PRD: [Nama Produk]
|
|
94
|
+
|
|
95
|
+
| Field | Value |
|
|
96
|
+
|-------|-------|
|
|
97
|
+
| Version | 1.0 (Draft) |
|
|
98
|
+
| Generated | [tanggal] |
|
|
99
|
+
| Source | discovery-output.md v[X] |
|
|
100
|
+
| Status | Draft / Review / Final |
|
|
101
|
+
| Author | AI PRD Generator |
|
|
102
|
+
| Target Auto-Dev Compatibility | Claude Code / Cursor / Aider |
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Vision Statement** — 1-2 kalimat visi produk berdasarkan Product Context.
|
|
106
|
+
|
|
107
|
+
**Executive Summary** — 3-5 bullet: apa yang dibangun, untuk siapa, masalah apa, differensiator, target scale.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## SECTION 2 — Product Context & Goals
|
|
112
|
+
|
|
113
|
+
- **Problem Statement** — 2-3 kalimat, spesifik dengan dampak jika dibiarkan
|
|
114
|
+
- **Target Audience**
|
|
115
|
+
- Primary persona: nama, karakteristik, pain points, goals
|
|
116
|
+
- Secondary persona (kalau ada)
|
|
117
|
+
- **Success Metrics (KPI)** — minimal 4, terukur:
|
|
118
|
+
- Adoption metric (contoh: MAU, activation rate)
|
|
119
|
+
- Engagement metric (contoh: DAU/MAU ratio, retention W1/W4)
|
|
120
|
+
- Business metric (contoh: revenue per user, invoice/user/month)
|
|
121
|
+
- Quality metric (contoh: NPS, error rate)
|
|
122
|
+
- **Non-Goals** — hal yang eksplisit **tidak** dikerjakan di v1 (penting biar auto-dev tidak nyasar)
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## SECTION 3 — Feature Map & Traceability
|
|
127
|
+
|
|
128
|
+
**Master Feature List** dengan ID:
|
|
129
|
+
|
|
130
|
+
| Feature ID | Name | Category | Priority | Sub-Feature Count | Related |
|
|
131
|
+
|-----------|------|----------|----------|-------------------|---------|
|
|
132
|
+
| F-01 | Authentication | Core | Must | 6 | F-05, F-08 |
|
|
133
|
+
| F-02 | Invoice Management | Core | Must | 11 | F-03, F-05 |
|
|
134
|
+
| ... | | | | | |
|
|
135
|
+
|
|
136
|
+
**Feature Dependency Diagram** — Mermaid `graph LR`:
|
|
137
|
+
|
|
138
|
+
```mermaid
|
|
139
|
+
graph LR
|
|
140
|
+
F01[F-01 Auth] --> F02[F-02 Invoice]
|
|
141
|
+
F01 --> F03[F-03 Client]
|
|
142
|
+
F02 --> F05[F-05 Notification]
|
|
143
|
+
F03 --> F02
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Aturan Mermaid untuk mencegah syntax error:**
|
|
147
|
+
- Node ID: alphanumeric + underscore, no spaces
|
|
148
|
+
- Label panjang: bungkus double quote
|
|
149
|
+
- Edge label: `-- "text" -->`
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## SECTION 4 — Functional Requirements (FR)
|
|
154
|
+
|
|
155
|
+
Tabel lengkap dengan ID yang traceable:
|
|
156
|
+
|
|
157
|
+
| FR ID | Feature | Sub-Feature | Requirement | Priority |
|
|
158
|
+
|-------|---------|-------------|-------------|----------|
|
|
159
|
+
| FR-01-01 | F-01 Auth | Login | Sistem harus mengizinkan user login dengan email + password | Must |
|
|
160
|
+
| FR-01-02 | F-01 Auth | Login | Sistem harus rate-limit login gagal (5 attempt / 15 menit / IP) | Must |
|
|
161
|
+
| FR-01-03 | F-01 Auth | Reset Password | Sistem harus kirim link reset ke email, expire 15 menit | Must |
|
|
162
|
+
| FR-02-01 | F-02 Invoice | Create | User dapat create invoice dengan minimal 1 line item | Must |
|
|
163
|
+
| ... | | | | |
|
|
164
|
+
|
|
165
|
+
**Format ID:** `FR-[FeatureID]-[Sequence]`
|
|
166
|
+
|
|
167
|
+
Setiap FR harus:
|
|
168
|
+
- Punya 1 acceptance criteria (di Section 6, referenced by FR ID)
|
|
169
|
+
- Terhubung ke minimal 1 API endpoint (di Section 8)
|
|
170
|
+
- Terhubung ke minimal 1 test scenario (di Section 13)
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## SECTION 5 — Non-Functional Requirements (NFR)
|
|
175
|
+
|
|
176
|
+
Terstruktur per kategori. Semua **terukur**.
|
|
177
|
+
|
|
178
|
+
**Performance**
|
|
179
|
+
| NFR ID | Metric | Target | Measurement |
|
|
180
|
+
|--------|--------|--------|-------------|
|
|
181
|
+
| NFR-P-01 | API response time | p50 < 150ms, p95 < 300ms, p99 < 800ms | APM monitoring |
|
|
182
|
+
| NFR-P-02 | Page load (TTI) | < 2s (3G Slow), < 1s (4G) | Lighthouse |
|
|
183
|
+
| NFR-P-03 | Search query | < 200ms untuk dataset ≤ 10k rows | Query log |
|
|
184
|
+
|
|
185
|
+
**Security**
|
|
186
|
+
| NFR ID | Requirement | Implementation |
|
|
187
|
+
|--------|-------------|----------------|
|
|
188
|
+
| NFR-S-01 | Password hashing | bcrypt cost 12 minimum |
|
|
189
|
+
| NFR-S-02 | Session token | JWT (15min) + refresh token (7d), stored httpOnly cookie |
|
|
190
|
+
| NFR-S-03 | Rate limiting | Per IP + per user, sliding window |
|
|
191
|
+
| NFR-S-04 | OWASP Top 10 | All mitigated, dokumentasi mitigation per item |
|
|
192
|
+
| NFR-S-05 | Data-at-rest encryption | AES-256 untuk kolom sensitif (KTP, NPWP, dst) |
|
|
193
|
+
| NFR-S-06 | Data-in-transit | TLS 1.3, HSTS enabled |
|
|
194
|
+
|
|
195
|
+
**Scalability**
|
|
196
|
+
| NFR ID | Requirement | Target |
|
|
197
|
+
|--------|-------------|--------|
|
|
198
|
+
| NFR-SC-01 | Concurrent user | 500 (v1), scalable to 5000 |
|
|
199
|
+
| NFR-SC-02 | Database | Read replica ready, connection pooling |
|
|
200
|
+
| NFR-SC-03 | Horizontal scaling | Stateless app, session di Redis |
|
|
201
|
+
|
|
202
|
+
**Availability**
|
|
203
|
+
| NFR ID | Requirement | Target |
|
|
204
|
+
|--------|-------------|--------|
|
|
205
|
+
| NFR-A-01 | Uptime SLA | 99.5% |
|
|
206
|
+
| NFR-A-02 | RPO | 1 hour |
|
|
207
|
+
| NFR-A-03 | RTO | 4 hours |
|
|
208
|
+
| NFR-A-04 | Backup | Daily automated, retained 30 days |
|
|
209
|
+
|
|
210
|
+
**Compliance** (kalau applicable)
|
|
211
|
+
| NFR ID | Regulation | Requirement |
|
|
212
|
+
|--------|-----------|-------------|
|
|
213
|
+
| NFR-C-01 | UU PDP No.27/2022 | Consent record, data subject rights (access/rectify/delete/portability), breach notification 72 jam |
|
|
214
|
+
| NFR-C-02 | Kominfo PSE | Registrasi PSE Lingkup Privat |
|
|
215
|
+
| NFR-C-03 | POJK 11/2022 | (kalau produk fintech) |
|
|
216
|
+
|
|
217
|
+
**Accessibility**
|
|
218
|
+
| NFR ID | Requirement |
|
|
219
|
+
|--------|-------------|
|
|
220
|
+
| NFR-AC-01 | WCAG 2.1 Level AA |
|
|
221
|
+
| NFR-AC-02 | Keyboard navigation semua interactive element |
|
|
222
|
+
| NFR-AC-03 | Screen reader support (ARIA labels) |
|
|
223
|
+
| NFR-AC-04 | Contrast ratio ≥ 4.5:1 (text normal), ≥ 3:1 (text large) |
|
|
224
|
+
|
|
225
|
+
**Localization**
|
|
226
|
+
| NFR ID | Requirement |
|
|
227
|
+
|--------|-------------|
|
|
228
|
+
| NFR-L-01 | Languages: [dari discovery] |
|
|
229
|
+
| NFR-L-02 | Date format: locale-aware |
|
|
230
|
+
| NFR-L-03 | Currency: multi-currency + display sesuai user pref |
|
|
231
|
+
| NFR-L-04 | Timezone: user-selectable, default Asia/Jakarta |
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## SECTION 6 — Detailed Feature Specification (Per Fitur)
|
|
236
|
+
|
|
237
|
+
**Ini section paling tebal.** Per fitur (F-01, F-02, dst), tulis blok berikut:
|
|
238
|
+
|
|
239
|
+
### F-XX — [Feature Name]
|
|
240
|
+
|
|
241
|
+
**Description:** [1-2 paragraf]
|
|
242
|
+
|
|
243
|
+
**Priority:** Must / Should / Could
|
|
244
|
+
|
|
245
|
+
**Related Features:** F-XX, F-YY
|
|
246
|
+
|
|
247
|
+
**User Stories:**
|
|
248
|
+
- Sebagai [role], saya ingin [action] agar [outcome]
|
|
249
|
+
- (minimal 2 story per fitur)
|
|
250
|
+
|
|
251
|
+
**Sub-Features:**
|
|
252
|
+
|
|
253
|
+
#### SF-XX-01 — [Sub-Feature Name]
|
|
254
|
+
|
|
255
|
+
- **Priority:** Must
|
|
256
|
+
- **Description:** ...
|
|
257
|
+
- **Acceptance Criteria:**
|
|
258
|
+
- **AC-1** — Given [context], When [action], Then [outcome]
|
|
259
|
+
- **AC-2** — Given ..., When ..., Then ...
|
|
260
|
+
- **Edge Cases:**
|
|
261
|
+
- EC-1: [skenario] → [expected behavior]
|
|
262
|
+
- EC-2: ...
|
|
263
|
+
- **UI Reference:** Screen `SC-XX-01` (di Section 12)
|
|
264
|
+
- **API Reference:** Endpoint `EP-XX-01` (di Section 8)
|
|
265
|
+
- **Test Reference:** Scenario `TS-XX-01` (di Section 13)
|
|
266
|
+
|
|
267
|
+
#### SF-XX-02 — ...
|
|
268
|
+
|
|
269
|
+
*(ulangi semua sub-fitur)*
|
|
270
|
+
|
|
271
|
+
**Business Rules Terkait:**
|
|
272
|
+
- BR-01: ...
|
|
273
|
+
- BR-02: ...
|
|
274
|
+
|
|
275
|
+
**State Machine** (kalau fitur ber-status):
|
|
276
|
+
```mermaid
|
|
277
|
+
stateDiagram-v2
|
|
278
|
+
...
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Transitions Table:**
|
|
282
|
+
| From | To | Trigger | Guard | Side Effect |
|
|
283
|
+
|------|----|----|--------|-----|
|
|
284
|
+
| draft | sent | send() | items.length ≥ 1 && total > 0 | send email, log audit |
|
|
285
|
+
| sent | overdue | scheduler | due_date < today && status != paid | queue reminder |
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## SECTION 7 — Data Model
|
|
290
|
+
|
|
291
|
+
### Entity Relationship Diagram (ERD)
|
|
292
|
+
|
|
293
|
+
```mermaid
|
|
294
|
+
erDiagram
|
|
295
|
+
User ||--o{ Invoice : "creates"
|
|
296
|
+
User ||--o{ Client : "manages"
|
|
297
|
+
Client ||--o{ Invoice : "receives"
|
|
298
|
+
Invoice ||--o{ InvoiceItem : "contains"
|
|
299
|
+
Invoice ||--o{ Payment : "settled_by"
|
|
300
|
+
|
|
301
|
+
User {
|
|
302
|
+
uuid id PK
|
|
303
|
+
string email UK
|
|
304
|
+
string password_hash
|
|
305
|
+
string full_name
|
|
306
|
+
string role
|
|
307
|
+
timestamp created_at
|
|
308
|
+
}
|
|
309
|
+
Invoice {
|
|
310
|
+
uuid id PK
|
|
311
|
+
uuid user_id FK
|
|
312
|
+
uuid client_id FK
|
|
313
|
+
string invoice_number UK
|
|
314
|
+
date issue_date
|
|
315
|
+
date due_date
|
|
316
|
+
decimal subtotal
|
|
317
|
+
decimal tax_amount
|
|
318
|
+
decimal total
|
|
319
|
+
string currency
|
|
320
|
+
string status
|
|
321
|
+
text notes
|
|
322
|
+
timestamp created_at
|
|
323
|
+
timestamp updated_at
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Entity Detail
|
|
328
|
+
|
|
329
|
+
**Untuk setiap entitas**, tulis blok berikut:
|
|
330
|
+
|
|
331
|
+
#### Entity: [Name]
|
|
332
|
+
|
|
333
|
+
**Table:** `snake_case_plural` (contoh: `invoices`)
|
|
334
|
+
|
|
335
|
+
**Fields:**
|
|
336
|
+
|
|
337
|
+
| Field | Type | Constraints | Default | Validation | Note |
|
|
338
|
+
|-------|------|-------------|---------|------------|------|
|
|
339
|
+
| id | UUID | PK, NOT NULL | `gen_random_uuid()` | - | - |
|
|
340
|
+
| user_id | UUID | FK → users.id, NOT NULL | - | must exist | Owner |
|
|
341
|
+
| invoice_number | VARCHAR(32) | UNIQUE, NOT NULL | auto | format `INV-YYYY-MM-NNNN` | Reset per bulan |
|
|
342
|
+
| issue_date | DATE | NOT NULL | `CURRENT_DATE` | - | - |
|
|
343
|
+
| due_date | DATE | NOT NULL | `issue_date + 30` | `>= issue_date` | - |
|
|
344
|
+
| subtotal | DECIMAL(15,2) | NOT NULL | 0 | `>= 0` | Auto-calc |
|
|
345
|
+
| tax_amount | DECIMAL(15,2) | NOT NULL | 0 | `>= 0` | Auto-calc |
|
|
346
|
+
| total | DECIMAL(15,2) | NOT NULL | 0 | `= subtotal + tax - discount` | Computed |
|
|
347
|
+
| status | ENUM | NOT NULL | 'draft' | draft/sent/paid/overdue/void | - |
|
|
348
|
+
| currency | CHAR(3) | NOT NULL | 'IDR' | ISO 4217 | - |
|
|
349
|
+
| notes | TEXT | NULL | NULL | max 1000 | - |
|
|
350
|
+
| created_at | TIMESTAMPTZ | NOT NULL | `NOW()` | - | - |
|
|
351
|
+
| updated_at | TIMESTAMPTZ | NOT NULL | `NOW()` | auto-update via trigger | - |
|
|
352
|
+
| deleted_at | TIMESTAMPTZ | NULL | NULL | - | Soft delete |
|
|
353
|
+
|
|
354
|
+
**Indexes:**
|
|
355
|
+
- `idx_invoices_user_id` on (user_id)
|
|
356
|
+
- `idx_invoices_client_id` on (client_id)
|
|
357
|
+
- `idx_invoices_status` on (status)
|
|
358
|
+
- `idx_invoices_due_date` on (due_date) — untuk overdue query
|
|
359
|
+
- `unique_invoice_number` UNIQUE on (invoice_number)
|
|
360
|
+
|
|
361
|
+
**Relations:**
|
|
362
|
+
- `belongs_to` User (via user_id)
|
|
363
|
+
- `belongs_to` Client (via client_id)
|
|
364
|
+
- `has_many` InvoiceItem (via invoice_id)
|
|
365
|
+
- `has_many` Payment (via invoice_id)
|
|
366
|
+
|
|
367
|
+
**Triggers / Computed:**
|
|
368
|
+
- `updated_at` auto-update on any change
|
|
369
|
+
- `total` auto-recompute saat items berubah
|
|
370
|
+
|
|
371
|
+
*(ulangi untuk semua entitas)*
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
## SECTION 8 — API Contracts
|
|
376
|
+
|
|
377
|
+
**INI SANGAT PENTING untuk auto-dev.** Setiap FR yang butuh API harus punya endpoint spec eksplisit.
|
|
378
|
+
|
|
379
|
+
**Base URL:** `https://api.[domain]/v1`
|
|
380
|
+
|
|
381
|
+
**Auth:** `Authorization: Bearer <access_token>`
|
|
382
|
+
|
|
383
|
+
**Response Envelope Standar:**
|
|
384
|
+
```json
|
|
385
|
+
{
|
|
386
|
+
"success": true,
|
|
387
|
+
"data": { ... },
|
|
388
|
+
"meta": { "pagination": { ... } },
|
|
389
|
+
"error": null
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
**Error Envelope:**
|
|
394
|
+
```json
|
|
395
|
+
{
|
|
396
|
+
"success": false,
|
|
397
|
+
"data": null,
|
|
398
|
+
"error": {
|
|
399
|
+
"code": "VALIDATION_ERROR",
|
|
400
|
+
"message": "Human readable",
|
|
401
|
+
"details": [ { "field": "email", "issue": "invalid format" } ]
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
**Error Codes Standar:**
|
|
407
|
+
| HTTP | Code | Meaning |
|
|
408
|
+
|------|------|---------|
|
|
409
|
+
| 400 | `VALIDATION_ERROR` | Input tidak valid |
|
|
410
|
+
| 401 | `UNAUTHORIZED` | Token missing/invalid |
|
|
411
|
+
| 403 | `FORBIDDEN` | Tidak punya permission |
|
|
412
|
+
| 404 | `NOT_FOUND` | Resource tidak ada |
|
|
413
|
+
| 409 | `CONFLICT` | Business rule violation (contoh: invoice sudah paid) |
|
|
414
|
+
| 422 | `UNPROCESSABLE` | Semantic error |
|
|
415
|
+
| 429 | `RATE_LIMITED` | Rate limit exceeded |
|
|
416
|
+
| 500 | `INTERNAL_ERROR` | Server error |
|
|
417
|
+
|
|
418
|
+
### Endpoint Specification (Per Endpoint)
|
|
419
|
+
|
|
420
|
+
Untuk setiap endpoint, tulis:
|
|
421
|
+
|
|
422
|
+
#### EP-02-01 — Create Invoice
|
|
423
|
+
|
|
424
|
+
- **Method:** POST
|
|
425
|
+
- **Path:** `/invoices`
|
|
426
|
+
- **Auth:** Required (Bearer token)
|
|
427
|
+
- **Permission:** `invoice:create`
|
|
428
|
+
- **Related FR:** FR-02-01
|
|
429
|
+
|
|
430
|
+
**Request Body:**
|
|
431
|
+
```json
|
|
432
|
+
{
|
|
433
|
+
"client_id": "uuid",
|
|
434
|
+
"issue_date": "2026-07-06",
|
|
435
|
+
"due_date": "2026-08-05",
|
|
436
|
+
"currency": "IDR",
|
|
437
|
+
"notes": "string, optional, max 1000",
|
|
438
|
+
"items": [
|
|
439
|
+
{
|
|
440
|
+
"description": "string, required, max 200",
|
|
441
|
+
"quantity": "number, >0",
|
|
442
|
+
"unit_price": "number, >=0",
|
|
443
|
+
"discount_pct": "number, 0-100, optional",
|
|
444
|
+
"tax_pct": "number, 0-100, optional"
|
|
445
|
+
}
|
|
446
|
+
]
|
|
447
|
+
}
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
**Validation:**
|
|
451
|
+
- `client_id` must exist and owned by user
|
|
452
|
+
- `items` minimal 1, max 100
|
|
453
|
+
- `due_date >= issue_date`
|
|
454
|
+
- Sum of items amount > 0
|
|
455
|
+
|
|
456
|
+
**Response 201:**
|
|
457
|
+
```json
|
|
458
|
+
{
|
|
459
|
+
"success": true,
|
|
460
|
+
"data": {
|
|
461
|
+
"id": "uuid",
|
|
462
|
+
"invoice_number": "INV-2026-07-0042",
|
|
463
|
+
"status": "draft",
|
|
464
|
+
"subtotal": 1000000,
|
|
465
|
+
"tax_amount": 110000,
|
|
466
|
+
"total": 1110000,
|
|
467
|
+
"created_at": "2026-07-06T10:00:00Z"
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
**Possible Errors:**
|
|
473
|
+
- 400 VALIDATION_ERROR — field validation failed
|
|
474
|
+
- 403 FORBIDDEN — user tidak punya `invoice:create`
|
|
475
|
+
- 404 NOT_FOUND — client_id tidak ada
|
|
476
|
+
- 429 RATE_LIMITED
|
|
477
|
+
|
|
478
|
+
**Side Effects:**
|
|
479
|
+
- Insert row ke `invoices`, `invoice_items`
|
|
480
|
+
- Log ke `audit_logs`
|
|
481
|
+
- Emit event `invoice.created` ke queue
|
|
482
|
+
|
|
483
|
+
*(ulangi untuk semua endpoint utama: list, get, update, delete, action-endpoints)*
|
|
484
|
+
|
|
485
|
+
**Endpoint Inventory (ringkasan)**
|
|
486
|
+
|
|
487
|
+
| Endpoint ID | Method | Path | FR | Permission |
|
|
488
|
+
|-------------|--------|------|----|-----------|
|
|
489
|
+
| EP-01-01 | POST | /auth/login | FR-01-01 | public |
|
|
490
|
+
| EP-01-02 | POST | /auth/refresh | FR-01-04 | public |
|
|
491
|
+
| EP-02-01 | POST | /invoices | FR-02-01 | invoice:create |
|
|
492
|
+
| EP-02-02 | GET | /invoices | FR-02-02 | invoice:read |
|
|
493
|
+
| EP-02-03 | GET | /invoices/:id | FR-02-03 | invoice:read |
|
|
494
|
+
| EP-02-04 | PATCH | /invoices/:id | FR-02-04 | invoice:update |
|
|
495
|
+
| EP-02-05 | POST | /invoices/:id/send | FR-02-05 | invoice:send |
|
|
496
|
+
| EP-02-06 | POST | /invoices/:id/void | FR-02-06 | invoice:void |
|
|
497
|
+
| ... | | | | |
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
## SECTION 9 — User Flows
|
|
502
|
+
|
|
503
|
+
Per user journey utama, satu Mermaid flowchart.
|
|
504
|
+
|
|
505
|
+
**Aturan Mermaid** (wajib untuk mencegah render error):
|
|
506
|
+
- Node ID camelCase atau underscore, no spaces
|
|
507
|
+
- Label > 20 char wajib dibungkus `"..."`
|
|
508
|
+
- Gunakan `-->` atau `-- "label" -->`
|
|
509
|
+
|
|
510
|
+
Untuk setiap flow:
|
|
511
|
+
|
|
512
|
+
### UF-01 — [Nama Flow, contoh: Create & Send Invoice]
|
|
513
|
+
|
|
514
|
+
**Persona:** [role]
|
|
515
|
+
**Entry Point:** [halaman awal]
|
|
516
|
+
**Goal:** [outcome]
|
|
517
|
+
|
|
518
|
+
```mermaid
|
|
519
|
+
flowchart TD
|
|
520
|
+
A["Dashboard"] --> B{"Klik Buat Invoice"}
|
|
521
|
+
B --> C["Form Create Invoice"]
|
|
522
|
+
C --> D["Isi Client + Items"]
|
|
523
|
+
D --> E{"Klik Simpan"}
|
|
524
|
+
E -- "Valid" --> F["Invoice Draft Tersimpan"]
|
|
525
|
+
E -- "Invalid" --> G["Tampil Error Inline"]
|
|
526
|
+
G --> D
|
|
527
|
+
F --> H{"Klik Kirim"}
|
|
528
|
+
H --> I["Konfirmasi Modal"]
|
|
529
|
+
I -- "Ya" --> J["Send Email + Status Sent"]
|
|
530
|
+
I -- "Batal" --> F
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
**Happy Path:** step-by-step (nomor)
|
|
534
|
+
|
|
535
|
+
**Edge Cases:**
|
|
536
|
+
- EC-1: Klien belum ada → CTA `+ Klien Baru` inline
|
|
537
|
+
- EC-2: Item kosong → tombol Simpan disabled
|
|
538
|
+
- EC-3: Email gagal terkirim → status tetap `draft`, tampilkan retry banner
|
|
539
|
+
|
|
540
|
+
*(ulangi untuk flow utama)*
|
|
541
|
+
|
|
542
|
+
---
|
|
543
|
+
|
|
544
|
+
## SECTION 10 — Architecture
|
|
545
|
+
|
|
546
|
+
**System Diagram:**
|
|
547
|
+
|
|
548
|
+
```mermaid
|
|
549
|
+
graph TD
|
|
550
|
+
Client["Web Client (Next.js)"] --> CDN["CDN / Edge"]
|
|
551
|
+
Client --> Gateway["API Gateway"]
|
|
552
|
+
Gateway --> Auth["Auth Service"]
|
|
553
|
+
Gateway --> App["App Service (API)"]
|
|
554
|
+
App --> DB[("PostgreSQL")]
|
|
555
|
+
App --> Cache[("Redis")]
|
|
556
|
+
App --> Queue["BullMQ"]
|
|
557
|
+
Queue --> Worker["Background Worker"]
|
|
558
|
+
Worker --> Email["Email Provider (SES/Resend)"]
|
|
559
|
+
Worker --> Storage["Object Storage (S3/R2)"]
|
|
560
|
+
App --> Storage
|
|
561
|
+
App --> Log["Log Aggregator (Loki)"]
|
|
562
|
+
App --> Metric["Metrics (Mimir)"]
|
|
563
|
+
App --> Trace["Traces (Tempo)"]
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
**Component Rationale:**
|
|
567
|
+
| Component | Choice | Rationale |
|
|
568
|
+
|-----------|--------|-----------|
|
|
569
|
+
| Gateway | Nginx / Caddy | TLS termination, rate limit edge |
|
|
570
|
+
| App | Node.js Fastify | Ecosystem TS, fast |
|
|
571
|
+
| ... | | |
|
|
572
|
+
|
|
573
|
+
**Bottleneck & Mitigation:**
|
|
574
|
+
| Potential Bottleneck | Impact | Mitigation |
|
|
575
|
+
|----------------------|--------|-----------|
|
|
576
|
+
| PDF generation sync | Slow API response | Move to Worker via queue |
|
|
577
|
+
| Search on large invoice list | Slow query | Add search index (pg_trgm) + Elasticsearch di v2 |
|
|
578
|
+
| Email delivery burst | Rate limit provider | Batching + retry with backoff |
|
|
579
|
+
|
|
580
|
+
---
|
|
581
|
+
|
|
582
|
+
## SECTION 11 — Design System Reference
|
|
583
|
+
|
|
584
|
+
**Copy dari `discovery-output.md` Section 7**, format ulang untuk PRD:
|
|
585
|
+
|
|
586
|
+
### 11.1 Foundation
|
|
587
|
+
- Component library: [shadcn/ui | MUI | ...]
|
|
588
|
+
- Base framework: Tailwind CSS
|
|
589
|
+
- Dark mode: enabled/disabled
|
|
590
|
+
- Icon library: Lucide / Heroicons
|
|
591
|
+
|
|
592
|
+
### 11.2 Design Tokens
|
|
593
|
+
|
|
594
|
+
**Colors** — tabel lengkap dengan token name, HEX, usage
|
|
595
|
+
**Typography** — tabel scale
|
|
596
|
+
**Spacing** — scale value
|
|
597
|
+
**Radius** — scale value
|
|
598
|
+
**Shadow** — scale value
|
|
599
|
+
**Motion** — duration + easing
|
|
600
|
+
**Z-index scale** — modal, dropdown, tooltip, toast
|
|
601
|
+
|
|
602
|
+
### 11.3 Component Specifications
|
|
603
|
+
|
|
604
|
+
Per komponen, tabel spec lengkap (Button, Input, Textarea, Select, Checkbox, Radio, Toggle, Card, Modal, Drawer, Toast, Table, Badge, Avatar, Tabs, Tooltip, Dropdown Menu, Pagination, Empty State, Skeleton).
|
|
605
|
+
|
|
606
|
+
**Format per component** (contoh Button):
|
|
607
|
+
|
|
608
|
+
```markdown
|
|
609
|
+
#### Button
|
|
610
|
+
|
|
611
|
+
**Sizes:** sm (32px) / md (40px) / lg (48px)
|
|
612
|
+
|
|
613
|
+
**Variants:**
|
|
614
|
+
| Variant | BG | Text | Border | Use case |
|
|
615
|
+
|---------|----|----|--------|----------|
|
|
616
|
+
|
|
617
|
+
**States:** default, hover, active, focus, disabled, loading
|
|
618
|
+
|
|
619
|
+
**Anatomy:** [icon-leading] · [label] · [icon-trailing]
|
|
620
|
+
|
|
621
|
+
**Props (untuk implementation):**
|
|
622
|
+
- `size`: 'sm' | 'md' | 'lg'
|
|
623
|
+
- `variant`: 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive' | 'link'
|
|
624
|
+
- `loading`: boolean
|
|
625
|
+
- `disabled`: boolean
|
|
626
|
+
- `iconLeading`: ReactNode
|
|
627
|
+
- `iconTrailing`: ReactNode
|
|
628
|
+
- `fullWidth`: boolean
|
|
629
|
+
|
|
630
|
+
**Accessibility:**
|
|
631
|
+
- `role="button"` (implisit dari `<button>`)
|
|
632
|
+
- Keyboard: Space/Enter to activate
|
|
633
|
+
- Loading state: `aria-busy="true"`
|
|
634
|
+
- Disabled: `aria-disabled="true"`
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
### 11.4 Layout System
|
|
638
|
+
- Breakpoints
|
|
639
|
+
- Container max-width
|
|
640
|
+
- Grid
|
|
641
|
+
- Sidebar / Header / Footer dimensions
|
|
642
|
+
- Content padding per breakpoint
|
|
643
|
+
|
|
644
|
+
### 11.5 Iconography
|
|
645
|
+
- Library, size scale, style (outline/solid), stroke width
|
|
646
|
+
|
|
647
|
+
### 11.6 Content & Voice
|
|
648
|
+
- Tone: [formal/casual/friendly]
|
|
649
|
+
- Voice guidelines
|
|
650
|
+
- Sentence case vs Title Case untuk labels
|
|
651
|
+
|
|
652
|
+
---
|
|
653
|
+
|
|
654
|
+
## SECTION 12 — Screen-Level Specification
|
|
655
|
+
|
|
656
|
+
**Per screen**, blok berikut:
|
|
657
|
+
|
|
658
|
+
### SC-XX-01 — [Screen Name, contoh: Invoice List]
|
|
659
|
+
|
|
660
|
+
- **URL Path:** `/invoices`
|
|
661
|
+
- **Layout Container:** AppShell (Header + Sidebar + Content)
|
|
662
|
+
- **Access:** requires auth + `invoice:read`
|
|
663
|
+
- **Related Feature:** F-02
|
|
664
|
+
- **API used:** EP-02-02 (list), EP-02-06 (delete bulk)
|
|
665
|
+
|
|
666
|
+
**Structure (top-to-bottom):**
|
|
667
|
+
|
|
668
|
+
1. **Page Header**
|
|
669
|
+
- Title: `Invoice` (h1, weight 700, size 30px)
|
|
670
|
+
- Description: `Kelola semua invoice Anda` (body-sm, neutral-500)
|
|
671
|
+
- Action: Button primary md `+ Buat Invoice` → navigate `SC-02-03`
|
|
672
|
+
|
|
673
|
+
2. **Filter Bar** (Card, padding 16px, mb 16px)
|
|
674
|
+
- Date range picker (default: last 30 days)
|
|
675
|
+
- Status multi-select (chip style)
|
|
676
|
+
- Search input (dengan icon leading, debounce 300ms)
|
|
677
|
+
- Button ghost `Reset filter` (kanan)
|
|
678
|
+
|
|
679
|
+
3. **Bulk Action Bar** (muncul saat ≥1 row dicentang)
|
|
680
|
+
- Text: `[N] terpilih`
|
|
681
|
+
- Action buttons: `Export`, `Send Reminder`, `Delete` (destructive)
|
|
682
|
+
|
|
683
|
+
4. **Data Table**
|
|
684
|
+
- Columns: `[checkbox]`, `Number`, `Client`, `Issue Date`, `Due Date`, `Total`, `Status` (Badge), `Actions` (Dropdown menu)
|
|
685
|
+
- Row height: 56px
|
|
686
|
+
- Hover: neutral-50 bg
|
|
687
|
+
- 10 rows per page
|
|
688
|
+
- Sortable columns: Issue Date, Due Date, Total
|
|
689
|
+
|
|
690
|
+
5. **Pagination** (bottom, right-aligned)
|
|
691
|
+
- Info: `Menampilkan 1-10 dari 234`
|
|
692
|
+
- Controls: `< Prev`, page numbers, `Next >`
|
|
693
|
+
|
|
694
|
+
**States:**
|
|
695
|
+
- **Loading:** table skeleton 10 rows
|
|
696
|
+
- **Empty (no data):** illustration + h2 `Belum ada invoice` + body `Buat invoice pertama Anda untuk mulai` + CTA `+ Buat Invoice`
|
|
697
|
+
- **Empty (filtered):** copy `Tidak ada invoice sesuai filter` + link `Reset filter`
|
|
698
|
+
- **Error:** fallback card + CTA `Coba lagi`
|
|
699
|
+
|
|
700
|
+
**Responsive:**
|
|
701
|
+
- **Mobile (<640px):**
|
|
702
|
+
- Table → Card list (1 kolom)
|
|
703
|
+
- Card: number, client, total, status, tanggal
|
|
704
|
+
- Filter → drawer di kanan, trigger dari button icon
|
|
705
|
+
- Bulk actions → sticky bottom bar
|
|
706
|
+
- **Tablet (640-1023px):**
|
|
707
|
+
- Table dengan kolom terlipat (hide Client atau Issue Date)
|
|
708
|
+
|
|
709
|
+
**Microcopy:**
|
|
710
|
+
- Search placeholder: `Cari nomor invoice atau nama klien...`
|
|
711
|
+
- Bulk delete confirm: `Hapus [N] invoice? Data yang dihapus bisa dipulihkan dari Trash selama 30 hari.`
|
|
712
|
+
- Toast success: `[N] invoice berhasil dihapus.`
|
|
713
|
+
- Toast error: `Gagal menghapus. Coba lagi.`
|
|
714
|
+
|
|
715
|
+
*(ulangi untuk semua screen utama: List, Detail, Create, Edit)*
|
|
716
|
+
|
|
717
|
+
---
|
|
718
|
+
|
|
719
|
+
## SECTION 13 — Test Scenarios & Acceptance
|
|
720
|
+
|
|
721
|
+
Format Gherkin (Given-When-Then) untuk setiap AC di Section 6, plus edge cases.
|
|
722
|
+
|
|
723
|
+
### TS-02-01 — Create Invoice Happy Path
|
|
724
|
+
|
|
725
|
+
**Related FR:** FR-02-01
|
|
726
|
+
**Related Endpoint:** EP-02-01
|
|
727
|
+
|
|
728
|
+
```gherkin
|
|
729
|
+
Feature: Create Invoice
|
|
730
|
+
|
|
731
|
+
Scenario: Admin creates invoice with valid client and items
|
|
732
|
+
Given user is logged in as admin
|
|
733
|
+
And client "PT ABC" exists
|
|
734
|
+
When user navigates to /invoices/new
|
|
735
|
+
And selects client "PT ABC"
|
|
736
|
+
And adds line item {desc: "Design logo", qty: 1, price: 5000000}
|
|
737
|
+
And clicks "Simpan sebagai Draft"
|
|
738
|
+
Then invoice is saved with status "draft"
|
|
739
|
+
And invoice_number matches pattern "INV-YYYY-MM-####"
|
|
740
|
+
And subtotal is 5000000
|
|
741
|
+
And tax_amount is 550000 (11% PPN)
|
|
742
|
+
And total is 5550000
|
|
743
|
+
And user is redirected to invoice detail
|
|
744
|
+
And audit log entry is created
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
### TS-02-02 — Create Invoice Validation Errors
|
|
748
|
+
|
|
749
|
+
```gherkin
|
|
750
|
+
Scenario: Create invoice without items
|
|
751
|
+
Given user is on /invoices/new
|
|
752
|
+
And client is selected
|
|
753
|
+
When user clicks "Simpan sebagai Draft" without adding items
|
|
754
|
+
Then submit button was disabled
|
|
755
|
+
And helper text "Minimal 1 item wajib diisi" is shown
|
|
756
|
+
|
|
757
|
+
Scenario: Create invoice with client not owned
|
|
758
|
+
Given user tries POST /invoices with client_id belonging to another user
|
|
759
|
+
Then response is 403 FORBIDDEN
|
|
760
|
+
And error.code is "FORBIDDEN"
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
*(ulangi untuk setiap sub-fitur — minimal 1 happy path + 2 edge case per sub-fitur)*
|
|
764
|
+
|
|
765
|
+
---
|
|
766
|
+
|
|
767
|
+
## SECTION 14 — Deployment & Operations
|
|
768
|
+
|
|
769
|
+
**Environments:**
|
|
770
|
+
| Env | Purpose | URL | Auto-deploy |
|
|
771
|
+
|-----|---------|-----|-------------|
|
|
772
|
+
| dev | Development | dev.[domain] | on push to `develop` |
|
|
773
|
+
| staging | Pre-prod QA | staging.[domain] | on merge to `main` |
|
|
774
|
+
| prod | Production | app.[domain] | manual approval |
|
|
775
|
+
|
|
776
|
+
**CI/CD Pipeline:**
|
|
777
|
+
```
|
|
778
|
+
push → lint → typecheck → unit test → build → integration test → deploy dev
|
|
779
|
+
↓
|
|
780
|
+
promote → staging
|
|
781
|
+
↓
|
|
782
|
+
approval → prod
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
**Cloud Infrastructure:**
|
|
786
|
+
- Provider: AWS
|
|
787
|
+
- Region: `ap-southeast-3` (Jakarta) — untuk latency Indonesia
|
|
788
|
+
- Compute: ECS Fargate atau EC2 (t3.medium min)
|
|
789
|
+
- DB: RDS PostgreSQL Multi-AZ
|
|
790
|
+
- Cache: ElastiCache Redis
|
|
791
|
+
- Storage: S3 dengan lifecycle policy
|
|
792
|
+
- CDN: CloudFront
|
|
793
|
+
|
|
794
|
+
**Observability Stack:**
|
|
795
|
+
- **Metrics:** Grafana Mimir (Prometheus-compatible)
|
|
796
|
+
- **Logs:** Grafana Loki
|
|
797
|
+
- **Traces:** Grafana Tempo
|
|
798
|
+
- **Dashboard:** Grafana (LGTM stack)
|
|
799
|
+
- **Alerting:** Grafana Alertmanager → Slack/PagerDuty
|
|
800
|
+
|
|
801
|
+
**Key Alerts:**
|
|
802
|
+
- API p95 > 500ms selama 5 menit
|
|
803
|
+
- Error rate > 1% selama 5 menit
|
|
804
|
+
- DB connection pool > 80% selama 3 menit
|
|
805
|
+
- Queue depth > 1000 selama 5 menit
|
|
806
|
+
- Disk usage > 80%
|
|
807
|
+
|
|
808
|
+
**Runbooks:**
|
|
809
|
+
- Deployment rollback: [1 kalimat prosedur]
|
|
810
|
+
- Database restore: [1 kalimat]
|
|
811
|
+
- Incident response: [link ke doc]
|
|
812
|
+
|
|
813
|
+
**Secrets Management:**
|
|
814
|
+
- AWS Secrets Manager / SSM Parameter Store
|
|
815
|
+
- No secrets di git, no secrets di env file production
|
|
816
|
+
|
|
817
|
+
---
|
|
818
|
+
|
|
819
|
+
## SECTION 15 — Assumptions, Risks, Open Questions
|
|
820
|
+
|
|
821
|
+
**Assumptions Made (karena Discovery tidak specify):**
|
|
822
|
+
- A-01: Tax PPN 11% (standar Indonesia 2025+)
|
|
823
|
+
- A-02: Default currency IDR
|
|
824
|
+
- A-03: Session timeout 7 hari (refresh token)
|
|
825
|
+
- A-04: [dst]
|
|
826
|
+
|
|
827
|
+
**Risks:**
|
|
828
|
+
| Risk ID | Description | Impact | Likelihood | Mitigation |
|
|
829
|
+
|---------|-------------|--------|-----------|-----------|
|
|
830
|
+
| R-01 | Email delivery rate limit di burst | High | Medium | Batching + fallback provider |
|
|
831
|
+
| R-02 | PDF generation memory bloat | Medium | Low | Worker isolated + memory limit |
|
|
832
|
+
| R-03 | Compliance PDP breach → sanksi | Very High | Low | Consent record + audit log + DPO |
|
|
833
|
+
|
|
834
|
+
**Open Questions** (perlu keputusan tim sebelum implementasi):
|
|
835
|
+
- Q-01: Apakah recurring invoice masuk MVP atau v2?
|
|
836
|
+
- Q-02: Multi-tenancy strategy: row-level atau schema-per-tenant?
|
|
837
|
+
- Q-03: [dst]
|
|
838
|
+
|
|
839
|
+
---
|
|
840
|
+
|
|
841
|
+
## SECTION 16 — Appendix: Traceability Matrix
|
|
842
|
+
|
|
843
|
+
**Cross-reference table** untuk auditability:
|
|
844
|
+
|
|
845
|
+
| Feature | Sub-Feature | FR | Endpoint | Screen | Test | AC |
|
|
846
|
+
|---------|-------------|----|----|--------|------|-----|
|
|
847
|
+
| F-02 | SF-02-01 Create | FR-02-01 | EP-02-01 | SC-02-03 | TS-02-01, TS-02-02 | AC-1, AC-2, AC-3 |
|
|
848
|
+
| F-02 | SF-02-02 List | FR-02-02 | EP-02-02 | SC-02-01 | TS-02-03 | AC-1 |
|
|
849
|
+
| ... | | | | | | |
|
|
850
|
+
|
|
851
|
+
**Coverage check:**
|
|
852
|
+
- Setiap FR punya endpoint ✅
|
|
853
|
+
- Setiap FR punya screen ✅
|
|
854
|
+
- Setiap FR punya test ✅
|
|
855
|
+
|
|
856
|
+
Kalau ada gap, tulis di Open Questions.
|
|
857
|
+
|
|
858
|
+
---
|
|
859
|
+
|
|
860
|
+
# WRITING RULES (Wajib Ditegakkan)
|
|
861
|
+
|
|
862
|
+
1. **Semua item punya ID.** FR-XX-YY, EP-XX-YY, SC-XX-YY, TS-XX-YY, dst. ID unik dan konsisten.
|
|
863
|
+
2. **Tidak ada "dsb" atau "dan lain-lain"** untuk requirement — sebutkan eksplisit atau tulis di Open Questions.
|
|
864
|
+
3. **Angka dan unit selalu eksplisit.** Bukan "cepat", tapi "< 300ms p95".
|
|
865
|
+
4. **Setiap section minimal isi 3 baris substansial** (kecuali diagram).
|
|
866
|
+
5. **Gunakan tabel** untuk requirement, endpoint, field, spec. Prose hanya untuk konteks.
|
|
867
|
+
6. **Kata perintah untuk requirement:** "harus", "wajib", "must" — tidak boleh "sebaiknya", "diharapkan".
|
|
868
|
+
7. **Format Mermaid** — patuhi aturan node ID no-spaces, long labels wajib di-quote.
|
|
869
|
+
8. **Sync antara section** — kalau FR-02-01 disebutkan, harus ada di Section 4 DAN referenced di Section 6, 8, 13.
|
|
870
|
+
9. **No lorem ipsum, no placeholder text.** Kalau data tidak ada, tulis eksplisit di Assumptions.
|
|
871
|
+
|
|
872
|
+
---
|
|
873
|
+
|
|
874
|
+
# CLOSING PROTOCOL
|
|
875
|
+
|
|
876
|
+
Setelah PRD selesai di-generate:
|
|
877
|
+
|
|
878
|
+
1. Tulis file `PRD-[slug-nama-produk].md` di working directory
|
|
879
|
+
2. Tampilkan ringkasan kompak (maksimal 10 baris) ke user:
|
|
880
|
+
|
|
881
|
+
> ✅ **PRD selesai.**
|
|
882
|
+
>
|
|
883
|
+
> - **File:** `PRD-[slug].md` ([X] KB, ~[Y] halaman ekuivalen)
|
|
884
|
+
> - **Sections:** 16
|
|
885
|
+
> - **Requirements:** [N] FR, [N] NFR
|
|
886
|
+
> - **Endpoints:** [N]
|
|
887
|
+
> - **Screens:** [N]
|
|
888
|
+
> - **Test scenarios:** [N]
|
|
889
|
+
> - **Assumptions dicatat:** [N]
|
|
890
|
+
> - **Open questions:** [N]
|
|
891
|
+
>
|
|
892
|
+
> **Rekomendasi langkah selanjutnya:**
|
|
893
|
+
> 1. Review Section 15 (Open Questions) bersama tim
|
|
894
|
+
> 2. Validasi Section 5 (NFR) dengan infrastructure engineer
|
|
895
|
+
> 3. Siap di-feed ke Claude Code / Cursor sebagai context
|
|
896
|
+
|
|
897
|
+
3. Present file ke user via `present_files`
|
|
898
|
+
|
|
899
|
+
---
|
|
900
|
+
|
|
901
|
+
# QUALITY CHECKLIST (WAJIB dijalankan sebelum output final)
|
|
902
|
+
|
|
903
|
+
Sebelum menyerahkan PRD ke user, verifikasi internal:
|
|
904
|
+
|
|
905
|
+
- [ ] Semua 16 section terisi (tidak ada yang kosong / lorem)
|
|
906
|
+
- [ ] Setiap FR punya endpoint di Section 8
|
|
907
|
+
- [ ] Setiap FR punya screen di Section 12
|
|
908
|
+
- [ ] Setiap FR punya test di Section 13
|
|
909
|
+
- [ ] Setiap entitas di Section 7 punya minimal 1 endpoint yang mengaksesnya
|
|
910
|
+
- [ ] Semua Mermaid diagram lolos aturan syntax
|
|
911
|
+
- [ ] Setiap state machine punya transition table
|
|
912
|
+
- [ ] Semua NFR punya angka spesifik (bukan "cepat", "banyak")
|
|
913
|
+
- [ ] Traceability Matrix di Section 16 lengkap
|
|
914
|
+
- [ ] Tidak ada "TBD" di badan PRD — kalau ada, pindah ke Section 15 Open Questions
|
|
915
|
+
- [ ] Design token dan component spec di Section 11 konsisten dengan Screen spec di Section 12
|
|
916
|
+
|
|
917
|
+
Kalau ada gagal check, perbaiki sebelum output.
|