compound-workflow 0.1.1

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 (55) hide show
  1. package/.claude-plugin/marketplace.json +11 -0
  2. package/.claude-plugin/plugin.json +12 -0
  3. package/.cursor-plugin/plugin.json +12 -0
  4. package/README.md +155 -0
  5. package/package.json +22 -0
  6. package/scripts/install-cli.mjs +313 -0
  7. package/scripts/sync-into-repo.sh +103 -0
  8. package/src/.agents/agents/research/best-practices-researcher.md +132 -0
  9. package/src/.agents/agents/research/framework-docs-researcher.md +134 -0
  10. package/src/.agents/agents/research/git-history-analyzer.md +62 -0
  11. package/src/.agents/agents/research/learnings-researcher.md +288 -0
  12. package/src/.agents/agents/research/repo-research-analyst.md +146 -0
  13. package/src/.agents/agents/review/agent-native-reviewer.md +299 -0
  14. package/src/.agents/agents/workflow/bug-reproduction-validator.md +87 -0
  15. package/src/.agents/agents/workflow/lint.md +20 -0
  16. package/src/.agents/agents/workflow/spec-flow-analyzer.md +149 -0
  17. package/src/.agents/commands/assess.md +60 -0
  18. package/src/.agents/commands/install.md +53 -0
  19. package/src/.agents/commands/metrics.md +59 -0
  20. package/src/.agents/commands/setup.md +9 -0
  21. package/src/.agents/commands/sync.md +9 -0
  22. package/src/.agents/commands/test-browser.md +393 -0
  23. package/src/.agents/commands/workflow/brainstorm.md +252 -0
  24. package/src/.agents/commands/workflow/compound.md +142 -0
  25. package/src/.agents/commands/workflow/plan.md +737 -0
  26. package/src/.agents/commands/workflow/review-v2.md +148 -0
  27. package/src/.agents/commands/workflow/review.md +110 -0
  28. package/src/.agents/commands/workflow/triage.md +54 -0
  29. package/src/.agents/commands/workflow/work.md +439 -0
  30. package/src/.agents/references/README.md +12 -0
  31. package/src/.agents/references/standards/README.md +9 -0
  32. package/src/.agents/scripts/self-check.mjs +227 -0
  33. package/src/.agents/scripts/sync-opencode.mjs +355 -0
  34. package/src/.agents/skills/agent-browser/SKILL.md +223 -0
  35. package/src/.agents/skills/audit-traceability/SKILL.md +260 -0
  36. package/src/.agents/skills/brainstorming/SKILL.md +250 -0
  37. package/src/.agents/skills/compound-docs/SKILL.md +533 -0
  38. package/src/.agents/skills/compound-docs/assets/critical-pattern-template.md +34 -0
  39. package/src/.agents/skills/compound-docs/assets/resolution-template.md +97 -0
  40. package/src/.agents/skills/compound-docs/references/yaml-schema.md +87 -0
  41. package/src/.agents/skills/compound-docs/schema.project.yaml +18 -0
  42. package/src/.agents/skills/compound-docs/schema.yaml +119 -0
  43. package/src/.agents/skills/data-foundations/SKILL.md +185 -0
  44. package/src/.agents/skills/document-review/SKILL.md +108 -0
  45. package/src/.agents/skills/file-todos/SKILL.md +177 -0
  46. package/src/.agents/skills/file-todos/assets/todo-template.md +106 -0
  47. package/src/.agents/skills/financial-workflow-integrity/SKILL.md +423 -0
  48. package/src/.agents/skills/git-worktree/SKILL.md +268 -0
  49. package/src/.agents/skills/pii-protection-prisma/SKILL.md +629 -0
  50. package/src/.agents/skills/process-metrics/SKILL.md +46 -0
  51. package/src/.agents/skills/process-metrics/assets/daily-template.md +37 -0
  52. package/src/.agents/skills/process-metrics/assets/monthly-template.md +21 -0
  53. package/src/.agents/skills/process-metrics/assets/weekly-template.md +25 -0
  54. package/src/.agents/skills/technical-review/SKILL.md +83 -0
  55. package/src/AGENTS.md +213 -0
