@xhulz/nina 0.64.3 → 0.65.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.
- package/package.json +1 -1
- package/releases/0.65.0/core/GAPS.md +125 -0
- package/releases/0.65.0/core/answered.json +80 -0
- package/releases/0.65.0/core/defaults/tree/.claude/agents/architect.md +1 -0
- package/releases/0.65.0/core/defaults/tree/.claude/agents/dba.md +3 -0
- package/releases/0.65.0/core/defaults/tree/.claude/agents/devops.md +1 -0
- package/releases/0.65.0/core/defaults/tree/.claude/agents/implementer.md +1 -0
- package/releases/0.65.0/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.65.0/core/defaults/tree/.claude/agents/qa.md +7 -0
- package/releases/0.65.0/core/defaults/tree/.claude/agents/reviewer.md +3 -0
- package/releases/0.65.0/core/defaults/tree/.claude/agents/secops.md +3 -0
- package/releases/0.65.0/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
- package/releases/0.65.0/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
- package/releases/0.65.0/core/tree/.claude/agents/architect.md +133 -0
- package/releases/0.65.0/core/tree/.claude/agents/dba.md +119 -0
- package/releases/0.65.0/core/tree/.claude/agents/design-reviewer.md +76 -0
- package/releases/0.65.0/core/tree/.claude/agents/devops.md +118 -0
- package/releases/0.65.0/core/tree/.claude/agents/implementer.md +172 -0
- package/releases/0.65.0/core/tree/.claude/agents/integration-tester.md +170 -0
- package/releases/0.65.0/core/tree/.claude/agents/planner.md +132 -0
- package/releases/0.65.0/core/tree/.claude/agents/qa.md +150 -0
- package/releases/0.65.0/core/tree/.claude/agents/reviewer.md +187 -0
- package/releases/0.65.0/core/tree/.claude/agents/secops.md +120 -0
- package/releases/0.65.0/core/tree/.claude/agents/solidity-auditor.md +126 -0
- package/releases/0.65.0/core/tree/.claude/agents/solidity-dev.md +123 -0
- package/releases/0.65.0/core/tree/.claude/agents-overview.md +65 -0
- package/releases/0.65.0/core/tree/.claude/frontend.md +10 -0
- package/releases/0.65.0/core/tree/.claude/graph.md +79 -0
- package/releases/0.65.0/core/tree/.claude/patterns.md +252 -0
- package/releases/0.65.0/core/tree/.claude/pills/README.md +150 -0
- package/releases/0.65.0/core/tree/.claude/pipeline.md +101 -0
- package/releases/0.65.0/core/tree/.claude/retrieval.md +103 -0
- package/releases/0.65.0/core/tree/.claude/router.md +211 -0
- package/releases/0.65.0/core/tree/.claude/templates/integration.md +48 -0
- package/releases/0.65.0/core/tree/CLAUDE.md +166 -0
- package/releases/0.65.0/core/tree/scripts/cost-watch.mjs +25 -0
- package/releases/0.65.0/core/tree/scripts/edit-guard.mjs +24 -0
- package/releases/0.65.0/core/tree/scripts/harness-check.mjs +80 -0
- package/releases/0.65.0/core/tree/scripts/loop-gate.mjs +19 -0
- package/releases/0.65.0/core/vocabulary.json +14 -0
- package/releases/0.65.0/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
- package/releases/0.65.0/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
- package/releases/0.65.0/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
- package/releases/0.65.0/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
- package/releases/0.65.0/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
- package/releases/0.65.0/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
- package/releases/0.65.0/surfaces/blockchain/tree/.claude/graph.md +11 -0
- package/releases/0.65.0/surfaces/blockchain/tree/.claude/router.md +6 -0
- package/releases/0.65.0/surfaces/blockchain/tree/CLAUDE.md +12 -0
- package/releases/0.65.0/surfaces/d1/tree/.claude/agents/dba.md +15 -0
- package/releases/0.65.0/surfaces/d1/tree/.claude/agents/devops.md +2 -0
- package/releases/0.65.0/surfaces/d1/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.65.0/surfaces/d1/tree/.claude/patterns.md +8 -0
- package/releases/0.65.0/surfaces/d1/tree/CLAUDE.md +2 -0
- package/releases/0.65.0/surfaces/db/tree/.claude/agents/architect.md +5 -0
- package/releases/0.65.0/surfaces/db/tree/.claude/agents/dba.md +54 -0
- package/releases/0.65.0/surfaces/db/tree/.claude/agents/devops.md +5 -0
- package/releases/0.65.0/surfaces/db/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.65.0/surfaces/db/tree/.claude/agents/integration-tester.md +10 -0
- package/releases/0.65.0/surfaces/db/tree/.claude/agents/planner.md +2 -0
- package/releases/0.65.0/surfaces/db/tree/.claude/agents/reviewer.md +22 -0
- package/releases/0.65.0/surfaces/db/tree/.claude/agents/secops.md +2 -0
- package/releases/0.65.0/surfaces/db/tree/.claude/agents-overview.md +5 -0
- package/releases/0.65.0/surfaces/db/tree/.claude/graph.md +8 -0
- package/releases/0.65.0/surfaces/db/tree/.claude/patterns.md +22 -0
- package/releases/0.65.0/surfaces/db/tree/.claude/pipeline.md +13 -0
- package/releases/0.65.0/surfaces/db/tree/.claude/retrieval.md +5 -0
- package/releases/0.65.0/surfaces/db/tree/.claude/router.md +4 -0
- package/releases/0.65.0/surfaces/db/tree/CLAUDE.md +17 -0
- package/releases/0.65.0/surfaces/drizzle/tree/.claude/agents/dba.md +9 -0
- package/releases/0.65.0/surfaces/drizzle/tree/.claude/agents/devops.md +5 -0
- package/releases/0.65.0/surfaces/drizzle/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.65.0/surfaces/drizzle/tree/.claude/patterns.md +7 -0
- package/releases/0.65.0/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
- package/releases/0.65.0/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
- package/releases/0.65.0/surfaces/edge-cf/tree/.claude/agents/devops.md +29 -0
- package/releases/0.65.0/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
- package/releases/0.65.0/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
- package/releases/0.65.0/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
- package/releases/0.65.0/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.65.0/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
- package/releases/0.65.0/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
- package/releases/0.65.0/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
- package/releases/0.65.0/surfaces/edge-cf/tree/CLAUDE.md +27 -0
- package/releases/0.65.0/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
- package/releases/0.65.0/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
- package/releases/0.65.0/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
- package/releases/0.65.0/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
- package/releases/0.65.0/surfaces/frontend/tree/.claude/agents/reviewer.md +56 -0
- package/releases/0.65.0/surfaces/frontend/tree/.claude/patterns.md +8 -0
- package/releases/0.65.0/surfaces/frontend/tree/.claude/retrieval.md +2 -0
- package/releases/0.65.0/surfaces/frontend/tree/.claude/router.md +14 -0
- package/releases/0.65.0/surfaces/frontend/tree/CLAUDE.md +5 -0
- package/releases/0.65.0/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
- package/releases/0.65.0/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
- package/releases/0.65.0/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
- package/releases/0.65.0/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
- package/releases/0.65.0/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
- package/releases/0.65.0/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
- package/releases/0.65.0/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
- package/releases/0.65.0/surfaces/integrations/tree/.claude/graph.md +10 -0
- package/releases/0.65.0/surfaces/integrations/tree/.claude/patterns.md +38 -0
- package/releases/0.65.0/surfaces/integrations/tree/.claude/pipeline.md +16 -0
- package/releases/0.65.0/surfaces/integrations/tree/.claude/retrieval.md +16 -0
- package/releases/0.65.0/surfaces/integrations/tree/.claude/router.md +6 -0
- package/releases/0.65.0/surfaces/integrations/tree/CLAUDE.md +17 -0
- package/releases/0.65.0/surfaces/money/tree/.claude/agents/architect.md +12 -0
- package/releases/0.65.0/surfaces/money/tree/.claude/agents/dba.md +14 -0
- package/releases/0.65.0/surfaces/money/tree/.claude/agents/implementer.md +11 -0
- package/releases/0.65.0/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.65.0/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
- package/releases/0.65.0/surfaces/money/tree/.claude/agents/secops.md +5 -0
- package/releases/0.65.0/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
- package/releases/0.65.0/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
- package/releases/0.65.0/surfaces/money/tree/.claude/graph.md +2 -0
- package/releases/0.65.0/surfaces/money/tree/.claude/patterns.md +49 -0
- package/releases/0.65.0/surfaces/money/tree/.claude/pipeline.md +2 -0
- package/releases/0.65.0/surfaces/money/tree/.claude/retrieval.md +2 -0
- package/releases/0.65.0/surfaces/money/tree/CLAUDE.md +11 -0
- package/releases/0.65.0/surfaces/pii/tree/.claude/agents/dba.md +2 -0
- package/releases/0.65.0/surfaces/pii/tree/.claude/agents/devops.md +8 -0
- package/releases/0.65.0/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.65.0/surfaces/pii/tree/.claude/agents/secops.md +2 -0
- package/releases/0.65.0/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
- package/releases/0.65.0/surfaces/pii/tree/CLAUDE.md +5 -0
- package/releases/0.65.0/surfaces/prisma/tree/.claude/agents/architect.md +2 -0
- package/releases/0.65.0/surfaces/prisma/tree/.claude/agents/dba.md +42 -0
- package/releases/0.65.0/surfaces/prisma/tree/.claude/agents/devops.md +11 -0
- package/releases/0.65.0/surfaces/prisma/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.65.0/surfaces/prisma/tree/.claude/agents/integration-tester.md +2 -0
- package/releases/0.65.0/surfaces/prisma/tree/.claude/agents/reviewer.md +2 -0
- package/releases/0.65.0/surfaces/prisma/tree/.claude/patterns.md +15 -0
- package/releases/0.65.0/surfaces/prisma/tree/CLAUDE.md +5 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!-- nina:slot db.2 -->
|
|
2
|
+
- **If the schema, a migration or a query was touched anywhere in the diff: name `dba` on your Gates line.** Its approval is what a database change needs; yours does not stand in for it.
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot db.3 -->
|
|
5
|
+
- Check for **tenant scoping** in every new query in app code: it filters by `{{TENANT_KEY}}`, or reaches it through the model's foreign keys. No "global" queries except the auth library's own tables. Missing scope = `REJECTED`.
|
|
6
|
+
|
|
7
|
+
<!-- nina:slot db.4 -->
|
|
8
|
+
- Check for N+1 queries when the DB layer was touched.
|
|
9
|
+
|
|
10
|
+
<!-- nina:slot db.5 -->
|
|
11
|
+
- **tenant-and-privacy** — `{{TENANT_KEY}}` in every app query (the auth library's own tables are the only exemption);
|
|
12
|
+
no PII or secrets in logs; no cross-tenant read
|
|
13
|
+
or write reachable through a route, an MCP tool, a webhook, or an RPC between services.
|
|
14
|
+
|
|
15
|
+
<!-- nina:slot db.6 -->
|
|
16
|
+
- The schema touched (a migration in play)
|
|
17
|
+
|
|
18
|
+
<!-- nina:slot db.7 -->
|
|
19
|
+
- Leave `dba` off your Gates line on a database change "because it seems fine."
|
|
20
|
+
|
|
21
|
+
<!-- nina:slot db.8 -->
|
|
22
|
+
- Schema or query touched and `dba` not dispatched → name it on your Gates line; the orchestrator sends it.
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
<!-- nina:slot db.1 -->
|
|
2
|
+
- **AuthZ / tenant isolation (Hard Rule #7)** — EVERY query in app code scopes by `{{TENANT_KEY}}` (or its foreign key). Hunt for any query, route, MCP tool, or queue, webhook or background path where another tenant's data could be read or mutated (IDOR). The auth library's own tables are the only allowed unscoped reads — verify nothing else is.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!-- nina:slot db.1 -->
|
|
2
|
+
- **DBA guardrail** → the database is the surface where a bad change causes hard-to-reverse damage. Schema mistakes, missing indexes, cache on reads that feed a correctness-critical write, missing tenant scope — all caught here.
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot db.2 -->
|
|
5
|
+
| **dba** | Mandatory gate on schema changes, migrations and new queries | `.claude/agents/dba.md` |
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<!-- nina:slot db.1 -->
|
|
2
|
+
- `dba` — gate: runs when the schema, a migration or a query is touched
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot db.2 -->
|
|
5
|
+
- `implementer` → `dba` on `DIFF-READY` — the diff touches the schema, a migration or a query
|
|
6
|
+
- `dba` → `qa` on `APPROVED` — once the reviewer, and every other gate the diff went to, approved too
|
|
7
|
+
- `dba` → `implementer` on `REJECTED` — a query or schema change in the diff is unsafe · max 2
|
|
8
|
+
- `dba` → `architect` on `REJECTED` — the spec's migration or data design is unsafe · max 2
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!-- nina:slot db.2 -->
|
|
2
|
+
- Routes must NOT import query functions (`list*`, `find*`, `get*`, `upsert*`, `delete*`) from `{{DB_PKG_NAME}}` — only its client and its error classes.
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot db.4 -->
|
|
5
|
+
- **Every service query filters by `{{TENANT_KEY}}` (tenant scope).** Missing scope = automatic `REJECTED`.
|
|
6
|
+
|
|
7
|
+
<!-- nina:slot db.5 -->
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Database conventions (enforced by the dba subagent)
|
|
12
|
+
|
|
13
|
+
- **A cached read names its policy, and a read that feeds a write whose correctness depends on it being fresh is NEVER cached.** A stale row contaminates the decision made from it. Hard reject in dba review.
|
|
14
|
+
- **Every query in app code filters by `{{TENANT_KEY}}`** (or reaches it through the model's foreign keys). Tenant isolation is in the data layer. The auth library's own tables are the only exemption.
|
|
15
|
+
- Migrations reviewed by **dba** for: non-blocking on large tables, index coverage for new query patterns, backward compatibility during rolling deploy.
|
|
16
|
+
- N+1 queries are a bug.
|
|
17
|
+
|
|
18
|
+
<!-- nina:slot db.6 -->
|
|
19
|
+
- A real database, never a mock of its client.
|
|
20
|
+
|
|
21
|
+
<!-- nina:slot db.7 -->
|
|
22
|
+
- **A library's schema generator never writes the tracked schema.** One rewrites the file whole, so it writes to scratch.<!-- nina:why --> An auth library's generator dropped a hand-added table and foreign key.<!-- /nina:why --> The tracked file is the reviewed one and lists its deltas from the generator's output; a check regenerates into scratch and compares the two, less those deltas.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!-- nina:slot db.1 -->
|
|
2
|
+
|
|
3
|
+
### DBA (mandatory for the database)
|
|
4
|
+
|
|
5
|
+
- **Trigger:** any schema change, any new query, any migration.
|
|
6
|
+
- **Input:** the schema diff and its migration SQL + the application code using the queries.
|
|
7
|
+
- **Output:** `APPROVED`, or `REJECTED` with specific issues.
|
|
8
|
+
- **Tools:** Read, Grep, Glob, Bash (schema validation, an offline migration diff, query-plan checks).
|
|
9
|
+
- **Checks:** schema validates; migration safe on large tables; index coverage; an explicit policy on every cached read; **no cache on a read that feeds a correctness-critical write**; **tenant scope in every query**; no N+1; no raw PII without encryption; the connection secret comes from the environment, never from code.
|
|
10
|
+
- **Position:** beside the reviewer, when a diff touches the schema, a migration or a query; `qa` waits for its `APPROVED`.
|
|
11
|
+
|
|
12
|
+
<!-- nina:slot db.2 -->
|
|
13
|
+
- **If the schema or a query was touched → dba approved.**
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!-- nina:slot db.1 -->
|
|
2
|
+
| Schema change | `.claude/architecture.md` § *Database*, the schema | implementer → reviewer + **dba** (mandatory) → qa |
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot db.2 -->
|
|
5
|
+
| New query in app code | `.claude/patterns.md` § *Database conventions* | implementer → reviewer + **dba** (mandatory) → qa |
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!-- nina:slot db.3 -->
|
|
2
|
+
| **ANY schema change, migration or new query** | **+ dba beside the reviewer** |
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot db.4 -->
|
|
5
|
+
- **dba** → mandatory gate on schema changes, migrations, and new queries
|
|
6
|
+
|
|
7
|
+
<!-- nina:slot db.5 -->
|
|
8
|
+
2. **Every schema change, migration and new query goes through the dba subagent.** No exceptions. It runs beside the reviewer, which names it on its Gates line, and `qa` does not go out without its `APPROVED`.
|
|
9
|
+
|
|
10
|
+
<!-- nina:slot db.6 -->
|
|
11
|
+
6. **A cached read names its cache policy, and a read that feeds a write whose correctness depends on it being fresh is never cached.** An implicit cache is a bug, and a stale row contaminates every decision made from it. A write invalidates what it made stale.
|
|
12
|
+
|
|
13
|
+
<!-- nina:slot db.7 -->
|
|
14
|
+
7. **Tenant isolation is enforced in the data layer.** Every query in app code filters by `{{TENANT_KEY}}`, or reaches it through the model's foreign keys. No "global" queries except the auth library's own tables. Reviewer rejects any query missing the scope.
|
|
15
|
+
|
|
16
|
+
<!-- nina:slot db.8 -->
|
|
17
|
+
- **the database**: the schema, a migration, or a query
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<!-- nina:slot drizzle.1 -->
|
|
2
|
+
No skill covers Drizzle. What `drizzle-orm` and `drizzle-kit` do is cited from their installed source, by
|
|
3
|
+
`file:line`, like any installed library — and what they do on this project's engine, from its integration doc.
|
|
4
|
+
|
|
5
|
+
<!-- nina:slot drizzle.2 -->
|
|
6
|
+
- **With Drizzle, offline and failing closed.** Draft the migration with `drizzle-kit generate`, which needs no driver and no URL, in a copy of the tree in your scratch directory — it writes the SQL, a snapshot and a journal, and the tracked migrations are not yours to write — and read the SQL, not the schema delta. On SQLite kit drafts a table redefinition for a nullability, type, default or primary-key change, a foreign key added to an existing column or with a changed action, and a CHECK: look for one in every draft. drizzle-kit fails open: it has exited 0 with no file when a rename prompt could not render without a terminal, on a schema syntax error and with a missing journal, and exited 0 with a `DROP TABLE` for every table when the schema exported none. The check passes only on exit 0, empty stderr, and either exactly one new `.sql` holding the change you expected, or no new file and the exact line "No schema changes, nothing to migrate". A rename comes out as `RENAME COLUMN` or as `ADD` plus `DROP COLUMN` depending on that prompt's answer: read which you got. An `ADD COLUMN … REFERENCES` draft has dropped the declared `ON DELETE` while kit's snapshot kept it, so a later "no changes" passes over a database that differs from the schema — only a test against the applied schema sees it.
|
|
7
|
+
|
|
8
|
+
<!-- nina:slot drizzle.3 -->
|
|
9
|
+
- Run a drizzle-kit command that writes to a database — `push`, `migrate` — or give a drizzle config the credentials of a real one. `generate` answers everything this gate asks.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!-- nina:slot drizzle.1 -->
|
|
2
|
+
- **Drizzle migrations go out through the one runner the architecture names**, as the SQL files the database gate read, and nothing else changes a shared database's schema.
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot drizzle.2 -->
|
|
5
|
+
- Run `drizzle-kit push` against a shared database. It applies the schema without a migration file, so no gate read what it did.
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
<!-- nina:slot drizzle.1 -->
|
|
2
|
+
- **With Drizzle, `db.transaction` and `db.batch` do what this project's driver does with them**, not what their types promise: `tsc` accepts calls a driver refuses at run time. Rely on either only as the integration doc's premises, cited from the driver's installed source, say it behaves.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<!-- nina:slot drizzle.1 -->
|
|
2
|
+
|
|
3
|
+
### Drizzle
|
|
4
|
+
|
|
5
|
+
- Tables are declared in one schema module per storage backend, and only there.
|
|
6
|
+
- A database error does not leave the data layer as Drizzle threw it: a failed single query's error carries the SQL and every bound value, in its message and as properties, so logging or serializing it publishes them. `logger: true` logs every parameter too.
|
|
7
|
+
- Tests run a real Drizzle client against the real engine or its local emulator; the client is never mocked.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!-- nina:slot edge-cf.1 -->
|
|
2
|
+
| `cloudflare:workers-best-practices` | `{{API_DIR}}/**` — routes, services, middleware, queue handlers |
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot edge-cf.2 -->
|
|
5
|
+
| `cloudflare:durable-objects` | `{{API_DIR}}/src/do/**` or any DO call site |
|
|
6
|
+
|
|
7
|
+
<!-- nina:slot edge-cf.3 -->
|
|
8
|
+
| `cloudflare:cloudflare` | Queues, R2 or KV |
|
|
9
|
+
|
|
10
|
+
<!-- nina:slot edge-cf.4 -->
|
|
11
|
+
| `cloudflare:wrangler` | its config or any wrangler command |
|
|
12
|
+
|
|
13
|
+
<!-- nina:slot edge-cf.5 -->
|
|
14
|
+
- **Cloudflare skill flag** — if the spec touches `{{API_DIR}}/**` (routes, services, queue handlers), the per-account Durable Object, Queues, R2/KV, or `wrangler` config, name which `cloudflare:*` plugin skill the implementer must consult (`cloudflare:workers-best-practices`, `cloudflare:durable-objects`, `cloudflare:cloudflare`, `cloudflare:wrangler`), or state why none applies. Retrieval beats recall — the reviewer rejects a spec that touches these surfaces without naming the skill.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<!-- nina:slot edge-cf.1 -->
|
|
2
|
+
- **`cloudflare:wrangler`** — before running ANY `wrangler` command or reading the wrangler config.
|
|
3
|
+
CLI flags and config fields churn between minor versions; pre-trained knowledge is stale.
|
|
4
|
+
|
|
5
|
+
<!-- nina:slot edge-cf.2 -->
|
|
6
|
+
- **`cloudflare:cloudflare`** — when the deploy touches R2, KV, Queues or Durable Object bindings.
|
|
7
|
+
|
|
8
|
+
<!-- nina:slot edge-cf.3 -->
|
|
9
|
+
- **Every target the change reaches, or none.** A change to the API surface deploys the Worker's staging or preview, and — where the frontend deploys apart from it, as a Pages project or a Worker of its own — the frontend's too. Deploying a frontend alone against an old API is the failure that renders new fields as "—". Where one Worker serves the API and the frontend as static assets, the two are one deploy. State which targets this change requires and why.
|
|
10
|
+
|
|
11
|
+
<!-- nina:slot edge-cf.4 -->
|
|
12
|
+
- **Secret and variable parity.** Everything the new code reads via `env.*` exists in the target environment (`wrangler secret list`). A missing secret fails at request time, not at deploy time.
|
|
13
|
+
|
|
14
|
+
<!-- nina:slot edge-cf.5 -->
|
|
15
|
+
- **A variable the frontend bundle reads is in the environment of its build command.** The wrangler config's `vars` reach a Worker at runtime and never a bundler, which inlines what it reads at build time. A missing one ships a blank page with no error.
|
|
16
|
+
|
|
17
|
+
## A frontend on Pages: which branch, and why it matters
|
|
18
|
+
|
|
19
|
+
Only where the frontend deploys as a Pages project of its own; a Worker that serves it as static assets has no
|
|
20
|
+
branch to choose. A **direct-upload** Pages project whose production branch is `main` goes to production or
|
|
21
|
+
to preview by `wrangler pages deploy --branch` alone:
|
|
22
|
+
|
|
23
|
+
- **Staging FE** → `--branch staging`. A preview deployment on its own URL. Production is untouched,
|
|
24
|
+
and this is the FE half of the staging pair with the Worker's staging.
|
|
25
|
+
- **Production FE** → `--branch main`. Only with an explicit go from {{OWNER}}, per § *Production*.
|
|
26
|
+
|
|
27
|
+
Deploying staging with `--branch=main` is a production deploy wearing a staging label — the
|
|
28
|
+
permission classifier is right to stop it. Use the staging branch and the block disappears, because
|
|
29
|
+
the block was correct.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!-- nina:slot edge-cf.1 -->
|
|
2
|
+
| `cloudflare:workers-best-practices` | anything under `{{API_DIR}}/**` |
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot edge-cf.2 -->
|
|
5
|
+
| `cloudflare:durable-objects` | anything under `{{API_DIR}}/src/do/**` or a DO call site |
|
|
6
|
+
|
|
7
|
+
<!-- nina:slot edge-cf.3 -->
|
|
8
|
+
| `cloudflare:cloudflare` | a queue consumer, or R2 / KV access |
|
|
9
|
+
|
|
10
|
+
<!-- nina:slot edge-cf.4 -->
|
|
11
|
+
| `cloudflare:wrangler` | a change to the wrangler config |
|
|
12
|
+
|
|
13
|
+
<!-- nina:slot edge-cf.5 -->
|
|
14
|
+
- Respect Workers conventions: secrets in `{{SECRETS_LOCAL}}` for local dev (never commit), `nodejs_compat` in the wrangler config's `compatibility_flags`.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!-- nina:slot edge-cf.1 -->
|
|
2
|
+
| `cloudflare:durable-objects` | DO lifecycle, alarms, RPC, storage — the API surface drifts fast |
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot edge-cf.2 -->
|
|
5
|
+
| `cloudflare:cloudflare` | Queues, R2 or KV bindings |
|
|
6
|
+
|
|
7
|
+
<!-- nina:slot edge-cf.3 -->
|
|
8
|
+
| `cloudflare:wrangler` | anything you run through the wrangler CLI |
|
|
9
|
+
|
|
10
|
+
<!-- nina:slot edge-cf.4 -->
|
|
11
|
+
- Any new Cloudflare binding read/write: R2, KV, Queues, Durable Objects — drive the binding via Miniflare and observe real I/O. Miniflare fetches `workers.cloudflare.com/cf.json` as it starts unless `CLOUDFLARE_CF_FETCH_ENABLED=false`: set it where a run promises no network beyond a named host
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<!-- nina:slot edge-cf.1 -->
|
|
2
|
+
| `cloudflare:workers-best-practices` | `{{API_DIR}}/**` |
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot edge-cf.2 -->
|
|
5
|
+
| `cloudflare:durable-objects` | `{{API_DIR}}/src/do/**` or a DO call site |
|
|
6
|
+
|
|
7
|
+
<!-- nina:slot edge-cf.3 -->
|
|
8
|
+
- `{{API_DIR}}/src/do/`, a queue consumer, or a webhook receiver touched (serialization + idempotency surface)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<!-- nina:slot edge-cf.1 -->
|
|
2
|
+
- **Secrets & config exposure** — no secrets in committed files, logs, error messages, or generated artifacts (`worker-configuration.d.ts` must contain only types, never values). `{{SECRETS_LOCAL}}`/`.env` gitignored. The split between `wrangler secret` and the wrangler config's `vars` is correct. No secret echoed in a response or thrown error.
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot edge-cf.2 -->
|
|
5
|
+
- **Dependency & binding posture** — obviously risky dependency usage; Cloudflare binding scoping (R2 bucket names, Queue, DO) — nothing over-privileged or world-exposed; `nodejs_compat` and compatibility flags sane.
|
|
6
|
+
|
|
7
|
+
<!-- nina:slot edge-cf.3 -->
|
|
8
|
+
- Prefer retrieval over recall for Cloudflare/{{AUTH_LIB}} specifics: consult the `cloudflare:*` skills and the installed `node_modules` source / `.claude/integrations/*.md` rather than assuming.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!-- nina:slot edge-cf.1 -->
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Secrets
|
|
6
|
+
|
|
7
|
+
- **Wrangler secrets** for Workers (`{{SECRETS_PROD}} X`). Never `.env` committed.
|
|
8
|
+
- **Local dev:** `{{SECRETS_LOCAL}}` (gitignored).
|
|
9
|
+
|
|
10
|
+
<!-- nina:slot edge-cf.2 -->
|
|
11
|
+
- `nodejs_compat` in the wrangler config's `compatibility_flags` is required for a database client that runs at the edge.
|
|
12
|
+
|
|
13
|
+
<!-- nina:slot edge-cf.3 -->
|
|
14
|
+
- **Miniflare** for Worker integration tests.
|
|
15
|
+
|
|
16
|
+
<!-- nina:slot edge-cf.4 -->
|
|
17
|
+
- **Miniflare** for Worker tests — the real Cloudflare runtime, no mocks.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<!-- nina:slot edge-cf.1 -->
|
|
2
|
+
| New Durable Object behavior | `.claude/architecture.md` § *Durable Objects*, existing `{{API_DIR}}/src/do/` | architect → implementer → reviewer + the integration gate, where there is one → qa |
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot edge-cf.2 -->
|
|
5
|
+
| New Queue handler | `.claude/architecture.md` § *Processing pipeline*, `{{API_DIR}}/src/queues/` | architect → implementer → reviewer + the integration gate, where there is one → qa |
|
|
6
|
+
|
|
7
|
+
<!-- nina:slot edge-cf.3 -->
|
|
8
|
+
| Wrangler / deploy / config | `CLAUDE.md` § *Common commands*, the wrangler config (`wrangler.jsonc` or `wrangler.toml`), `.claude/patterns.md` § *Secrets* | implementer → reviewer → qa → devops |
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<!-- nina:slot edge-cf.1 -->
|
|
2
|
+
|
|
3
|
+
### Cloudflare plugin skills — USE THEM
|
|
4
|
+
|
|
5
|
+
The `cloudflare@cloudflare` plugin is installed at user scope and exposes skills that retrieve the **current** Cloudflare docs at invocation time. Pre-trained knowledge of Workers / Wrangler / DO / Queues / Cron APIs is stale — prefer the skill over recalling from memory.
|
|
6
|
+
|
|
7
|
+
<!-- nina:slot edge-cf.2 -->
|
|
8
|
+
| Write or review code under `{{API_DIR}}/**` (routes, services, middleware, webhook receivers, queue consumers) | `cloudflare:workers-best-practices` | catches floating promises, global-state misuse, missing `waitUntil`, streaming pitfalls |
|
|
9
|
+
|
|
10
|
+
<!-- nina:slot edge-cf.3 -->
|
|
11
|
+
| Touch `{{API_DIR}}/src/do/**` or any Durable Object call site | `cloudflare:durable-objects` | covers RPC, alarms, WebSocket, SQLite storage, lifecycle — DO API surface drifts fast |
|
|
12
|
+
|
|
13
|
+
<!-- nina:slot edge-cf.4 -->
|
|
14
|
+
| Touch `{{API_DIR}}/src/queues/**`, R2/KV usage | `cloudflare:cloudflare` | Queues, R2, KV decision tree |
|
|
15
|
+
|
|
16
|
+
<!-- nina:slot edge-cf.5 -->
|
|
17
|
+
| Author or modify `wrangler.toml` / `wrangler.jsonc` / run any `wrangler` CLI command | `cloudflare:wrangler` | CLI flags + config fields churn between minor versions |
|
|
18
|
+
|
|
19
|
+
<!-- nina:slot edge-cf.6 -->
|
|
20
|
+
| Run the **devops** deploy stage | `cloudflare:wrangler` (+ `cloudflare:cloudflare` for R2/KV/Queues/DO bindings) | deploy flags and binding config churn between versions |
|
|
21
|
+
|
|
22
|
+
<!-- nina:slot edge-cf.7 -->
|
|
23
|
+
|
|
24
|
+
The plugin's other skills — `cloudflare:agents-sdk`, `cloudflare:sandbox-sdk`, `cloudflare:cloudflare-email-service` — apply only where {{PROJECT}} adopts what they cover: an agent runtime, untrusted code execution, sending mail.
|
|
25
|
+
|
|
26
|
+
<!-- nina:slot edge-cf.8 -->
|
|
27
|
+
10. **Secrets in `{{SECRETS_LOCAL}}` for local dev, `{{SECRETS_PROD}}` for production.** Never `.env` committed. `nodejs_compat` is required in the `compatibility_flags` of the wrangler config (`wrangler.jsonc` or `wrangler.toml`) for any database client that runs at the edge.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!-- nina:slot frontend.1 -->
|
|
2
|
+
| `cloudflare:web-perf` | a page-load performance question in `{{APP_DIR}}` |
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot frontend.2 -->
|
|
5
|
+
|
|
6
|
+
## Visual acceptance — required in any spec touching `{{APP_DIR}}/**`
|
|
7
|
+
|
|
8
|
+
A frontend spec that describes only structure produces a diff that only structure can verify, and
|
|
9
|
+
structure is exactly what a test renderer checks while blind to the result. So state, as a short
|
|
10
|
+
checklist, what must be TRUE ON THE SCREEN at **1440** and at **375**:
|
|
11
|
+
|
|
12
|
+
- what is visible, and what must not be
|
|
13
|
+
- what sits beside what, and what wraps or stacks at 375
|
|
14
|
+
- the empty, loading and error states, if the change can produce them
|
|
15
|
+
- anything that must not overflow, clip or overlap
|
|
16
|
+
|
|
17
|
+
Each line must be checkable from a screenshot by someone who did not write the code — the **reviewer**
|
|
18
|
+
checks the render against this list, and rejects on a mismatch. "Follows the design system" is not a
|
|
19
|
+
criterion. A spec that touches `{{APP_DIR}}/**` without this section is incomplete, and the reviewer
|
|
20
|
+
rejects it back to you.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<!-- nina:slot frontend.1 -->
|
|
2
|
+
- Playwright is **not** a skill — it is an MCP server, and its `browser_*` tools are granted to you
|
|
3
|
+
directly in `tools:` above. Use them; there is nothing to invoke via `Skill`.
|
|
4
|
+
|
|
5
|
+
<!-- nina:slot frontend.2 -->
|
|
6
|
+
- **Build-time env.** A bundler inlines the variables the app reads at build time, so one the deploy platform supplies at RUNTIME never reaches the bundle — it is not late, it is absent. Verify every variable the app reads is present in the build environment — a missing one ships a blank page, not an error.
|
|
7
|
+
|
|
8
|
+
<!-- nina:slot frontend.3 -->
|
|
9
|
+
|
|
10
|
+
## Render smoke — for any diff touching `{{APP_DIR}}/**`
|
|
11
|
+
|
|
12
|
+
You are not the visual gate; the **reviewer** is, and it judges the render against the spec before
|
|
13
|
+
the code ever reaches you. Your question is narrower and different: **did the deployed thing come up
|
|
14
|
+
at all?** That is not the same failure — a build that renders perfectly on a reviewer's localhost
|
|
15
|
+
still ships a blank page when the API's address was missing from the deploy build.<!-- nina:why --> That is exactly
|
|
16
|
+
what happened here once.<!-- /nina:why -->
|
|
17
|
+
|
|
18
|
+
So on the preview URL, not localhost: `browser_navigate`, one screenshot at 1440, and
|
|
19
|
+
`browser_console_messages`. You are checking that the page renders and the console is clean — not
|
|
20
|
+
whether the layout is right. A blank page or a console error is `VERDICT: BLOCKED`.
|
|
21
|
+
|
|
22
|
+
<!-- nina:slot frontend.4 -->
|
|
23
|
+
|
|
24
|
+
If the changed path genuinely cannot be smoked without authenticating, do NOT authenticate: say so,
|
|
25
|
+
report what you could verify, and hand the authenticated check to a human.
|
|
26
|
+
|
|
27
|
+
<!-- nina:slot frontend.5 -->
|
|
28
|
+
, mcp__plugin_playwright_playwright__browser_navigate, mcp__plugin_playwright_playwright__browser_resize, mcp__plugin_playwright_playwright__browser_take_screenshot, mcp__plugin_playwright_playwright__browser_snapshot, mcp__plugin_playwright_playwright__browser_console_messages, mcp__plugin_playwright_playwright__browser_close
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<!-- nina:slot frontend.1 -->
|
|
2
|
+
| `frontend-design:frontend-design` | new UI in `{{APP_DIR}}` — a page or component that did not exist |
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot frontend.2 -->
|
|
5
|
+
|
|
6
|
+
## Look at the screen you built
|
|
7
|
+
|
|
8
|
+
When you change anything under `{{APP_DIR}}/**`, open it in the browser before you hand off — the dev
|
|
9
|
+
server or a local build, with `browser_navigate` plus a screenshot at 1440 and 375. Your test suite
|
|
10
|
+
renders with no layout engine, in jsdom or to a string in Node, and cannot tell you whether the thing renders.
|
|
11
|
+
|
|
12
|
+
This is a self-check, not a gate: you are the author, and an author reviewing their own work catches
|
|
13
|
+
the obvious and misses the rest. **reviewer** is the gate. The point of doing it here is that the
|
|
14
|
+
obvious defects are cheap to fix now and expensive after review, qa and a deploy.
|
|
15
|
+
|
|
16
|
+
Report what you saw. "Rendered at both widths, matches the spec's Visual acceptance" — or what
|
|
17
|
+
differed.
|
|
18
|
+
|
|
19
|
+
That is also why a component test must assert content, not presence: `toHaveTextContent('4 matched')`,
|
|
20
|
+
not `toBeInTheDocument()` on the badge that shows it, and never `toHaveClass` as proof that something
|
|
21
|
+
looks right — it passes whether or not a pixel landed. A test that pins a removal renders the tab or
|
|
22
|
+
route where the removed thing used to appear; rendering the default one proves nothing about another.
|
|
23
|
+
|
|
24
|
+
<!-- nina:slot frontend.3 -->
|
|
25
|
+
, mcp__plugin_playwright_playwright__browser_navigate, mcp__plugin_playwright_playwright__browser_resize, mcp__plugin_playwright_playwright__browser_take_screenshot, mcp__plugin_playwright_playwright__browser_snapshot, mcp__plugin_playwright_playwright__browser_console_messages, mcp__plugin_playwright_playwright__browser_close
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!-- nina:slot frontend.1 -->
|
|
2
|
+
|
|
3
|
+
## What your green does not mean
|
|
4
|
+
|
|
5
|
+
Your suite renders with **no layout engine**, in jsdom or to a string in Node. `toHaveClass('flex')` passes whether or not
|
|
6
|
+
anything was laid out; nothing you run can tell you the screen is right. When you report PASS on a
|
|
7
|
+
diff touching `{{APP_DIR}}/**`, say so — the rendering is checked by the **reviewer**, against the
|
|
8
|
+
spec's Visual acceptance criteria. A PASS read as "the screen works" is how frontend
|
|
9
|
+
defects reached staging through every gate.
|
|
10
|
+
|
|
11
|
+
## When a failure arrives labelled "pre-existing"
|
|
12
|
+
|
|
13
|
+
On this surface the cause is usually the test harness rather than the feature — a text query matching
|
|
14
|
+
several elements at once, a missing router context for a link, a `vi.mock` hoisted above the value it
|
|
15
|
+
closes over, jsdom without `showModal`, a hardcoded origin. Every one of them is fixable in the test
|
|
16
|
+
without weakening the assertion, and every one of them stays invisible for exactly as long as the
|
|
17
|
+
failure keeps the label.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<!-- nina:slot frontend.2 -->
|
|
2
|
+
- **If the diff touches `{{APP_DIR}}/**`: the spec — or the dispatch, in a chain with no architect — must carry a Visual acceptance section, and you check the render against it — see *Visual gate* below.** No section → REJECT upstream to whoever wrote it.
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot frontend.3 -->
|
|
5
|
+
- `{{BUILD_CMD}}` for the affected frontend packages (`{{APP_DIR}}`)
|
|
6
|
+
|
|
7
|
+
<!-- nina:slot frontend.4 -->
|
|
8
|
+
## Visual gate — you are the stage that looks at the screen
|
|
9
|
+
|
|
10
|
+
Deciding whether a screen is right is review, not operations, and it belongs here. qa runs vitest with
|
|
11
|
+
**no layout engine**, in jsdom or rendering to a string: `toHaveClass('flex')` passes whether or not a single pixel
|
|
12
|
+
landed anywhere. Every gate before you reads text. This is why backend work lands first time through
|
|
13
|
+
this pipeline and frontend work does not — the frontend defects were never expressible as a test.
|
|
14
|
+
|
|
15
|
+
For any diff touching `{{APP_DIR}}/**`:
|
|
16
|
+
|
|
17
|
+
1. Build and serve it the way this project serves a build for review:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
{{VISUAL_SERVE}}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Where screens sit behind a login, this command is what gets past it: a plain static server
|
|
24
|
+
screenshots the login page. Where the build reaches an API at an address fixed at build time, one
|
|
25
|
+
that does not match what the command serves ships a screen that renders but fetches nothing. A
|
|
26
|
+
request the served build does not answer is a gap in it: send it back naming the request, rather
|
|
27
|
+
than judge a broken screen. It must also reach the empty and error states the change has; a state
|
|
28
|
+
you cannot reach is a state nobody reviewed.
|
|
29
|
+
2. `browser_navigate` to the changed screen. Screenshot at **1440** and at **375**
|
|
30
|
+
(`browser_resize` first — the viewport resets to a narrow default across navigations, so
|
|
31
|
+
resize AFTER navigating and confirm the width in the screenshot before you judge it).
|
|
32
|
+
|
|
33
|
+
Many screens are not reachable by URL: a tab kept in component state is never in the route, so
|
|
34
|
+
`browser_click` is the ONLY way in — a navigate-and-screenshot lands on the default tab and proves
|
|
35
|
+
nothing about the changed one.
|
|
36
|
+
3. **Measure, do not squint.** `browser_evaluate` turns "looks fine" into a number, and an
|
|
37
|
+
overflow you can measure is one you can attribute. To decide whether horizontal scroll is
|
|
38
|
+
yours or pre-existing, measure `document.documentElement.scrollWidth` vs `clientWidth` on
|
|
39
|
+
the changed screen AND on an untouched one — and, when it matters, build `HEAD` into a second
|
|
40
|
+
dist from a worktree (Hard Rule #18) and measure both. Reporting a pre-existing app-wide defect
|
|
41
|
+
as a regression wastes a cycle; excusing a real one as "probably pre-existing" ships it.
|
|
42
|
+
4. Check each screenshot against the spec's **Visual acceptance** list, item by item. Not "looks
|
|
43
|
+
fine" — the specific claims the spec made.
|
|
44
|
+
5. `browser_console_messages`. An error there is a defect even when the page renders.
|
|
45
|
+
|
|
46
|
+
A screen that contradicts the spec is `REJECTED`, with the screenshot and what is wrong in it. Say in
|
|
47
|
+
your report which widths you looked at; a frontend approval that does not mention having looked is
|
|
48
|
+
the failure this section exists to end.
|
|
49
|
+
|
|
50
|
+
<!-- nina:slot frontend.1 -->
|
|
51
|
+
, mcp__plugin_playwright_playwright__browser_navigate, mcp__plugin_playwright_playwright__browser_resize, mcp__plugin_playwright_playwright__browser_take_screenshot, mcp__plugin_playwright_playwright__browser_snapshot, mcp__plugin_playwright_playwright__browser_console_messages, mcp__plugin_playwright_playwright__browser_click, mcp__plugin_playwright_playwright__browser_evaluate, mcp__plugin_playwright_playwright__browser_close
|
|
52
|
+
|
|
53
|
+
<!-- nina:slot frontend.5 -->
|
|
54
|
+
- **visual** — the spec's Visual acceptance section exists and the render matches it (see *Visual gate*).
|
|
55
|
+
A reviewer checking only another dimension skips it, so it is its own: owned by **patterns-and-scope**
|
|
56
|
+
when no visual reviewer is dispatched, and never nobody's.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<!-- nina:slot frontend.1 -->
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Frontend conventions
|
|
6
|
+
|
|
7
|
+
This project's own — its framework, where server state lives, how it styles, and what it shares between
|
|
8
|
+
apps — are in `.claude/frontend.md`. Read them before writing or reviewing code under `{{APP_DIR}}`.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!-- nina:slot frontend.2 -->
|
|
2
|
+
|
|
3
|
+
### Nothing before devops can see the screen
|
|
4
|
+
The reviewer reads a diff. qa runs vitest with **no layout engine**, in jsdom or rendering to a string — `toHaveClass('flex')`
|
|
5
|
+
passes whether or not a pixel landed anywhere. Every backend invariant is checkable in text, which is why
|
|
6
|
+
the same pipeline lands backend work first time and lets frontend defects through every gate: they were
|
|
7
|
+
never expressible as a test.
|
|
8
|
+
|
|
9
|
+
So any diff touching `{{APP_DIR}}/**` carries a visual contract: the **architect** writes a *Visual acceptance*
|
|
10
|
+
checklist (what must be true on screen at 1440 and 375), the **reviewer** rejects an FE spec without one, the
|
|
11
|
+
**implementer** looks at its own screen before handing off, and the **reviewer** builds, serves and screenshots it,
|
|
12
|
+
rejecting on a mismatch — judging the screen is review, not operations. **devops** only smokes the
|
|
13
|
+
deployed preview for a blank page. Browser access is granted through the Playwright MCP `browser_*` tools in those agents'
|
|
14
|
+
`tools:` lists — it is not a skill, and an agent without those tools in its list cannot see anything.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!-- nina:slot frontend.1 -->
|
|
2
|
+
| Audit page-load perf on `{{APP_DIR}}` — LCP, INP, CLS, bundle size | `cloudflare:web-perf` | Core Web Vitals + Chrome DevTools MCP integration |
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot frontend.2 -->
|
|
5
|
+
| **ANY diff touching `{{APP_DIR}}/**`** | architect writes a **Visual acceptance** checklist (the dispatch carries it in a chain with no architect); the **reviewer builds, serves and screenshots at 1440 and 375** and rejects on a mismatch; devops smokes the deployed preview for a blank page. A test renderer has no layout engine — a green suite says nothing about the screen |
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!-- nina:slot integrations.1 -->
|
|
2
|
+
- **Integration premises (MANDATORY when an integration boundary is in scope)** — list every behavior of the dependency the implementation depends on. Each premise is a one-sentence claim followed by the evidence its kind requires:
|
|
3
|
+
- `installed-library` → `<package dir>/node_modules/<lib>/<file>:<line>`, the version installed here beside it — the package's own link, which a peer change does not rename; a dependency of a dependency, through its parent's: `<package dir>/node_modules/<parent> → <lib>@<version>/<file>:<line>`
|
|
4
|
+
- `live-api` → a contract-test case, or a response observed against the service and captured verbatim
|
|
5
|
+
- `platform-binding` → behavior observed under the local emulator, plus the platform's installed types
|
|
6
|
+
- or a `P<n>` reference into `.claude/integrations/<slug>.md`, which itself carries one of the above
|
|
7
|
+
|
|
8
|
+
A premise without a citation is an **assumption**, not a fact. The integration-tester rejects the spec if a cited line does not validate at runtime, and rejects it outright if it lists uncited premises. When in doubt: read the source. A live service you have no way to read is observed for you: report `BLOCKED` with an `ISSUES` id per premise to observe, the integration-tester records each as a `P<n>` with the response it got, and you are resumed with them. Integration regressions ship precisely when a premise is assumed instead of cited.
|
|
9
|
+
|
|
10
|
+
Also mark **"INTEGRATION-TESTER REQUIRED"** prominently at the top of the spec when this section is present.
|
|
11
|
+
|
|
12
|
+
<!-- nina:slot integrations.3 -->
|
|
13
|
+
- **For every integration behavior the implementation will rely on, get the evidence its kind requires** — open the installed source and cite `file:line`, or, for a live service, have it observed first (report `BLOCKED` naming what to observe). Library docs and editor autocomplete are not citations. This is the single most important rule for avoiding integration regressions.
|
|
14
|
+
|
|
15
|
+
<!-- nina:slot integrations.4 -->
|
|
16
|
+
- **State an integration's behavior as fact without the evidence its kind requires.** Uncited premises are assumptions, and assumptions are what ship integration bugs to prod. If you cannot find the citation, the answer is to read more source or escalate — not to write the spec anyway.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!-- nina:slot integrations.1 -->
|
|
2
|
+
- If you change anything that touches an integration boundary, or add a call into one, flag it explicitly in the diff summary so the reviewer dispatches **integration-tester** before approving.
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot integrations.3 -->
|
|
5
|
+
|
|
6
|
+
## Integrations
|
|
7
|
+
- All access to a dependency goes through its **boundary module**, named in `.nina/profile.json`. **Never** reach it directly from a service, route, queue consumer or scheduled handler.
|
|
8
|
+
- For a `live-api`, the contract-test suite is the source of truth and **both** implementations must pass it. Inject the mock in tests — and never let the mock be the only thing that ran.
|
|
9
|
+
- Honor the spec's **Integration premises** verbatim. If one looks wrong while you are writing code against it, STOP and loop back to the architect. Do not work around a premise: the premise is the contract, and a contract that is wrong gets fixed, not bypassed.
|
|
10
|
+
|
|
11
|
+
<!-- nina:slot integrations.4 -->
|
|
12
|
+
- Reach an integration from anywhere outside its boundary module. Inject the mock in tests.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!-- nina:slot integrations.2 -->
|
|
2
|
+
2. **Live APIs.** A third-party service you reach over the network — payments, messaging, automation, mail. *Their* API is the contract; we conform to it. There IS a real service to hit, so you run the **contract-test suite** for that boundary against **BOTH** the real client and the mock, and you check each `P<n>` premise in `.claude/integrations/<slug>.md` against what the service actually returns.
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot integrations.3 -->
|
|
5
|
+
- The boundary module of any declared integration, or a new call into one
|
|
6
|
+
|
|
7
|
+
<!-- nina:slot integrations.6 -->
|
|
8
|
+
- For the integration you are gating: `.claude/integrations/<slug>.md`. Read EVERY premise it lists, not a sample — the one you skip is the one the diff depends on.
|
|
9
|
+
|
|
10
|
+
<!-- nina:slot integrations.7 -->
|
|
11
|
+
|
|
12
|
+
## You MUST — live APIs
|
|
13
|
+
|
|
14
|
+
- **Run the contract-test suite** for the boundary against **both** implementations. Every `P<n>` in the integration's doc maps to a case — verify every one the diff touches.
|
|
15
|
+
- **Never accept a green mock as proof.** If only the mock run is green you have verified nothing about production. Say so, and reject.
|
|
16
|
+
- **Confirm nothing reaches the service outside its boundary module.** Grep the diff and the tree for raw HTTP or client construction past the boundary. Any leak is a hard reject.
|
|
17
|
+
- **Confirm the boundary validates responses** in both implementations, so drift in the service surfaces structurally rather than as a type error three layers downstream.
|
|
18
|
+
|
|
19
|
+
<!-- nina:slot integrations.8 -->
|
|
20
|
+
- **Update `.claude/integrations/<slug>.md` when you observe new behavior**, with the evidence that settled it: a contract case, an observed response, or a source citation.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<!-- nina:slot integrations.1 -->
|
|
2
|
+
- **If an integration boundary was touched anywhere in the diff, or a new call into one was added: name `integration-tester` on your Gates line.** Also verify the architect's spec carries an **Integration premises** section, each premise with the evidence its kind requires. If the section is missing or uncited, REJECT upstream — do not approve a diff against an incomplete spec.
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot integrations.3 -->
|
|
5
|
+
- The diff touches an integration boundary — **integration-tester also required**
|
|
6
|
+
|
|
7
|
+
<!-- nina:slot integrations.4 -->
|
|
8
|
+
- Leave `integration-tester` off your Gates line "because the unit tests pass." Unit tests honor the spec's mock, not the real dependency — only the integration-tester catches drift.
|
|
9
|
+
|
|
10
|
+
<!-- nina:slot integrations.5 -->
|
|
11
|
+
- Integration boundary touched and `integration-tester` not dispatched → name it on your Gates line; the orchestrator sends it.
|
|
12
|
+
|
|
13
|
+
<!-- nina:slot integrations.6 -->
|
|
14
|
+
- Spec touches an integration boundary but lacks the **Integration premises** section with citations → back to **architect** for spec amendment (do NOT approve the implementer's diff against an incomplete spec).
|
|
15
|
+
|
|
16
|
+
<!-- nina:slot integrations.7 -->
|
|
17
|
+
- **The one exception is a `live-api` contract-test case.** Its job is to catch the real service
|
|
18
|
+
diverging from the mock, not to encode a mutation of code this project owns, so there is often no
|
|
19
|
+
change on this side that turns it red. Name the divergence it would catch instead — "the provider
|
|
20
|
+
renames a field the mock still sends" — and do not reject it for lacking a mutation: that test is
|
|
21
|
+
integration-tester's gate, and rejecting it here would remove the one check that watches the mock.
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
<!-- nina:slot integrations.1 -->
|
|
2
|
+
- **Input validation / injection / SSRF** — validate at every trust boundary: request body, query, queue message, webhook payload, and every integration response. Parameterized queries only — never string-built SQL. Outbound HTTP to an integration: SSRF and URL-allowlist posture. Start with the unauthenticated internet-facing paths — a webhook receiver, a health route, a dev-only surface — and verify their signature or secret gate cannot be bypassed or replayed.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<!-- nina:slot integrations.1 -->
|
|
2
|
+
- **Integration guardrail** → a spec describes behavior the implementer relies on, and unit tests with structural mocks honor the *spec*, not the dependency. The integration-tester runs the real thing: real flows for an installed library or a platform binding, and for a live API the contract suite against both the real client and the mock, so the two cannot diverge. Drift between assumption and reality is caught before deploy rather than after.
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot integrations.2 -->
|
|
5
|
+
| **integration-tester** | Mandatory gate on any integration boundary; runs real flows, and for a `live-api` the contract suite against both implementations; verifies each cited premise; keeps `.claude/integrations/<slug>.md` current | `.claude/agents/integration-tester.md` |
|
|
6
|
+
|
|
7
|
+
<!-- nina:slot integrations.3 -->
|
|
8
|
+
- **`.claude/integrations/`** — one doc per integration, holding its verified premises
|