baldart 3.14.1 → 3.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/VERSION +1 -1
  3. package/framework/.claude/agents/REGISTRY.md +10 -0
  4. package/framework/.claude/agents/api-perf-cost-auditor.md +47 -10
  5. package/framework/.claude/agents/coder.md +61 -10
  6. package/framework/.claude/agents/prd-card-writer.md +92 -19
  7. package/framework/.claude/agents/security-reviewer.md +1 -1
  8. package/framework/.claude/skills/bug/references/logging-patterns.md +45 -8
  9. package/framework/.claude/skills/new/SKILL.md +319 -87
  10. package/framework/.claude/skills/prd/SKILL.md +30 -2
  11. package/framework/.claude/skills/prd/assets/card-template.yml +59 -27
  12. package/framework/.claude/skills/prd/assets/epic-template.yml +11 -3
  13. package/framework/.claude/skills/prd/assets/prd-template.md +90 -14
  14. package/framework/.claude/skills/prd/assets/state-template.md +18 -0
  15. package/framework/.claude/skills/prd/references/api-perf-gate.md +102 -52
  16. package/framework/.claude/skills/prd/references/audit-phase.md +13 -13
  17. package/framework/.claude/skills/prd/references/backlog-phase.md +81 -0
  18. package/framework/.claude/skills/prd/references/discovery-phase.md +214 -28
  19. package/framework/.claude/skills/prd/references/prd-writing-phase.md +65 -23
  20. package/framework/.claude/skills/prd/references/research-phase.md +22 -4
  21. package/framework/.claude/skills/prd/references/ui-design-phase.md +115 -3
  22. package/framework/.claude/skills/prd/references/validation-phase.md +22 -2
  23. package/framework/docs/PROJECT-CONFIGURATION.md +41 -1
  24. package/framework/templates/baldart.config.template.yml +25 -0
  25. package/package.json +1 -1
  26. package/src/commands/configure.js +72 -0
  27. package/src/commands/update.js +13 -1
@@ -14,13 +14,21 @@ flagged after implementation.
14
14
 
15
15
  **Gates 1-4 run ONLY** when Gate 5 finds matches OR the PRD contains any of:
16
16
  - `## API contract changes` section with endpoints
17
- - `## Data model` section with new Firestore collections/fields
18
- - Server Actions with Firestore writes
17
+ - `## Data model` section with new {entity}s/{field}s (collections+documents in
18
+ Firestore/Mongo, tables+rows in SQL, items in DynamoDB)
19
+ - Server-side mutations touching the persistence layer
19
20
  - Background jobs, cron, batch operations
20
21
 
21
22
  If none of the above: log `API Performance Gate: N/A — no API/data surface` in
22
23
  state file and skip to "Present and Confirm".
23
24
 
25
+ **Stack-awareness:** before running Gates 1-4, read `stack.database` and
26
+ `stack.framework` from `baldart.config.yml`. The gate sections below have a
27
+ universal core (works for any stack) PLUS stack-specific addenda that activate
28
+ only when the matching value is set. When `stack.database: ""` (unset), the
29
+ gate degrades to the universal core and logs a recommendation to run `npx
30
+ baldart configure` so future gates pick up the stack-specific rules.
31
+
24
32
  ---
25
33
 
26
34
  ## The 5-Gate Protocol
@@ -39,50 +47,80 @@ Scan the PRD text for these keywords. Each match = a finding.
39
47
  | "no limit" / "unlimited" / "senza limiti" / "illimitato" | Unbounded data growth | HIGH |
40
48
  | "save all in document" / "embed list" / "array nel documento" | Unbounded array in doc (max 1MB, degrades at 40K) | HIGH |
41
49
  | "upload file/image via API" / "carica file" | 4.5MB payload limit on Vercel Functions | HIGH |