@@ -0,0 +1,629 @@
1
+ ---
2
+ name: pii-protection-prisma
3
+ description: Enforce PII table separation + envelope encryption + KEK rotation for Prisma + Postgres.
4
+ ---
5
+
6
+ # PII Protection & Encryption Standard (Prisma + Postgres)
7
+
8
+ ## Purpose
9
+
10
+ Provide an enforceable, production-grade standard for storing and handling PII in systems that may be regulated or financially sensitive.
11
+
12
+ This skill is designed to be followed as a build-time guardrail:
13
+
14
+ - clear rules (MUST / SHOULD / MUST NOT)
15
+ - concrete schema requirements
16
+ - concrete encryption + rotation approach
17
+ - operational failure modes + runbooks
18
+ - Prisma + Postgres reference implementation
19
+
20
+ ---
21
+
22
+ ## Scope
23
+
24
+ Applies to any system that stores or processes:
25
+
26
+ - identity information (name, DOB, address, document ids)
27
+ - contact details (email/phone) when linked to an individual
28
+ - government identifiers
29
+ - bank identifiers (BSB/account), tax identifiers
30
+ - uploaded documents containing personal information
31
+ - free-text fields where users may provide personal info
32
+
33
+ ---
34
+
35
+ ## Definitions
36
+
37
+ - PII: personally identifiable information.
38
+ - DEK: Data Encryption Key. Random per-record symmetric key used to encrypt PII payload.
39
+ - KEK: Key Encryption Key. A master key stored in a KMS used to wrap (encrypt) DEKs.
40
+ - Envelope Encryption: Encrypt data with a DEK; encrypt the DEK with a KEK; store encrypted DEK + ciphertext.
41
+
42
+ ---
43
+
44
+ ## Non-Negotiable Rules (MUST / MUST NOT)
45
+
46
+ ### Data placement
47
+
48
+ - MUST store PII outside primary business tables.
49
+ - MUST NOT store PII inside `applications.draftData` or any general JSON blob in a business table.
50
+ - MUST store PII in a dedicated table (1:1 with owning entity) using encrypted columns.
51
+ - MUST keep audit logs free of plaintext PII.
52
+
53
+ ### Encryption
54
+
55
+ - MUST use envelope encryption by default for PII at rest.
56
+ - MUST use an AEAD cipher (AES-256-GCM) for PII payload encryption.
57
+ - MUST generate a unique random IV/nonce per encryption.
58
+ - MUST bind ciphertext to record identity using AAD (Associated Authenticated Data).
59
+ - MUST store `keyId` and `schemaVersion` per record.
60
+ - MUST NOT use deterministic encryption for general PII fields.
61
+
62
+ ### Key management
63
+
64
+ - MUST store KEKs in a KMS or secure secret manager (not the DB).
65
+ - MUST support multiple active key versions for decryption.
66
+ - MUST support key rotation without downtime.
67
+
68
+ ### Logging & analytics
69
+
70
+ - MUST NOT log decrypted PII.
71
+ - MUST NOT emit PII to analytics pipelines.
72
+ - MUST implement structured redaction for logs and error reporting.
73
+
74
+ ---
75
+
76
+ ## Architecture: Table Separation
77
+
78
+ ### What belongs in the business table
79
+
80
+ Keep only non-sensitive business state:
81
+
82
+ - lifecycle status, steps
83
+ - timestamps
84
+ - risk flags / derived fields (non-PII)
85
+ - foreign keys / references
86
+
87
+ ### What belongs in the PII table
88
+
89
+ - encrypted payload (ciphertext)
90
+ - encrypted DEK
91
+ - key metadata (key id, versions)
92
+ - schema version
93
+ - timestamps
94
+
95
+ ### Draft vs submitted data
96
+
97
+ If your workflow has drafts:
98
+
99
+ - MUST keep draft PII encrypted the same way as submitted PII.
100
+ - MUST NOT keep draft PII in general draft JSON.
101
+
102
+ Practical approach:
103
+
104
+ - `applications.draftData` -> non-PII draft fields only
105
+ - `application_pii` -> encrypted PII payload including draft PII
106
+
107
+ ---
108
+
109
+ ## Required Postgres Schema
110
+
111
+ ### Recommended DDL (bytea for ciphertext)
112
+
113
+ ```sql
114
+ CREATE TABLE application_pii (
115
+ application_id uuid PRIMARY KEY REFERENCES applications(id) ON DELETE CASCADE,
116
+
117
+ -- Wrapped DEK (encrypted with KEK) - required for envelope encryption
118
+ dek_ciphertext bytea NOT NULL,
119
+
120
+ -- PII payload encrypted with DEK (AES-256-GCM)
121
+ pii_ciphertext bytea NOT NULL,
122
+
123
+ -- Key metadata
124
+ kek_key_id text NOT NULL, -- e.g. "pii-kek-v3"
125
+ schema_version int NOT NULL DEFAULT 1,
126
+
127
+ -- Optional: integrity/ops metadata
128
+ pii_hash text NULL, -- hash of plaintext (for change detection) - do NOT use for lookups
129
+ last_decrypted_at timestamptz NULL, -- optional, for ops; do not over-collect
130
+
131
+ created_at timestamptz NOT NULL DEFAULT now(),
132
+ updated_at timestamptz NOT NULL DEFAULT now()
133
+ );
134
+
135
+ CREATE INDEX application_pii_kek_key_id_idx ON application_pii(kek_key_id);
136
+ ```
137
+
138
+ ### Storage format requirements
139
+
140
+ - `dek_ciphertext` stores the wrapped DEK returned by KMS (opaque bytes).
141
+ - `pii_ciphertext` stores a versioned AEAD blob described below.
142
+
143
+ ---
144
+
145
+ ## Ciphertext Format (PII Payload)
146
+
147
+ ### PII payload encryption format (recommended)
148
+
149
+ Store as `bytea` with explicit layout:
150
+
151
+ - 1 byte: format version (currently `0x01`)
152
+ - 12 bytes: IV/nonce (random per encryption)
153
+ - 16 bytes: auth tag (GCM)
154
+ - N bytes: ciphertext
155
+
156
+ Layout:
157
+
158
+ ```
159
+ [ v1 ][ iv (12) ][ tag (16) ][ ciphertext (N) ]
160
+ ```
161
+
162
+ Why:
163
+
164
+ - easy decoding
165
+ - supports future algorithm changes via the format version byte
166
+
167
+ ---
168
+
169
+ ## AAD (Associated Authenticated Data)
170
+
171
+ ### AAD MUST include
172
+
173
+ Bind ciphertext to its intended record to prevent blob swapping:
174
+
175
+ - `application_id`
176
+ - `schema_version`
177
+
178
+ Recommended AAD string:
179
+
180
+ ```
181
+ application_pii:{application_id}:{schema_version}
182
+ ```
183
+
184
+ If a ciphertext blob is copied to another `application_id`, decryption must fail authentication.
185
+
186
+ ---
187
+
188
+ ## Envelope Encryption Flow (Standard)
189
+
190
+ ### Encrypt (write/update PII)
191
+
192
+ 1. Validate PII object against schema (zod).
193
+ 2. Serialize to JSON bytes.
194
+ 3. Generate random DEK (32 bytes).
195
+ 4. Encrypt JSON bytes with DEK using AES-256-GCM (with AAD).
196
+ 5. Wrap DEK using KEK via KMS (returns `dek_ciphertext`).
197
+ 6. Store `dek_ciphertext`, `pii_ciphertext`, `kek_key_id`, `schema_version`.
198
+
199
+ ### Decrypt (read PII)
200
+
201
+ 1. Load `dek_ciphertext`, `pii_ciphertext`, `kek_key_id`, `schema_version`.
202
+ 2. Unwrap DEK using KMS + `kek_key_id`.
203
+ 3. Decrypt `pii_ciphertext` using DEK and computed AAD.
204
+ 4. Parse JSON.
205
+ 5. Validate decrypted payload against schema version.
206
+
207
+ ---
208
+
209
+ ## Prisma Models (Production Baseline)
210
+
211
+ ```prisma
212
+ model Application {
213
+ id String @id @default(uuid()) @db.Uuid
214
+ userId String @db.Uuid
215
+
216
+ status ApplicationStatus @default(DRAFT)
217
+ currentStep String @default("start")
218
+ draftData Json @default("{}") // MUST be non-PII only
219
+
220
+ version Int @default(1)
221
+
222
+ createdAt DateTime @default(now())
223
+ updatedAt DateTime @updatedAt
224
+
225
+ pii ApplicationPII?
226
+ }
227
+
228
+ model ApplicationPII {
229
+ applicationId String @id @db.Uuid
230
+ application Application @relation(fields: [applicationId], references: [id], onDelete: Cascade)
231
+
232
+ dekCiphertext Bytes
233
+ piiCiphertext Bytes
234
+
235
+ kekKeyId String
236
+ schemaVersion Int @default(1)
237
+
238
+ piiHash String?
239
+ createdAt DateTime @default(now())
240
+ updatedAt DateTime @updatedAt
241
+
242
+ @@index([kekKeyId])
243
+ }
244
+
245
+ enum ApplicationStatus {
246
+ DRAFT
247
+ SUBMITTED
248
+ PROCESSING
249
+ AWAITING_EXTERNAL
250
+ AWAITING_REVIEW
251
+ APPROVED
252
+ REJECTED
253
+ FAILED
254
+ }
255
+ ```
256
+
257
+ ---
258
+
259
+ ## Reference Implementation (TypeScript)
260
+
261
+ ### PII Schema (zod)
262
+
263
+ Version your schema explicitly.
264
+
265
+ ```ts
266
+ import { z } from "zod";
267
+
268
+ export const PiiV1 = z.object({
269
+ fullName: z.string().min(1),
270
+ dob: z.string().min(4), // prefer ISO date string; validate more strictly in real code
271
+ address: z.object({
272
+ line1: z.string().min(1),
273
+ suburb: z.string().min(1),
274
+ postcode: z.string().min(3),
275
+ country: z.string().min(2),
276
+ }),
277
+ email: z.string().email().optional(),
278
+ phone: z.string().optional(),
279
+ governmentId: z.string().optional(),
280
+ bank: z
281
+ .object({
282
+ bsb: z.string().optional(),
283
+ accountNumber: z.string().optional(),
284
+ })
285
+ .optional(),
286
+ });
287
+ export type PiiV1Type = z.infer<typeof PiiV1>;
288
+ ```
289
+
290
+ ### Crypto helpers (AES-256-GCM)
291
+
292
+ Use Node `crypto` for AEAD. Keep it isolated in a module.
293
+
294
+ ```ts
295
+ import crypto from "crypto";
296
+
297
+ const IV_LEN = 12;
298
+ const TAG_LEN = 16;
299
+ const FORMAT_V1 = 0x01;
300
+
301
+ export function encryptAesGcm(params: {
302
+ key: Buffer; // 32 bytes
303
+ plaintext: Buffer;
304
+ aad: Buffer;
305
+ }): Buffer {
306
+ if (params.key.length !== 32) throw new Error("DEK must be 32 bytes");
307
+ const iv = crypto.randomBytes(IV_LEN);
308
+ const cipher = crypto.createCipheriv("aes-256-gcm", params.key, iv);
309
+ cipher.setAAD(params.aad);
310
+ const ciphertext = Buffer.concat([cipher.update(params.plaintext), cipher.final()]);
311
+ const tag = cipher.getAuthTag();
312
+
313
+ return Buffer.concat([Buffer.from([FORMAT_V1]), iv, tag, ciphertext]);
314
+ }
315
+
316
+ export function decryptAesGcm(params: {
317
+ key: Buffer;
318
+ blob: Buffer;
319
+ aad: Buffer;
320
+ }): Buffer {
321
+ const version = params.blob.readUInt8(0);
322
+ if (version !== FORMAT_V1) throw new Error(`Unsupported ciphertext version: ${version}`);
323
+
324
+ const iv = params.blob.subarray(1, 1 + IV_LEN);
325
+ const tag = params.blob.subarray(1 + IV_LEN, 1 + IV_LEN + TAG_LEN);
326
+ const ciphertext = params.blob.subarray(1 + IV_LEN + TAG_LEN);
327
+
328
+ const decipher = crypto.createDecipheriv("aes-256-gcm", params.key, iv);
329
+ decipher.setAAD(params.aad);
330
+ decipher.setAuthTag(tag);
331
+ return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
332
+ }
333
+ ```
334
+
335
+ ### KMS interface (KEK wrap/unwrap)
336
+
337
+ You will integrate a KMS provider (AWS KMS, Azure Key Vault, GCP KMS). Keep it behind an interface.
338
+
339
+ ```ts
340
+ export interface KekProvider {
341
+ /** Wraps (encrypts) a DEK under the specified KEK key version. */
342
+ wrapDek(params: { kekKeyId: string; dek: Buffer }): Promise<Buffer>;
343
+ /** Unwraps (decrypts) a wrapped DEK using the specified KEK key version. */
344
+ unwrapDek(params: { kekKeyId: string; wrappedDek: Buffer }): Promise<Buffer>;
345
+ }
346
+ ```
347
+
348
+ ### End-to-end encrypt/decrypt
349
+
350
+ ```ts
351
+ import { PiiV1 } from "./piiSchema";
352
+ import { encryptAesGcm, decryptAesGcm } from "./crypto";
353
+ import crypto from "crypto";
354
+
355
+ function aadFor(applicationId: string, schemaVersion: number): Buffer {
356
+ return Buffer.from(`application_pii:${applicationId}:${schemaVersion}`, "utf8");
357
+ }
358
+
359
+ export async function encryptPiiRecord(params: {
360
+ applicationId: string;
361
+ schemaVersion: number;
362
+ kekKeyId: string;
363
+ pii: unknown;
364
+ kek: KekProvider;
365
+ }): Promise<{ dekCiphertext: Buffer; piiCiphertext: Buffer; piiHash: string }> {
366
+ // Validate
367
+ const parsed = PiiV1.parse(params.pii);
368
+
369
+ const plaintext = Buffer.from(JSON.stringify(parsed), "utf8");
370
+ const dek = crypto.randomBytes(32);
371
+
372
+ const piiCiphertext = encryptAesGcm({
373
+ key: dek,
374
+ plaintext,
375
+ aad: aadFor(params.applicationId, params.schemaVersion),
376
+ });
377
+
378
+ const dekCiphertext = await params.kek.wrapDek({ kekKeyId: params.kekKeyId, dek });
379
+
380
+ // Optional: hash for change detection (NOT for searching)
381
+ const piiHash = crypto.createHash("sha256").update(plaintext).digest("hex");
382
+
383
+ return { dekCiphertext, piiCiphertext, piiHash };
384
+ }
385
+
386
+ export async function decryptPiiRecord(params: {
387
+ applicationId: string;
388
+ schemaVersion: number;
389
+ kekKeyId: string;
390
+ dekCiphertext: Buffer;
391
+ piiCiphertext: Buffer;
392
+ kek: KekProvider;
393
+ }): Promise<unknown> {
394
+ const dek = await params.kek.unwrapDek({
395
+ kekKeyId: params.kekKeyId,
396
+ wrappedDek: params.dekCiphertext,
397
+ });
398
+
399
+ const plaintext = decryptAesGcm({
400
+ key: dek,
401
+ blob: params.piiCiphertext,
402
+ aad: aadFor(params.applicationId, params.schemaVersion),
403
+ });
404
+
405
+ const obj = JSON.parse(plaintext.toString("utf8"));
406
+
407
+ // Validate against schema version (example uses v1)
408
+ return PiiV1.parse(obj);
409
+ }
410
+ ```
411
+
412
+ ---
413
+
414
+ ## Prisma Usage Patterns
415
+
416
+ ### Write/update PII (upsert)
417
+
418
+ - Encrypt in application code.
419
+ - Store ciphertext + wrapped DEK.
420
+ - Never store plaintext at rest.
421
+
422
+ ```ts
423
+ await prisma.applicationPII.upsert({
424
+ where: { applicationId },
425
+ create: {
426
+ applicationId,
427
+ dekCiphertext,
428
+ piiCiphertext,
429
+ kekKeyId,
430
+ schemaVersion,
431
+ piiHash,
432
+ },
433
+ update: {
434
+ dekCiphertext,
435
+ piiCiphertext,
436
+ kekKeyId,
437
+ schemaVersion,
438
+ piiHash,
439
+ },
440
+ });
441
+ ```
442
+
443
+ ### Read PII (explicit, controlled)
444
+
445
+ Avoid `include: { pii: true }` as a default. Only include PII in carefully scoped code paths.
446
+
447
+ ```ts
448
+ const row = await prisma.applicationPII.findUnique({ where: { applicationId } });
449
+ if (!row) return null;
450
+
451
+ const pii = await decryptPiiRecord({
452
+ applicationId,
453
+ schemaVersion: row.schemaVersion,
454
+ kekKeyId: row.kekKeyId,
455
+ dekCiphertext: row.dekCiphertext,
456
+ piiCiphertext: row.piiCiphertext,
457
+ kek,
458
+ });
459
+ ```
460
+
461
+ ---
462
+
463
+ ## Key Rotation (KEK Rotation)
464
+
465
+ ### Requirements
466
+
467
+ - MUST support decrypt with old and new KEK key versions.
468
+ - MUST encrypt new writes with the latest KEK version.
469
+ - MUST provide a migration plan to re-wrap DEKs and (optionally) re-encrypt payloads.
470
+
471
+ ### Standard rotation phases
472
+
473
+ 1. Introduce new KEK (e.g. v4) in KMS.
474
+ 2. Deploy app that:
475
+ - wraps new DEKs under v4
476
+ - unwraps DEKs under v1..v4 as needed
477
+ 3. Migrate existing rows.
478
+
479
+ #### Migration Option A: Re-wrap DEK only (preferred)
480
+
481
+ - unwrap DEK using old KEK
482
+ - wrap same DEK with new KEK
483
+ - update `dek_ciphertext`, `kek_key_id`
484
+ - no need to re-encrypt `pii_ciphertext` (payload stays encrypted under DEK)
485
+
486
+ #### Migration Option B: Full re-encrypt (rare)
487
+
488
+ - decrypt payload
489
+ - generate new DEK
490
+ - encrypt payload
491
+ - wrap new DEK
492
+
493
+ Use only if you suspect DEK exposure or require periodic payload re-encryption.
494
+
495
+ ### Rotation completeness check
496
+
497
+ - query: `SELECT count(*) FROM application_pii WHERE kek_key_id != 'pii-kek-v4';`
498
+ - must reach 0 before retiring v1..v3
499
+
500
+ ---
501
+
502
+ ## Failure Modes & Required Behavior
503
+
504
+ ### Decryption fails (auth tag / AAD mismatch)
505
+
506
+ Likely causes:
507
+
508
+ - wrong application id/AAD
509
+ - ciphertext corruption
510
+ - tampering
511
+
512
+ Required behavior:
513
+
514
+ - treat as security incident signal
515
+ - return safe error (no payload leakage)
516
+ - emit security event (not containing PII)
517
+
518
+ ### KMS unwrap fails
519
+
520
+ Likely causes:
521
+
522
+ - missing permissions
523
+ - wrong key id
524
+ - key disabled/rotated incorrectly
525
+
526
+ Required behavior:
527
+
528
+ - fail closed (do not proceed)
529
+ - alert immediately
530
+ - degrade endpoints that require PII
531
+
532
+ ### Schema validation fails after decryption
533
+
534
+ Likely causes:
535
+
536
+ - schema drift
537
+ - historical bad data
538
+
539
+ Required behavior:
540
+
541
+ - surface a controlled "data format invalid" error
542
+ - provide migration path
543
+ - log only metadata (schemaVersion, appId), never the plaintext
544
+
545
+ ---
546
+
547
+ ## Query & Search Guidance
548
+
549
+ ### You cannot query encrypted PII
550
+
551
+ Accept this. Do not design SQL queries that need plaintext PII.
552
+
553
+ ### Allowed patterns
554
+
555
+ - Store derived, non-sensitive fields outside encryption:
556
+ - `country_code`, `age_band`, `risk_flag`
557
+ - Store hashed lookup tokens for controlled dedupe:
558
+ - `email_hash = sha256(lowercase(email))` (still sensitive; treat as restricted)
559
+
560
+ If you add hash fields:
561
+
562
+ - keep them out of analytics
563
+ - restrict access like PII
564
+ - document their purpose
565
+
566
+ ---
567
+
568
+ ## Tests (Minimum)
569
+
570
+ ### Crypto correctness tests
571
+
572
+ - encrypt->decrypt roundtrip
573
+ - AAD mismatch fails decrypt
574
+ - swapped ciphertext across ids fails decrypt
575
+ - corrupted blob fails decrypt
576
+ - version byte unsupported fails decrypt
577
+
578
+ ### Storage tests
579
+
580
+ - PII never present in business table after writes
581
+ - logs redact/omit PII
582
+
583
+ ### Rotation tests
584
+
585
+ - decrypt old key id works
586
+ - re-wrap migration updates `kek_key_id` and keeps data readable
587
+
588
+ ---
589
+
590
+ ## PR Review Checklist (Enforcement)
591
+
592
+ - [ ] No PII fields added to business tables.
593
+ - [ ] No PII added to `draftData` JSON.
594
+ - [ ] Encryption uses AES-256-GCM with random IV.
595
+ - [ ] AAD includes record identity + schema version.
596
+ - [ ] `kek_key_id` and `schema_version` stored.
597
+ - [ ] No decrypted PII logged or sent to analytics.
598
+ - [ ] Rotation path documented for any key change.
599
+
600
+ ---
601
+
602
+ ## Anti-Patterns (MUST NOT)
603
+
604
+ - Storing plaintext PII in DB "temporarily"
605
+ - Using DB-native encryption only (without app-layer envelope encryption)
606
+ - Deterministic encryption for general PII
607
+ - Reusing IVs
608
+ - Omitting AAD
609
+ - Storing keys in DB or code
610
+ - Returning decrypted PII via broad "include" queries by default
611
+
612
+ ---
613
+
614
+ ## Notes on DB Permissions with Prisma
615
+
616
+ Prisma commonly uses one DB user. Table-level enforcement requires:
617
+
618
+ - separate DB roles + separate Prisma clients, or
619
+ - a service boundary where only a dedicated service can access `application_pii`
620
+
621
+ If you cannot enforce via DB roles today:
622
+
623
+ - enforce via strict repository modules + code review
624
+ - add automated lint checks for forbidden includes
625
+ - plan a path to split DB roles later
626
+
627
+ ---
628
+
629
+ End of Skill.
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: process-metrics
3
+ description: Define a portable metrics schema and templates for evaluating workflow performance. Use after /workflow:work, /workflow:review, or /workflow:compound to log outcomes and refine the system.
4
+ ---
5
+
6
+ # Process Metrics
7
+
8
+ ## Goal
9
+
10
+ Measure workflow performance so the system can improve over time.
11
+
12
+ Metrics should be:
13
+
14
+ - light-weight (takes < 3 minutes)
15
+ - comparable (same fields each time)
16
+ - actionable (each failure maps to an improvement)
17
+
18
+ ## Storage
19
+
20
+ - Daily: `docs/metrics/daily/YYYY-MM-DD.md`
21
+ - Weekly: `docs/metrics/weekly/YYYY-WW.md`
22
+ - Monthly: `docs/metrics/monthly/YYYY-MM.md`
23
+
24
+ ## Core Fields
25
+
26
+ - workflow: brainstorm|plan|work|triage|review|compound|test-browser|other
27
+ - context: plan/todo/pr/solution path or label
28
+ - outcome: success|partial|failed
29
+ - minutes: integer
30
+ - risk_tier: low|medium|high
31
+ - quality:
32
+ - tests: ran|skipped|failed
33
+ - lint: ran|skipped|failed
34
+ - blocker: one sentence
35
+ - rework: 0|1|2|3+ (how many times you had to redo work)
36
+
37
+ ## Notes
38
+
39
+ - Failures should include "what failed" + "why" + "what to change".
40
+ - Improvements should target a component: command/skill/agent/config.
41
+
42
+ ## Templates
43
+
44
+ - [daily-template.md](./assets/daily-template.md)
45
+ - [weekly-template.md](./assets/weekly-template.md)
46
+ - [monthly-template.md](./assets/monthly-template.md)
@@ -0,0 +1,37 @@
1
+ ---
2
+ date: YYYY-MM-DD
3
+ ---
4
+
5
+ # Daily Metrics
6
+
7
+ ## Session
8
+
9
+ - workflow: <brainstorm|plan|work|triage|review|compound|test-browser|other>
10
+ - context: <path or short label>
11
+ - outcome: <success|partial|failed>
12
+ - minutes: <int>
13
+ - risk_tier: <low|medium|high>
14
+
15
+ ## Quality Signals
16
+
17
+ - tests: <ran|skipped|failed>
18
+ - lint: <ran|skipped|failed>
19
+
20
+ ## Friction
21
+
22
+ - blocker: <one sentence>
23
+ - rework: <0|1|2|3+>
24
+
25
+ ## What Failed (if any)
26
+
27
+ - <bullet>
28
+
29
+ ## Assessment
30
+
31
+ - root cause: <one sentence>
32
+ - what to change next time: <1-3 bullets>
33
+ - component targets: <command|skill|agent|config|todo>
34
+
35
+ ## Actions
36
+
37
+ - <action> (target: command|skill|agent|config)