claude-code-pilot 3.2.0 → 3.3.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 (93) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +14 -9
  3. package/bin/install.js +124 -16
  4. package/manifest.json +18 -3
  5. package/package.json +3 -2
  6. package/src/agents/django-build-resolver.md +252 -0
  7. package/src/agents/django-reviewer.md +169 -0
  8. package/src/agents/fastapi-reviewer.md +79 -0
  9. package/src/agents/fsharp-reviewer.md +109 -0
  10. package/src/agents/swift-build-resolver.md +170 -0
  11. package/src/agents/swift-reviewer.md +116 -0
  12. package/src/commands/ccp/cost-report.md +107 -0
  13. package/src/commands/ccp/intel.md +3 -3
  14. package/src/commands/ccp/mvp-phase.md +45 -0
  15. package/src/commands/ccp/plan-prd.md +160 -0
  16. package/src/commands/ccp/pr-ecc.md +184 -0
  17. package/src/commands/ccp/security-scan.md +74 -0
  18. package/src/hooks/ccp-bash-hook-dispatcher.js +96 -0
  19. package/src/hooks/ccp-context-monitor.js +23 -0
  20. package/src/hooks/ccp-doc-file-warning.js +93 -0
  21. package/src/hooks/ccp-pre-bash-dispatcher.js +24 -0
  22. package/src/hooks/ccp-write-gateguard.js +868 -0
  23. package/src/lib/project-detect.js +0 -2
  24. package/src/lib/shell-substitution.js +499 -0
  25. package/src/pilot/references/execute-mvp-tdd.md +81 -0
  26. package/src/pilot/references/mvp-concepts.md +49 -0
  27. package/src/pilot/references/planner-graphify-auto-update.md +67 -0
  28. package/src/pilot/references/planner-human-verify-mode.md +57 -0
  29. package/src/pilot/references/planner-mvp-mode.md +53 -0
  30. package/src/pilot/references/skeleton-template.md +48 -0
  31. package/src/pilot/references/spidr-splitting.md +69 -0
  32. package/src/pilot/references/user-story-template.md +58 -0
  33. package/src/pilot/references/verify-mvp-mode.md +85 -0
  34. package/src/pilot/references/worktree-path-safety.md +89 -0
  35. package/src/pilot/workflows/help.md +5 -0
  36. package/src/pilot/workflows/mvp-phase.md +199 -0
  37. package/src/skills/agent-architecture-audit/SKILL.md +256 -0
  38. package/src/skills/agent-harness-design/SKILL.md +73 -0
  39. package/src/skills/angular-developer/SKILL.md +154 -0
  40. package/src/skills/angular-developer/references/angular-animations.md +160 -0
  41. package/src/skills/angular-developer/references/angular-aria.md +410 -0
  42. package/src/skills/angular-developer/references/cli.md +86 -0
  43. package/src/skills/angular-developer/references/component-harnesses.md +59 -0
  44. package/src/skills/angular-developer/references/component-styling.md +91 -0
  45. package/src/skills/angular-developer/references/components.md +117 -0
  46. package/src/skills/angular-developer/references/creating-services.md +97 -0
  47. package/src/skills/angular-developer/references/data-resolvers.md +69 -0
  48. package/src/skills/angular-developer/references/define-routes.md +67 -0
  49. package/src/skills/angular-developer/references/defining-providers.md +72 -0
  50. package/src/skills/angular-developer/references/di-fundamentals.md +120 -0
  51. package/src/skills/angular-developer/references/e2e-testing.md +56 -0
  52. package/src/skills/angular-developer/references/effects.md +83 -0
  53. package/src/skills/angular-developer/references/hierarchical-injectors.md +43 -0
  54. package/src/skills/angular-developer/references/host-elements.md +80 -0
  55. package/src/skills/angular-developer/references/injection-context.md +63 -0
  56. package/src/skills/angular-developer/references/inputs.md +101 -0
  57. package/src/skills/angular-developer/references/linked-signal.md +59 -0
  58. package/src/skills/angular-developer/references/loading-strategies.md +61 -0
  59. package/src/skills/angular-developer/references/mcp.md +108 -0
  60. package/src/skills/angular-developer/references/navigate-to-routes.md +69 -0
  61. package/src/skills/angular-developer/references/outputs.md +86 -0
  62. package/src/skills/angular-developer/references/reactive-forms.md +122 -0
  63. package/src/skills/angular-developer/references/rendering-strategies.md +44 -0
  64. package/src/skills/angular-developer/references/resource.md +77 -0
  65. package/src/skills/angular-developer/references/route-animations.md +56 -0
  66. package/src/skills/angular-developer/references/route-guards.md +52 -0
  67. package/src/skills/angular-developer/references/router-lifecycle.md +45 -0
  68. package/src/skills/angular-developer/references/router-testing.md +87 -0
  69. package/src/skills/angular-developer/references/show-routes-with-outlets.md +68 -0
  70. package/src/skills/angular-developer/references/signal-forms.md +795 -0
  71. package/src/skills/angular-developer/references/signals-overview.md +94 -0
  72. package/src/skills/angular-developer/references/tailwind-css.md +69 -0
  73. package/src/skills/angular-developer/references/template-driven-forms.md +114 -0
  74. package/src/skills/angular-developer/references/testing-fundamentals.md +65 -0
  75. package/src/skills/error-handling/SKILL.md +376 -0
  76. package/src/skills/fastapi-patterns/SKILL.md +327 -0
  77. package/src/skills/flox-environments/SKILL.md +496 -0
  78. package/src/skills/fsharp-testing/SKILL.md +280 -0
  79. package/src/skills/ios-icon-gen/SKILL.md +157 -0
  80. package/src/skills/ios-icon-gen/scripts/generate_icons.swift +258 -0
  81. package/src/skills/ios-icon-gen/scripts/iconify_gen.sh +235 -0
  82. package/src/skills/make-interfaces-feel-better/SKILL.md +151 -0
  83. package/src/skills/mysql-patterns/SKILL.md +412 -0
  84. package/src/skills/plan-orchestrate/SKILL.md +220 -0
  85. package/src/skills/prisma-patterns/SKILL.md +371 -0
  86. package/src/skills/production-audit/SKILL.md +206 -0
  87. package/src/skills/security-scan/references/agentshield-policy-exception/candidate-playbook.md +49 -0
  88. package/src/skills/security-scan/references/agentshield-policy-exception/report.json +35 -0
  89. package/src/skills/security-scan/references/agentshield-policy-exception/scenario.json +62 -0
  90. package/src/skills/security-scan/references/agentshield-policy-exception/trace.json +45 -0
  91. package/src/skills/security-scan/references/agentshield-policy-exception/verifier-result.json +35 -0
  92. package/src/skills/vite-patterns/SKILL.md +449 -0
  93. package/src/skills/windows-desktop-e2e/SKILL.md +887 -0