42
- | "auto-increment ID" / "sequential ID" / "ID sequenziale" | Firestore hotspot | HIGH |
43
- | "search by name" / "full-text search" / "cerca per nome" | Firestore cannot do full-text search | HIGH |
44
- | "page N" / "skip" / "offset" / "pagina N" | Offset pagination (charges for ALL skipped docs) | MEDIUM |
45
- | "send email/SMS" in request path / "invia email/SMS" | Blocking I/O in request path (use waitUntil) | MEDIUM |
46
- | "filter by A and B and C" / "filtra per A e B e C" | Composite index explosion | MEDIUM |
47
- | "import CSV" / "bulk insert" / "importa CSV" | Batch 500-doc limit per write | MEDIUM |
50
+ | "search by name" / "full-text search" / "cerca per nome" | Many DBs need a dedicated search index (Algolia/Typesense/Postgres FTS/Mongo Atlas Search) — DB-specific | HIGH |
51
+ | "page N" / "skip" / "offset" / "pagina N" | Offset pagination is inefficient in most DBs (Firestore charges for skipped docs; SQL scans them) | MEDIUM |
52
+ | "send email/SMS" in request path / "invia email/SMS" | Blocking I/O in request path (use waitUntil / background job) | MEDIUM |
53
+ | "filter by A and B and C" / "filtra per A e B e C" | Composite index explosion (Firestore: explicit indexes; SQL: pick a covering index; Mongo: compound index) | MEDIUM |
54
+
55
+ **Stack-specific keyword addenda** (apply only when matching `stack.database`):
56
+
57
+ | Stack | Keyword | Problem | Severity |
58
+ |-------|---------|---------|----------|
59
+ | firestore | "auto-increment ID" / "sequential ID" / "ID sequenziale" | Firestore write hotspot (sequential keys serialize on a single tablet) | HIGH |
60
+ | firestore | "import CSV" / "bulk insert" / "importa CSV" | Firestore batch limit: 500 docs/write | MEDIUM |
61
+ | firestore / mongodb | "save all in document" / "embed list" / "array nel documento" | Unbounded array in doc (Firestore max 1MB, degrades >40K elements; Mongo 16MB doc cap) | HIGH |
62
+ | postgres / supabase | "SELECT \\*" on hot path | Avoid; specify columns to keep index-only scans viable | MEDIUM |
63
+ | postgres / supabase | "JSONB column" + indexed query | Verify GIN index exists | MEDIUM |
64
+ | dynamodb | "filter expression" instead of KeyCondition | Filter scans full partition — pick a GSI instead | HIGH |
48
65
 
49
66
  **If 0 matches AND no API/data sections**: skip Gates 1-4, proceed to present.
50
67
  **If any match**: continue to Gates 1-4.
51
68
 
52
69
  ### Gate 1 — Data Model Review
53
70
 
54
- Check the PRD `## Data model` section for:
71
+ Universal checks (apply to any persistence layer):
55
72
 
56
- - [ ] **Unbounded arrays**: any array field that can grow without limit → CRITICAL
57
- - [ ] **Document size**: will any document exceed ~100KB regularly? → HIGH
58
- - [ ] **Sequential/predictable IDs**: custom IDs based on timestamp or counter HIGH (Firestore hotspot, use auto-ID)
59
- - [ ] **Fan-out writes**: one user action writing to N documents where N scales with users/data HIGH
60
- - [ ] **Global counters**: fields incremented by many concurrent writers → HIGH (transaction serialization)
61
- - [ ] **Real-time listeners**: justified? Can polling or ISR replace them? → HIGH (1 read/matched doc at attach)
73
+ - [ ] **Unbounded arrays/lists**: any array field/JSONB column/list-typed attribute that can grow without limit → CRITICAL
74
+ - [ ] **Row/document size**: will any row exceed a healthy size (Firestore ~100KB, Mongo 16MB, Postgres tuple alignment) regularly? → HIGH
75
+ - [ ] **Fan-out writes**: one user action writing to N rows/docs where N scales with users/data → HIGH
76
+ - [ ] **Global counters**: columns/fields incremented by many concurrent writers HIGH (write-skew, contention, or transaction serialization depending on stack)
62
77
  - [ ] **Denormalization strategy**: read-heavy paths should denormalize; write-heavy should normalize → MEDIUM
63
- - [ ] **Index exemptions**: large text/description fields should exempt from indexing → MEDIUM (N+1 index writes per doc write)
78
+
79
+ Stack-specific addenda (activate when matching `stack.database`):
80
+
81
+ - [ ] firestore: **Sequential/predictable IDs** (timestamp/counter) → HIGH (write hotspot, use auto-ID)
82
+ - [ ] firestore: **Real-time listeners** — justified? Can polling or ISR replace them? → HIGH (1 read/matched doc at attach + 1 per change)
83
+ - [ ] firestore: **Index exemptions** for large text fields → MEDIUM (N+1 index writes per doc write)
84
+ - [ ] mongodb: **Embedded vs referenced** — embedded grows the parent doc; referenced needs $lookup → MEDIUM
85
+ - [ ] postgres/supabase: **JSONB vs typed columns** — JSONB without GIN index = full scan → MEDIUM
86
+ - [ ] supabase: **RLS coverage** — every new table MUST have RLS policies aligned with the auth contract → CRITICAL
87
+ - [ ] dynamodb: **Hot partition** — does the partition key distribute evenly under load? → HIGH
64
88
 
