@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.
@@ -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.