@@ -0,0 +1,371 @@
1
+ ---
2
+ name: prisma-patterns
3
+ description: Prisma ORM patterns for TypeScript backends — schema design, query optimization, transactions, pagination, and critical traps like updateMany returning count not records, $transaction timeouts, migrate dev resetting the DB, @updatedAt skipped on bulk writes, and serverless connection exhaustion.
4
+ origin: ECC
5
+ ---
6
+
7
+ # Prisma Patterns
8
+
9
+ Production patterns and non-obvious traps for Prisma ORM in TypeScript backends.
10
+ Tested against Prisma 5.x and 6.x. Some behaviors differ from Prisma 4.
11
+
12
+ Check the Prisma version before applying version-specific patterns:
13
+
14
+ ```bash
15
+ npx prisma --version
16
+ ```
17
+
18
+ Prisma 5 introduced `relationJoins`, which can load relations via JOIN rather than separate queries depending on query strategy and configuration. The `omit` field modifier and `prisma.$extends` Client Extensions API were also added. Note: `relationJoins` can cause row explosion on large 1:N relations or deep nested `include` — benchmark both approaches when relations may return many rows per parent.
19
+
20
+ ## When to Activate
21
+
22
+ - Designing or modifying Prisma schema models and relations
23
+ - Writing queries, transactions, or pagination logic
24
+ - Using `updateMany`, `deleteMany`, or any bulk operation
25
+ - Running or planning database migrations
26
+ - Deploying to serverless environments (Vercel, Lambda, Cloudflare Workers)
27
+ - Implementing soft delete or multi-tenant row filtering
28
+
29
+ ## Core Concepts
30
+
31
+ ### ID Strategy
32
+
33
+ | Strategy | Use When | Avoid When |
34
+ |---|---|---|
35
+ | `@default(cuid())` | Default choice — URL-safe, sortable, no collisions | Sequential IDs needed for external systems |
36
+ | `@default(uuid())` | Interoperability with non-Prisma systems required | High-write tables (random UUIDs fragment B-tree indexes) |
37
+ | `@default(autoincrement())` | Internal join tables, audit logs | Public-facing IDs (exposes record count) |
38
+
39
+ ### Schema Defaults
40
+
41
+ ```prisma
42
+ model User {
43
+ id String @id @default(cuid())
44
+ email String @unique // @unique already creates an index — no @@index needed
45
+ name String
46
+ role Role @default(USER)
47
+ posts Post[]
48
+ createdAt DateTime @default(now())
49
+ updatedAt DateTime @updatedAt
50
+ deletedAt DateTime?
51
+
52
+ @@index([createdAt])
53
+ @@index([deletedAt, createdAt]) // composite for soft-delete + sort queries
54
+ }
55
+ ```
56
+
57
+ - Add `@@index` on every foreign key and column used in `WHERE` or `ORDER BY`.
58
+ - Declare `deletedAt DateTime?` upfront when soft delete is a foreseeable requirement — adding it later requires a migration on a live table.
59
+ - `updatedAt @updatedAt` is set automatically by Prisma on `update` and `upsert` only (see Anti-Patterns for bulk update trap).
60
+
61
+ ### `include` vs `select`
62
+
63
+ | | `include` | `select` |
64
+ |---|---|---|
65
+ | Returns | All scalar fields + specified relations | Only specified fields |
66
+ | Use when | You need most fields plus a relation | Hot paths, large tables, avoiding over-fetch |
67
+ | Performance | May over-fetch on wide tables | Minimal payload, faster on large datasets |
68
+ | Prisma 5 note | Uses JOIN by default (`relationJoins`) | Same |
69
+
70
+ ```ts
71
+ // include — all columns + relation
72
+ const user = await prisma.user.findUnique({
73
+ where: { id },
74
+ include: { posts: { select: { id: true, title: true } } },
75
+ });
76
+
77
+ // select — explicit allowlist
78
+ const user = await prisma.user.findUnique({
79
+ where: { id },
80
+ select: { id: true, email: true, name: true },
81
+ });
82
+ ```
83
+
84
+ Never return raw Prisma entities from API responses — map to response DTOs to control exposed fields:
85
+
86
+ ```ts
87
+ // BAD: leaks passwordHash, deletedAt, internal fields
88
+ return await prisma.user.findUniqueOrThrow({ where: { id } });
89
+
90
+ // GOOD: explicit DTO mapping
91
+ const user = await prisma.user.findUniqueOrThrow({ where: { id } });
92
+ return { id: user.id, name: user.name, email: user.email };
93
+ ```
94
+
95
+ ### Transaction Form Selection
96
+
97
+ | Situation | Use |
98
+ |---|---|
99
+ | Independent operations, no inter-dependency | Array form |
100
+ | Later step depends on earlier result | Interactive form |
101
+ | External calls (email, HTTP) involved | Outside transaction entirely |
102
+
103
+ ```ts
104
+ // Array form — batched in one round trip
105
+ const [user, post] = await prisma.$transaction([
106
+ prisma.user.update({ where: { id }, data: { name } }),
107
+ prisma.post.create({ data: { title, authorId: id } }),
108
+ ]);
109
+
110
+ // Interactive form — use tx client only, never the outer prisma client
111
+ const post = await prisma.$transaction(async (tx) => {
112
+ const user = await tx.user.findUniqueOrThrow({ where: { id } });
113
+ if (user.role !== 'ADMIN') throw new Error('Forbidden');
114
+ return tx.post.create({ data: { title, authorId: user.id } });
115
+ });
116
+ ```
117
+
118
+ ### PrismaClient Singleton
119
+
120
+ Each `PrismaClient` instance opens its own connection pool. Instantiate once.
121
+
122
+ ```ts
123
+ // lib/prisma.ts
124
+ import { PrismaClient } from '@prisma/client';
125
+
126
+ const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient };
127
+
128
+ export const prisma =
129
+ globalForPrisma.prisma ??
130
+ new PrismaClient({
131
+ log: process.env.NODE_ENV === 'development' ? ['query', 'error'] : ['error'],
132
+ });
133
+
134
+ if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
135
+ ```
136
+
137
+ The `globalThis` pattern prevents duplicate instances during hot reload (Next.js, nodemon, ts-node-dev).
138
+
139
+ ### N+1 Problem
140
+
141
+ Loading relations inside a loop issues one query per row.
142
+
143
+ ```ts
144
+ // BAD: N+1 — one extra query per user
145
+ const users = await prisma.user.findMany();
146
+ for (const user of users) {
147
+ const posts = await prisma.post.findMany({ where: { authorId: user.id } });
148
+ }
149
+
150
+ // GOOD: single query
151
+ const users = await prisma.user.findMany({ include: { posts: true } });
152
+ ```
153
+
154
+ With Prisma 5+ `relationJoins`, the `include` form uses a single JOIN. On large 1:N sets this may increase result set size — benchmark both approaches if the relation can return many rows per parent.
155
+
156
+ ## Code Examples
157
+
158
+ ### Cursor Pagination (preferred for feeds and large datasets)
159
+
160
+ ```ts
161
+ async function getPosts(cursor?: string, limit = 20) {
162
+ const items = await prisma.post.findMany({
163
+ where: { published: true },
164
+ orderBy: [
165
+ { createdAt: 'desc' },
166
+ { id: 'desc' }, // secondary sort prevents unstable pagination on duplicate timestamps
167
+ ],
168
+ take: limit + 1,
169
+ ...(cursor && { cursor: { id: cursor }, skip: 1 }),
170
+ });
171
+
172
+ const hasNextPage = items.length > limit;
173
+ if (hasNextPage) items.pop();
174
+
175
+ return { items, nextCursor: hasNextPage ? items[items.length - 1].id : null };
176
+ }
177
+ ```
178
+
179
+ Fetch `limit + 1` and pop — canonical way to detect `hasNextPage` without an extra count query. Always include a unique field (e.g. `id`) as a secondary `orderBy` to prevent unstable pagination when multiple rows share the same timestamp. Use offset pagination only when users need to jump to arbitrary pages (admin tables).
180
+
181
+ ### Soft Delete
182
+
183
+ ```ts
184
+ // Always filter explicitly — do not rely on middleware (hides behavior, hard to debug)
185
+ const activeUsers = await prisma.user.findMany({ where: { deletedAt: null } });
186
+
187
+ await prisma.user.update({ where: { id }, data: { deletedAt: new Date() } });
188
+ await prisma.user.update({ where: { id }, data: { deletedAt: null } }); // restore
189
+ ```
190
+
191
+ ### Error Handling
192
+
193
+ ```ts
194
+ import { Prisma } from '@prisma/client';
195
+
196
+ try {
197
+ await prisma.user.create({ data: { email } });
198
+ } catch (e) {
199
+ if (e instanceof Prisma.PrismaClientKnownRequestError) {
200
+ if (e.code === 'P2002') throw new ConflictError('Email already exists');
201
+ if (e.code === 'P2025') throw new NotFoundError('Record not found');
202
+ if (e.code === 'P2003') throw new BadRequestError('Referenced record does not exist');
203
+ }
204
+ throw e;
205
+ }
206
+ ```
207
+
208
+ Common codes: `P2002` unique violation · `P2025` not found · `P2003` foreign key violation.
209
+
210
+ Catch at the service boundary and translate to domain errors. Never expose raw Prisma messages to API consumers.
211
+
212
+ ### Connection Pool — Serverless
213
+
214
+ Embed connection params directly in `DATABASE_URL` — string concatenation breaks if the URL already has query parameters (e.g. `?schema=public`):
215
+
216
+ ```bash
217
+ # .env — preferred: embed params in the URL
218
+ DATABASE_URL="postgresql://user:pass@host/db?connection_limit=1&pool_timeout=20"
219
+
220
+ # With an external pooler (PgBouncer, Supabase pooler)
221
+ DATABASE_URL="postgresql://user:pass@host/db?pgbouncer=true&connection_limit=1"
222
+ ```
223
+
224
+ ```ts
225
+ // Vercel, AWS Lambda, and similar serverless runtimes: cap pool to 1 per instance
226
+ // connection_limit and pool_timeout are controlled via DATABASE_URL
227
+ const prisma = new PrismaClient();
228
+ ```
229
+
230
+ ## Anti-Patterns
231
+
232
+ ### `updateMany` returns a count, not records
233
+
234
+ ```ts
235
+ // BAD: result is { count: 2 } — users[0] is undefined
236
+ const users = await prisma.user.updateMany({ where: { role: 'GUEST' }, data: { role: 'USER' } });
237
+
238
+ // GOOD: capture IDs first, then update, then fetch only the affected rows
239
+ const targets = await prisma.user.findMany({
240
+ where: { role: 'GUEST' },
241
+ select: { id: true },
242
+ });
243
+ const ids = targets.map((u) => u.id);
244
+ await prisma.user.updateMany({ where: { id: { in: ids } }, data: { role: 'USER' } });
245
+ const updated = await prisma.user.findMany({ where: { id: { in: ids } } });
246
+ ```
247
+
248
+ Same applies to `deleteMany` — returns `{ count: n }`, never the deleted rows.
249
+
250
+ ### `$transaction` interactive form times out after 5 seconds
251
+
252
+ ```ts
253
+ // BAD: external call inside transaction exceeds 5s default → "Transaction already closed"
254
+ await prisma.$transaction(async (tx) => {
255
+ const user = await tx.user.findUniqueOrThrow({ where: { id } });
256
+ await sendWelcomeEmail(user.email); // external call
257
+ await tx.user.update({ where: { id }, data: { emailSent: true } });
258
+ });
259
+
260
+ // GOOD: external calls outside the transaction
261
+ const user = await prisma.user.findUniqueOrThrow({ where: { id } });
262
+ await sendWelcomeEmail(user.email);
263
+ await prisma.user.update({ where: { id }, data: { emailSent: true } });
264
+
265
+ // Only raise timeout when bulk processing genuinely needs it
266
+ await prisma.$transaction(async (tx) => { ... }, { timeout: 30_000 });
267
+ ```
268
+
269
+ ### `migrate dev` can reset the database
270
+
271
+ `migrate dev` detects schema drift and may prompt to reset the DB, dropping all data.
272
+
273
+ ```bash
274
+ # NEVER on shared dev, staging, or production
275
+ npx prisma migrate dev --name add_column
276
+
277
+ # Safe everywhere except local solo dev
278
+ npx prisma migrate deploy
279
+
280
+ # Check drift without applying
281
+ npx prisma migrate diff \
282
+ --from-migrations ./prisma/migrations \
283
+ --to-schema-datamodel ./prisma/schema.prisma \
284
+ --shadow-database-url "$SHADOW_DATABASE_URL"
285
+ ```
286
+
287
+ ### Manually editing a migration file breaks future deploys
288
+
289
+ Prisma checksums every migration file. Editing after apply causes `P3006 checksum mismatch` on every environment where the original already ran. Create a new migration instead.
290
+
291
+ ### Breaking schema changes require multi-step migration
292
+
293
+ Adding `NOT NULL` to an existing column or renaming a column in one migration will lock the table or drop data. Use expand-and-contract:
294
+
295
+ ```bash
296
+ # Step 1: create migration locally, then deploy
297
+ npx prisma migrate dev --name add_new_column # local only
298
+ npx prisma migrate deploy # staging / production
299
+ ```
300
+
301
+ ```ts
302
+ // Step 2: backfill data (run in a script or migration job, not in the shell)
303
+ await prisma.user.updateMany({ data: { newColumn: derivedValue } });
304
+ ```
305
+
306
+ ```bash
307
+ # Step 3: create the NOT NULL constraint migration locally, then deploy
308
+ npx prisma migrate dev --name make_new_column_required # local only
309
+ npx prisma migrate deploy # staging / production
310
+ ```
311
+
312
+ ### `@updatedAt` does not fire on `updateMany`
313
+
314
+ `@updatedAt` is set automatically only on `update` and `upsert`. Bulk writes leave it stale.
315
+
316
+ ```ts
317
+ // BAD: updatedAt stays at its old value
318
+ await prisma.post.updateMany({ where: { authorId }, data: { published: true } });
319
+
320
+ // GOOD
321
+ await prisma.post.updateMany({
322
+ where: { authorId },
323
+ data: { published: true, updatedAt: new Date() },
324
+ });
325
+ ```
326
+
327
+ ### Soft delete + `findUniqueOrThrow` leaks deleted records
328
+
329
+ `findUniqueOrThrow` throws `P2025` only when the row does not exist in the DB. Soft-deleted rows still exist and are returned without error.
330
+
331
+ `findUniqueOrThrow` requires a unique constraint field in `where` — adding `deletedAt: null` alongside `id` breaks the type because `{ id, deletedAt }` is not a compound unique constraint. Use `findFirstOrThrow` instead.
332
+
333
+ ```ts
334
+ // BAD: returns soft-deleted user
335
+ const user = await prisma.user.findUniqueOrThrow({ where: { id } });
336
+
337
+ // BAD: Prisma type error — { id, deletedAt } is not a unique constraint
338
+ const user = await prisma.user.findUniqueOrThrow({ where: { id, deletedAt: null } });
339
+
340
+ // GOOD: findFirstOrThrow supports arbitrary where conditions
341
+ const user = await prisma.user.findFirstOrThrow({ where: { id, deletedAt: null } });
342
+ ```
343
+
344
+ ### `deleteMany` without `where` deletes every row
345
+
346
+ ```ts
347
+ // BAD: silently wipes the table
348
+ await prisma.post.deleteMany();
349
+
350
+ // GOOD
351
+ await prisma.post.deleteMany({ where: { authorId: userId } });
352
+ ```
353
+
354
+ ## Best Practices
355
+
356
+ | Rule | Reason |
357
+ |---|---|
358
+ | `migrate deploy` in CI/CD, `migrate dev` only locally | `migrate dev` can reset the DB on drift |
359
+ | Map entities to response DTOs | Prevents leaking internal fields |
360
+ | Catch `PrismaClientKnownRequestError` at service boundary | Translate to domain errors |
361
+ | Prefer `*OrThrow` methods over manual null checks | Throws P2025 automatically; use `findFirstOrThrow` when filtering non-unique fields |
362
+ | `connection_limit=1` + external pooler in serverless | Prevents connection exhaustion |
363
+ | Always provide `where` on `deleteMany` | Prevents accidental table wipe |
364
+ | Set `updatedAt: new Date()` manually in `updateMany` | `@updatedAt` skips bulk writes |
365
+
366
+ ## Related Skills
367
+
368
+ - `nestjs-patterns` — NestJS service layer that integrates Prisma
369
+ - `postgres-patterns` — PostgreSQL-level indexing and connection tuning
370
+ - `database-migrations` — multi-step migration planning for production
371
+ - `backend-patterns` — general API and service layer design
@@ -0,0 +1,206 @@
1
+ ---
2
+ name: production-audit
3
+ description: Local-evidence production readiness audit for shipped apps, pre-launch reviews, post-merge checks, and "what breaks in prod?" questions without sending repo data to an external audit service.
4
+ origin: community
5
+ ---
6
+
7
+ # Production Audit
8
+
9
+ Use this skill when the user asks whether an application is ready to ship, what
10
+ could break in production, or what must be fixed before a launch. This is a
11
+ maintainer-safe rewrite of the stale community production-audit idea: it keeps
12
+ the useful production-readiness lens and removes unpinned external execution and
13
+ third-party data sharing.
14
+
15
+ ## When to Use
16
+
17
+ - The user asks "is this production-ready", "what would break in prod", "what
18
+ did we miss", "audit this repo", or "ready to ship?"
19
+ - A feature was merged and needs a pre-deploy or post-merge risk pass.
20
+ - A public launch, demo, customer rollout, or investor walkthrough is close.
21
+ - CI is green but the user wants production risk, not only test status.
22
+ - A deployed URL, release branch, PR, or current checkout is available for
23
+ evidence gathering.
24
+
25
+ ## When Not to Use
26
+
27
+ - During active implementation when the right lens is line-level secure coding;
28
+ use `security-review` first.
29
+ - For pure libraries, templates, docs-only repos, or scaffolds unless the user
30
+ wants packaging/release readiness rather than application readiness.
31
+ - When the user asks for a formal compliance audit. This skill is engineering
32
+ triage, not legal, financial, medical, or regulatory certification.
33
+ - When the only available evidence is a product idea with no repo, deployment,
34
+ CI, or runtime surface.
35
+
36
+ ## How It Works
37
+
38
+ Build the audit from local and user-authorized evidence. Do not run unpinned
39
+ remote code, upload repository contents to third-party services, or call
40
+ external scanners unless the user explicitly approves that specific tool and
41
+ data flow.
42
+
43
+ Use this order:
44
+
45
+ 1. Establish the release surface.
46
+ 2. Read recent changes and current branch state.
47
+ 3. Inspect runtime, auth, data, payment, background-job, AI, and deployment
48
+ boundaries that actually exist in the repo.
49
+ 4. Check CI, tests, migrations, environment documentation, and rollback path.
50
+ 5. Produce a short ship/block recommendation with specific fixes.
51
+
52
+ ## Evidence Checklist
53
+
54
+ Start with cheap, local signals:
55
+
56
+ ```text
57
+ git status --short --branch
58
+ git log --oneline --decorate -20
59
+ git diff --stat origin/main...HEAD
60
+ ```
61
+
62
+ Then inspect the project-specific surface:
63
+
64
+ - Package scripts, CI workflows, release scripts, Docker files, and deployment
65
+ manifests.
66
+ - API routes, webhooks, auth middleware, background workers, cron jobs, and
67
+ database migrations.
68
+ - Environment variable documentation and startup checks.
69
+ - Observability hooks, error reporting, logs, health checks, and dashboards.
70
+ - Rollback, seed, migration, and backfill instructions.
71
+ - E2E coverage for the user paths that matter most.
72
+
73
+ If a deployed URL is in scope, use browser or HTTP checks only against that URL
74
+ and avoid credentialed actions unless the user supplies a safe test account.
75
+
76
+ ## Risk Lenses
77
+
78
+ ### Security And Auth
79
+
80
+ - Are public routes, API routes, and admin routes clearly separated?
81
+ - Are auth and authorization enforced server-side?
82
+ - Are secrets kept out of client bundles, logs, example output, and checked-in
83
+ files?
84
+ - Are rate limits, CSRF protections, CORS policy, and upload validation present
85
+ where the app needs them?
86
+ - Does the AI or agent surface defend against prompt injection, tool abuse, and
87
+ untrusted content crossing into privileged actions?
88
+
89
+ ### Data Integrity
90
+
91
+ - Do migrations run forward cleanly and have a rollback or recovery plan?
92
+ - Are destructive migrations, backfills, and data imports staged safely?
93
+ - Do database policies, grants, and service-role boundaries match the app's
94
+ tenancy model?
95
+ - Are retries idempotent for writes, jobs, and webhook handlers?
96
+
97
+ ### Payments And Webhooks
98
+
99
+ - Are webhook signatures verified before parsing trusted payload fields?
100
+ - Is each payment, subscription, or fulfillment webhook idempotent?
101
+ - Are replay, duplicate delivery, and out-of-order delivery handled?
102
+ - Are test-mode and live-mode credentials separated?
103
+
104
+ ### Operations
105
+
106
+ - Can the app start from a clean checkout using documented commands?
107
+ - Are required environment variables named, validated, and fail-fast?
108
+ - Is there a health check that proves dependencies are reachable?
109
+ - Are deploy, rollback, and incident-owner paths documented?
110
+ - Are logs useful without leaking secrets or personal data?
111
+
112
+ ### User Experience
113
+
114
+ - Are the launch-critical paths covered on desktop and mobile?
115
+ - Are forms usable on mobile without input zoom, layout overlap, or blocked
116
+ submission states?
117
+ - Do loading, empty, error, and permission-denied states tell the user what
118
+ happened?
119
+ - Is there a support or recovery path when a critical operation fails?
120
+
121
+ ## Scoring
122
+
123
+ Use scores to force prioritization, not to imply mathematical certainty.
124
+
125
+ | Band | Score | Meaning |
126
+ | --- | --- | --- |
127
+ | Blocked | 0-49 | Do not ship until the top risks are fixed |
128
+ | Risky | 50-69 | Ship only behind a small rollout or internal beta |
129
+ | Launchable With Caveats | 70-84 | Ship if owners accept the listed risks |
130
+ | Strong | 85-100 | No obvious launch blockers from available evidence |
131
+
132
+ Cap the score at `69` if any of these are true:
133
+
134
+ - Authentication or authorization is missing on sensitive data.
135
+ - Payment or fulfillment webhooks are not idempotent.
136
+ - Required migrations cannot be run safely.
137
+ - Secrets are exposed in client bundles, logs, or committed files.
138
+ - There is no rollback path for a high-impact release.
139
+
140
+ Cap the score at `84` if CI is not green or the launch-critical path was not
141
+ tested end to end.
142
+
143
+ ## Output Format
144
+
145
+ Lead with one sentence:
146
+
147
+ ```text
148
+ Production audit: 76/100, launchable with caveats, with webhook idempotency and rollback docs as the two risks to fix before public launch.
149
+ ```
150
+
151
+ Then list:
152
+
153
+ - `Blockers`: must-fix items before deploy.
154
+ - `High-value fixes`: next fixes if the user wants to improve the score.
155
+ - `Evidence checked`: files, commands, CI, deployed URL, or PRs inspected.
156
+ - `Evidence missing`: what would change confidence if provided.
157
+ - `Next action`: one concrete fix or verification step.
158
+
159
+ Keep strengths short. The user asked for readiness, so the useful answer is the
160
+ remaining risk and the next action.
161
+
162
+ ## Example
163
+
164
+ User:
165
+
166
+ ```text
167
+ is this ready to ship?
168
+ ```
169
+
170
+ Response:
171
+
172
+ ```text
173
+ Production audit: 68/100, risky, because Stripe webhooks are verified but not idempotent and there is no rollback note for the pending migration.
174
+
175
+ Blockers:
176
+ - Add idempotency for `checkout.session.completed` before fulfilling orders.
177
+ - Write and test the rollback path for `20260511_add_billing_state.sql`.
178
+
179
+ High-value fixes:
180
+ - Add a health check that verifies database and payment-provider reachability.
181
+ - Add one E2E path for upgrade, webhook fulfillment, and billing-page refresh.
182
+
183
+ Evidence checked:
184
+ - `api/stripe/webhook.ts`
185
+ - `db/migrations/20260511_add_billing_state.sql`
186
+ - GitHub Actions run for the release branch
187
+
188
+ Next action: Want me to patch webhook idempotency first?
189
+ ```
190
+
191
+ ## Anti-Patterns
192
+
193
+ - Running `npx <package>@latest` or a remote scanner as the default audit path.
194
+ - Uploading source, secrets, customer data, or private topology to an external
195
+ audit service without explicit approval.
196
+ - Producing a score without naming the evidence checked.
197
+ - Treating green CI as production readiness.
198
+ - Ending with a generic "let me know what you want to do."
199
+
200
+ ## See Also
201
+
202
+ - Skill: `security-review`
203
+ - Skill: `deployment-patterns`
204
+ - Skill: `e2e-testing`
205
+ - Skill: `tdd-workflow`
206
+ - Skill: `verification-loop`
@@ -0,0 +1,49 @@
1
+ # AgentShield Policy Exception Playbook
2
+
3
+ Candidate id: `sarif-backed-timeboxed-exception-review`
4
+
5
+ Use this playbook when AgentShield organization-policy output produces a
6
+ finding that may need remediation, a time-boxed exception, or explicit
7
+ enforcement.
8
+
9
+ ## Accepted Path
10
+
11
+ 1. Identify the AgentShield finding id, category, severity, affected file or
12
+ MCP/hook surface, and policy pack or organization baseline.
13
+ 2. Retrieve scanner evidence before judgment:
14
+ - SARIF/code-scanning result, especially `agentshield-policy/*`
15
+ - JSON/HTML report evidence
16
+ - terminal or GitHub Action job-summary counts
17
+ 3. Record lifecycle fields for any exception request: owner, ticket, scope,
18
+ expiry, rationale, and whether it is active, expiring soon, or expired.
19
+ 4. Keep expired exceptions rejected or enforced until new evidence exists.
20
+ 5. Decide whether immediate remediation is possible. If not, only promote a
21
+ narrow time-boxed exception tied to the named owner, ticket, scope, and
22
+ expiry.
23
+ 6. Keep AgentShield code, policy packs, enforcement settings, release state,
24
+ and live security posture out of the read-only evaluator run.
25
+
26
+ ## Rejected Path
27
+
28
+ Do not blanket suppress a policy category, policy pack, or organization gate
29
+ because a finding is inconvenient.
30
+
31
+ Do not downgrade critical/high findings without SARIF or report evidence and a
32
+ current owner, ticket, scope, and expiry.
33
+
34
+ Do not treat expired exceptions as active. Expired means the policy gate should
35
+ remain enforced until a maintainer creates a fresh, bounded exception or fixes
36
+ the underlying issue.
37
+
38
+ ## Minimum Validation
39
+
40
+ - `npx ecc-agentshield scan --format json`
41
+ - AgentShield SARIF/code-scanning artifact or report evidence
42
+ - `npx ecc-agentshield scan --format html` when executive review evidence is
43
+ needed
44
+ - Current exception lifecycle fields: owner, ticket, scope, expiry, status
45
+ - `node tests/docs/evaluator-rag-prototype.test.js`
46
+ - `git diff --check`
47
+
48
+ Record the scanner evidence, lifecycle state, policy-pack source, and
49
+ remediation-versus-exception decision in the maintainer PR body or handoff.
@@ -0,0 +1,35 @@
1
+ {
2
+ "schema_version": "ecc.evaluator-rag.report.v1",
3
+ "scenario_id": "agentshield-policy-exception",
4
+ "run_id": "2026-05-12-agentshield-policy-exception-prototype",
5
+ "result": "prototype_passed",
6
+ "read_only": true,
7
+ "scores": {
8
+ "sarif_report_evidence": 0.95,
9
+ "exception_lifecycle": 0.93,
10
+ "ownership_specificity": 0.9,
11
+ "remediation_decision": 0.88,
12
+ "blanket_suppression_safety": 1
13
+ },
14
+ "findings": [
15
+ {
16
+ "id": "sarif-report-match-required",
17
+ "severity": "warning",
18
+ "summary": "AgentShield policy exceptions must name SARIF or report evidence before a remediation or exception playbook can be promoted."
19
+ },
20
+ {
21
+ "id": "expired-exception-enforcement",
22
+ "severity": "warning",
23
+ "summary": "Expired exceptions must remain rejected or enforced; the evaluator cannot treat stale approvals as active evidence."
24
+ },
25
+ {
26
+ "id": "bounded-owner-fields",
27
+ "severity": "info",
28
+ "summary": "Accepted exceptions preserve owner, ticket, scope, expiry, policy-pack source, and affected surface fields."
29
+ }
30
+ ],
31
+ "recommended_next_action": {
32
+ "candidate_id": "sarif-backed-timeboxed-exception-review",
33
+ "action": "Use the promoted playbook for future AgentShield policy exception requests before changing gates, suppressing categories, or accepting security risk."
34
+ }
35
+ }