65
89
  ### Gate 2 — Query & Index Review
66
90
 
67
- Check the PRD `## API contract changes` and data access patterns for:
91
+ Universal checks:
92
+
93
+ - [ ] **Pagination on all list endpoints**: every endpoint returning multiple rows/docs MUST specify cursor-based pagination (keyset / `startAfter` / `cursor`), never raw offset → CRITICAL
94
+ - [ ] **N+1 queries**: "get parent, then for each child get details" pattern → CRITICAL (use a single query with join / `collectionGroup` / `$lookup` / denormalize depending on stack)
95
+ - [ ] **Index table completeness**: if the PRD has read endpoints touching the persistence layer but Section 5 has no `### Database Indexes & Query Optimization` table (or says "nessun indice"), verify this is actually correct by checking each query pattern. Missing the table when compound queries exist → **CRITICAL**
96
+
97
+ Stack-specific addenda:
68
98
 
69
- - [ ] **Pagination on all list endpoints**: every endpoint returning multiple docs MUST specify cursor-based pagination (startAfter), never offset → CRITICAL
70
- - [ ] **N+1 queries**: "get parent, then for each child get details" pattern CRITICAL (use collectionGroup or denormalize)
71
- - [ ] **Composite index specification**: every query combining `where()` + `orderBy()` on different fields, or multiple `where()` on different fields, MUST have an explicit entry in the PRD's `### Firestore Composite Indexes` table (Section 5). Missing index = runtime FAILED_PRECONDITION (500 in production) **CRITICAL**
72
- - [ ] **Composite index count**: >3 inequality filters on different fields = index explosion MEDIUM
73
- - [ ] **orderBy + inequality on different fields**: requires composite index, max 1 inequality per query. Verify the PRD index table includes the correct field order (the inequality field MUST be last in the index definition) **HIGH**
74
- - [ ] **Full-text search**: Firestore doesn't support it natively; require Algolia/Typesense or array-contains workaround HIGH
75
- - [ ] **Index table completeness**: if the PRD has API endpoints that read from Firestore but Section 5 has no `### Firestore Composite Indexes` table (or says "nessun indice"), verify this is actually correct by checking each query pattern. Missing the table when compound queries exist **CRITICAL**
99
+ - [ ] firestore: **Composite index** for every query combining `where()` + `orderBy()` on different fields or multiple `where()` on different fields MUST appear in the PRD index table (Variant A). Missing index = runtime FAILED_PRECONDITION **CRITICAL**
100
+ - [ ] firestore: **orderBy + inequality on different fields** requires composite index, max 1 inequality per query (inequality field MUST be last) → HIGH
101
+ - [ ] firestore: **>3 inequality filters on different fields** = index explosionMEDIUM
102
+ - [ ] firestore: **Full-text search** not native, needs Algolia/Typesense or array-contains workaroundHIGH
103
+ - [ ] postgres/supabase: **EXPLAIN check** on the slowest query in the PRD sequential scans on >100k rowsCRITICAL
104
+ - [ ] postgres/supabase: **Covering index** for hot read pathsMEDIUM
105
+ - [ ] mongodb: **Compound index field order** matches query equality range sort ruleHIGH
106
+ - [ ] dynamodb: **GSI / LSI definition** for every access pattern that doesn't hit the primary key → CRITICAL
76
107
 
77
- ### Gate 3 — Serverless Architecture Review
108
+ ### Gate 3 — Serverless / Runtime Architecture Review
78
109
 
79
- Check endpoint design and background processing for:
110
+ Universal checks:
80
111
 
81
- - [ ] **Payload size**: any endpoint accepting/returning >4.5MB → CRITICAL (use signed URL pattern for uploads)
82
- - [ ] **Sync vs async**: long operations (email, PDF, image processing) in request path → HIGH (use waitUntil or background job)
83
- - [ ] **Function duration**: operations that may exceed 300s HIGH (split into steps or use queue)
84
- - [ ] **CPU-intensive ops**: image resize, PDF gen, heavy computation in serverless → MEDIUM (consider dedicated worker)
85
- - [ ] **GET Route Handler caching**: new GET endpoints MUST specify `revalidate` or `use cache` — dynamic by default since Next.js 15 → HIGH
112
+ - [ ] **Payload size**: any endpoint accepting/returning >4.5MB → CRITICAL (use signed URL / multipart upload pattern). Limit varies by deployment (Vercel 4.5MB, Cloudflare Workers 100MB, AWS Lambda 6MB sync) — calibrate to `stack.deployment`.
113
+ - [ ] **Sync vs async**: long operations (email, PDF, image processing) in request path → HIGH (use queue / waitUntil / background worker per stack)
114
+ - [ ] **Function duration**: operations that may exceed the platform timeout (Vercel 300s Hobby / 800s Pro; Lambda 900s; Cloudflare Workers 30s CPU) → HIGH
115
+ - [ ] **CPU-intensive ops**: image resize, PDF gen, heavy computation in serverless → MEDIUM (consider dedicated worker / container)
116
+
117
+ Stack-specific addenda (`stack.framework`):
118
+
119
+ - [ ] nextjs: **GET Route Handler caching** — new GET endpoints MUST specify `revalidate` or `use cache` — dynamic by default since Next.js 15 → HIGH
120
+ - [ ] nextjs: **Server Action vs Route Handler** chosen explicitly per use case → MEDIUM
121
+ - [ ] remix: **`loader`/`action` separation** + `headers` for `Cache-Control` set explicitly → MEDIUM
122
+ - [ ] sveltekit: **`+page.server.ts` vs `+page.ts`** chosen explicitly; Edge vs Node runtime declared → MEDIUM
123
+ - [ ] astro: **`output: 'static'|'server'|'hybrid'`** declared per route group; `prerender` flag set for static routes → MEDIUM
86
124
 
87
125
  ### Gate 4 — Caching Strategy Review
88
126
 
@@ -123,30 +161,42 @@ Check data display and freshness requirements for:
123
161
 
124
162
  ## Reference Data
125
163
 
126
- ### Firestore Pricing (Blaze Plan)
127
- - Reads: $0.06 / 100K
128
- - Writes: $0.18 / 100K
129
- - Deletes: $0.02 / 100K
130
- - Offset pagination charges for ALL skipped docs
131
- - Index writes: N+1 per document (1 doc + N indexed fields)
132
- - Listener: 1 read/matched doc at attach + 1 per change
133
-
134
- ### Vercel Fluid Compute
135
- - Payload limit: 4.5 MB request/response
136
- - Active CPU pricing: pauses during I/O
137
- - Max duration: 300s (Hobby) / 800s (Pro)
138
- - File descriptor limit: 1,024 shared
139
- - Cold start: archived after 2 weeks (prod) / 48h (preview)
140
-
141
- ### Next.js App Router
142
- - GET Route Handlers: DYNAMIC by default since v15 (must opt-in to caching)
143
- - `use cache` in serverless: in-memory LRU, not persisted across cold starts
144
- - `React.cache()`: single-request dedup only
145
- - Sequential `await` = waterfall; use `Promise.all()` for independent fetches
164
+ Cite ONLY the section matching the project's `stack.database` /
165
+ `stack.framework` / `stack.deployment`. Sections for other stacks are
166
+ informational and should be removed from the populated PRD section.
167
+
168
+ ### Database pricing & cost-model snapshots
169
+
170
+ **Firestore (Blaze Plan):** Reads $0.06/100K, Writes $0.18/100K, Deletes $0.02/100K. Offset pagination charges for ALL skipped docs. Index writes: N+1 per document. Listener: 1 read/matched doc at attach + 1 per change.
171
+
172
+ **Supabase / Postgres:** Compute + storage + egress billing. Hot row updates serialize on a single page; HOT updates and `FILLFACTOR` matter for write-heavy tables. Sequential scan >100k rows is a HIGH finding regardless of price.
173
+
174
+ **MongoDB Atlas:** Cluster tier dictates IOPS ceiling. Aggregation `$lookup` cost ≈ N queries when uncached. WiredTiger cache hit rate <90% on hot path = HIGH finding.
175
+
176
+ **DynamoDB:** On-demand vs provisioned billing model dictates RCU/WCU sizing. Hot partition (skewed PK distribution) triggers throttle errors before billing impact shows.
177
+
178
+ ### Runtime limits (by `stack.deployment`)
179
+
180
+ | Platform | Payload | Duration | Notes |
181
+ |----------|---------|----------|-------|
182
+ | Vercel (Fluid Compute) | 4.5 MB | 300s Hobby / 800s Pro | Active CPU pricing, FD limit 1024 shared, archived after 2w prod / 48h preview |
183
+ | Cloudflare Workers | 100 MB | 30s CPU | No FD pool, smaller heap |
184
+ | AWS Lambda | 6 MB sync / 256 KB async | 900s | Cold start ~100-500ms; provisioned concurrency to mitigate |
185
+ | Firebase Functions (gen 2) | 32 MB | 540s 2nd-gen | Cold start ~200-2000ms |
186
+
187
+ ### Framework caching primitives (by `stack.framework`)
188
+
189
+ **Next.js App Router:** GET Route Handlers DYNAMIC by default since v15 (opt-in to caching via `revalidate` / `'use cache'`). `'use cache'` in serverless: in-memory LRU, not persisted across cold starts. `React.cache()`: single-request dedup only. Sequential `await` = waterfall; use `Promise.all()` for independent fetches.
190
+
191
+ **Remix:** `headers` function sets `Cache-Control`; `loader` revalidation via `shouldRevalidate`. No built-in cache layer — relies on CDN.
192
+
193
+ **SvelteKit:** `setHeaders({'cache-control': ...})` in `load`; `prerender = true` for static; Edge vs Node runtime per route.
194
+
195
+ **Astro:** SSG by default; `output: 'server'` enables on-demand rendering; per-route `prerender` toggle.
146
196
 
147
197
  ### When to Skip
148
198
 
149
199
  - Hotfix cards (single-line bug fixes): Gate 5 only
150
- - Purely frontend cards (no API/Firestore): skip entirely
200
+ - Purely frontend cards (no persistence/API touch): skip entirely
151
201
  - Documentation-only: skip entirely
152
202
  - Prototype/spike (throw-away): skip entirely
@@ -24,11 +24,11 @@ Scan each card for security signals to decide whether the `security-reviewer` ag
24
24
  |--------|---------------|
25
25
  | **New or modified API route** | `files_likely_touched` contains `route.ts`, `api/`, or requirements mention new endpoints |
26
26
  | **Authentication/authorization changes** | Requirements/files mention `withAuth`, `checkPermission`, `permissions`, login, session, token, JWT, OAuth |
27
- | **Firestore security rules** | `files_likely_touched` contains `firestore.rules` or requirements mention rule changes |
27
+ | **Persistence-layer access rules** | `files_likely_touched` contains rule/policy files matching `stack.database` (e.g. `firestore.rules`, Supabase RLS migration, MongoDB validator) or requirements mention rule changes |
28
28
  | **External integrations** | Requirements mention webhooks, third-party APIs, payment, SMS, email providers, or external callbacks |
29
29
  | **File upload or media handling** | Requirements mention upload, image, file, media, or `files_likely_touched` contains upload/media paths |
30
30
  | **User input processing** | Requirements mention forms, search, filters, or query parameters that flow into DB queries or server logic |
31
- | **Multi-tenant data access** | Requirements mention cross-store, cross-merchant, or data visible to multiple tenants |
31
+ | **Multi-tenant data access** | Requirements mention cross-tenant boundaries (cross-org, cross-workspace, cross-store, cross-account — actual tenant noun from `identity.audience_segments[]`) or data visible to multiple tenants |
32
32
  | **Sensitive data handling** | Requirements mention PII, credentials, tokens, secrets, or personal data |
33
33
 
34
34
  **How to assess**: Read each card's `requirements`, `acceptance_criteria`, `files_likely_touched`, `areas`, `existing_patterns`, `anti_patterns`, `validation_commands`, `error_handling`, and `scope_boundaries` fields. This is a deterministic check — no LLM judgment calls needed.
@@ -48,7 +48,7 @@ Scan each card for performance signals to decide whether the `api-perf-cost-audi
48
48
  | Signal | Where to look |
49
49
  |--------|---------------|
50
50
  | **New or modified API route** | `files_likely_touched` contains `route.ts`, `api/`, or requirements mention new endpoints |
51
- | **Firestore read/write operations** | Requirements mention collection, query, document, write, transaction, batch, listener, onSnapshot |
51
+ | **Persistence-layer read/write operations** | Requirements mention {entity} read/write, query, transaction, batch, listener/subscription/change-stream (terminology depends on `stack.database`) |
52
52
  | **List/search/filter endpoints** | Requirements mention listing, searching, filtering, sorting, pagination, or "show all" |
53
53
  | **Background/batch processing** | Requirements mention cron, batch, import, export, bulk, queue, or scheduled tasks |
54
54
  | **Real-time/live updates** | Requirements mention real-time, live, auto-update, listener, onSnapshot, subscription |
@@ -172,12 +172,12 @@ Requirements smell detection:
172
172
  - Compound requirements covering multiple behaviors
173
173
  - Dependency shadows: implicit deps not in depends_on
174
174
 
175
- Firestore-specific (this project uses Firestore):
176
- - Unbounded reads without .limit()
177
- - Offset-based pagination instead of cursor-based
178
- - getDoc() in loops instead of batch reads
179
- - Missing composite index declarations
180
- - Transaction hotspot risks
175
+ Database-specific (apply the subset matching `stack.database`):
176
+ - Universal: unbounded reads without limit, offset-based pagination instead of cursor/keyset, N+1 read in loops, transaction/concurrency hotspot risks
177
+ - firestore: missing composite index declarations, sequential-ID hotspots, listener attach cost not budgeted
178
+ - postgres/supabase: sequential scan on >100k rows, RLS policy missing on new table, JSONB without GIN index
179
+ - mongodb: missing compound index for hot aggregation, embedded array unbounded growth
180
+ - dynamodb: missing GSI for non-PK access pattern, hot-partition risk
181
181
 
182
182
  Card structure:
183
183
  - files_likely_touched missing entries or conflicting across cards
@@ -339,8 +339,8 @@ After challenge pass, rank ALL surviving findings relative to each other by impa
339
339
  ### Severity Calibration Examples
340
340
 
341
341
  **HIGH** (must fix before implementation):
342
- - "acceptance_criteria says 'user can see bookings' but doesn't specify pagination → unbounded Firestore read"
343
- > Evidence: "AC-2: Il merchant visualizza le prenotazioni" — no limit/pagination mentioned
342
+ - "acceptance_criteria says 'user can see <list>' but doesn't specify pagination → unbounded persistence-layer read"
343
+ > Evidence: "AC-2: <persona from identity.audience_segments[]> views <entity>" — no limit/pagination mentioned
344
344
 
345
345
  **MEDIUM** (should fix, skip if ambiguous):
346
346
  - "files_likely_touched missing the API route doc update"
@@ -359,9 +359,9 @@ After challenge pass, rank ALL surviving findings relative to each other by impa
359
359
 
360
360
  **doc-reviewer**: Check documentation links, PRD references are valid and aligned, planned changes requiring doc updates not mentioned. Verify `files_likely_touched` includes doc files. Check `areas` completeness. Flag `git_strategy: TBD`. Include Obsidian trigger assessment (section H) in findings -- evaluate whether the planned docs will require KB sync per `.claude/skills/doc-reviewer-support/references/obsidian-integration.md`.
361
361
 
362
- **api-perf-cost-auditor** (only when `perf_review_needed: true`): Apply the 5-gate protocol from `.claude/agent-memory/senior-researcher/api-perf-cost-audit-protocol.md`. Read referenced source files. Check: unbounded reads, N+1 queries, fan-out writes, missing pagination, offset pagination, missing GET Route Handler caching, listener vs polling costs, 4.5MB payload limits, transaction hotspots.
362
+ **api-perf-cost-auditor** (only when `perf_review_needed: true`): Apply the 5-gate protocol from `.claude/agent-memory/senior-researcher/api-perf-cost-audit-protocol.md`. Read referenced source files. Universal checks: unbounded reads, N+1 queries, fan-out writes, missing pagination, offset pagination, listener vs polling costs, payload size limits per `stack.deployment`, transaction hotspots. Stack-specific addenda apply per `stack.database` + `stack.framework` (see `framework/.claude/skills/prd/references/api-perf-gate.md`).
363
363
 
364
- **security-reviewer** (only when `security_review_needed: true`): Read `.claude/agents/security-reviewer.md` for full methodology. Focus on: auth gaps, input validation, multi-tenant isolation, Firestore rules alignment, sensitive data exposure, webhook validation, rate limiting, IDOR risks.
364
+ **security-reviewer** (only when `security_review_needed: true`): Read `.claude/agents/security-reviewer.md` for full methodology. Focus on: auth gaps, input validation, multi-tenant isolation, persistence-layer access rules alignment (Firestore rules / Supabase RLS / Mongo validator / DynamoDB IAM — per `stack.database`), sensitive data exposure, webhook validation, rate limiting, IDOR risks.
365
365
 
366
366
  ## Step 6.7 — Collect & Merge Findings
367
367
 
@@ -4,6 +4,87 @@
4
4
 
5
5
  Mark task 4 as `in_progress`.
6
6
 
7
+ ## MANDATORY Card Rules (zero tolerance — read first)
8
+
9
+ These two rules apply to EVERY child card (epics use `owner_agent: ""`). The
10
+ `prd-card-writer` agent enforces them; the skill MUST verify post-generation
11
+ and HALT if violated.
12
+
13
+ ### Rule A — `owner_agent` is mandatory and enum-validated
14
+
15
+ Every child card MUST declare `owner_agent` with one of the five values:
16
+
17
+ ```
18
+ coder | ui-expert | plan | visual-designer | motion-expert
19
+ ```
20
+
21
+ There is no implicit default and `claude` is NOT a valid value. Cards with
22
+ missing, empty, placeholder, or non-enum values fail Step 6 (validation-phase)
23
+ and BLOCK the commit. The `/new` orchestrator dispatches implementation
24
+ agent by this field (see `framework/.claude/skills/new/SKILL.md` § Agent
25
+ Routing), so a wrong value silently routes the card to the wrong specialist.
26
+
27
+ ### Rule B — UI cards have UI-only scope (no mixed scope)
28
+
29
+ A card with `owner_agent: ui-expert` MUST be scoped EXCLUSIVELY to graphical
30
+ concerns: components, layout, styling, token usage, motion, accessibility.
31
+ It MUST NOT bundle:
32
+
33
+ - business logic / domain rules
34
+ - API integration or data fetching
35
+ - state management beyond local UI state
36
+ - form validation logic (rendering validation states is UI; the rules are not)
37
+ - backend changes (routes, handlers, DB schema)
38
+
39
+ If a feature step involves BOTH UI and logic, the writer MUST split it into:
40
+
41
+ - one `owner_agent: ui-expert` card — visual implementation, consumes mocked
42
+ or contract-stable data
43
+ - one `owner_agent: coder` card — logic, API call, state, persistence
44
+
45
+ The two cards are wired via `depends_on:` / `blocks:`. A common ordering is
46
+ `coder → ui-expert` (contract ready before UI), but the reverse is acceptable
47
+ when the UI card consumes a hand-written mock.
48
+
49
+ #### Split example — Login feature
50
+
51
+ **Bad** (single mixed card):
52
+
53
+ ```yaml
54
+ id: FEAT-XXXX-01
55
+ owner_agent: coder
56
+ scope:
57
+ summary: |
58
+ Implement the login page: form layout, validation states, call to
59
+ POST /api/auth/login, token storage, redirect to dashboard.
60
+ ```
61
+
62
+ **Good** (split into UI + logic):
63
+
64
+ ```yaml
65
+ # UI card — visual only, consumes a mocked auth contract
66
+ id: FEAT-XXXX-02
67
+ owner_agent: ui-expert
68
+ depends_on: [FEAT-XXXX-01]
69
+ scope:
70
+ summary: |
71
+ Login form layout (email/password fields, primary CTA, link to recovery),
72
+ visual states (idle / loading / error / success), a11y (focus order,
73
+ aria-live for inline errors), responsive breakpoints. Consumes the
74
+ `useAuth()` mock provided by FEAT-XXXX-01.
75
+ ```
76
+
77
+ ```yaml
78
+ # Logic card — API call + state + redirect
79
+ id: FEAT-XXXX-01
80
+ owner_agent: coder
81
+ blocks: [FEAT-XXXX-02]
82
+ scope:
83
+ summary: |
84
+ POST /api/auth/login handler, token storage in httpOnly cookie, redirect
85
+ on success, `useAuth()` hook exposing { status, error, signIn }.
86
+ ```
87
+
7
88
  ## MANDATORY Card Structure (zero tolerance — read first)
8
89
 
9
90
  Every PRD generates **1 epic card + N children**, regardless of N. The