@xhulz/nina 0.44.0 → 0.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/package.json +1 -1
  2. package/releases/0.45.0/core/GAPS.md +125 -0
  3. package/releases/0.45.0/core/answered.json +41 -0
  4. package/releases/0.45.0/core/defaults/tree/.claude/agents/architect.md +1 -0
  5. package/releases/0.45.0/core/defaults/tree/.claude/agents/dba.md +3 -0
  6. package/releases/0.45.0/core/defaults/tree/.claude/agents/devops.md +1 -0
  7. package/releases/0.45.0/core/defaults/tree/.claude/agents/implementer.md +1 -0
  8. package/releases/0.45.0/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
  9. package/releases/0.45.0/core/defaults/tree/.claude/agents/qa.md +2 -0
  10. package/releases/0.45.0/core/defaults/tree/.claude/agents/reviewer.md +3 -0
  11. package/releases/0.45.0/core/defaults/tree/.claude/agents/secops.md +3 -0
  12. package/releases/0.45.0/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
  13. package/releases/0.45.0/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
  14. package/releases/0.45.0/core/tree/.claude/agents/architect.md +127 -0
  15. package/releases/0.45.0/core/tree/.claude/agents/dba.md +110 -0
  16. package/releases/0.45.0/core/tree/.claude/agents/devops.md +115 -0
  17. package/releases/0.45.0/core/tree/.claude/agents/implementer.md +158 -0
  18. package/releases/0.45.0/core/tree/.claude/agents/integration-tester.md +168 -0
  19. package/releases/0.45.0/core/tree/.claude/agents/planner.md +132 -0
  20. package/releases/0.45.0/core/tree/.claude/agents/qa.md +152 -0
  21. package/releases/0.45.0/core/tree/.claude/agents/reviewer.md +183 -0
  22. package/releases/0.45.0/core/tree/.claude/agents/secops.md +120 -0
  23. package/releases/0.45.0/core/tree/.claude/agents/solidity-auditor.md +126 -0
  24. package/releases/0.45.0/core/tree/.claude/agents/solidity-dev.md +123 -0
  25. package/releases/0.45.0/core/tree/.claude/agents-overview.md +64 -0
  26. package/releases/0.45.0/core/tree/.claude/frontend.md +10 -0
  27. package/releases/0.45.0/core/tree/.claude/graph.md +75 -0
  28. package/releases/0.45.0/core/tree/.claude/patterns.md +248 -0
  29. package/releases/0.45.0/core/tree/.claude/pills/README.md +147 -0
  30. package/releases/0.45.0/core/tree/.claude/pipeline.md +101 -0
  31. package/releases/0.45.0/core/tree/.claude/retrieval.md +103 -0
  32. package/releases/0.45.0/core/tree/.claude/router.md +198 -0
  33. package/releases/0.45.0/core/tree/.claude/templates/integration.md +45 -0
  34. package/releases/0.45.0/core/tree/CLAUDE.md +163 -0
  35. package/releases/0.45.0/core/tree/scripts/cost-watch.mjs +25 -0
  36. package/releases/0.45.0/core/tree/scripts/edit-guard.mjs +24 -0
  37. package/releases/0.45.0/core/tree/scripts/harness-check.mjs +71 -0
  38. package/releases/0.45.0/core/tree/scripts/loop-gate.mjs +19 -0
  39. package/releases/0.45.0/core/vocabulary.json +13 -0
  40. package/releases/0.45.0/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
  41. package/releases/0.45.0/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
  42. package/releases/0.45.0/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
  43. package/releases/0.45.0/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
  44. package/releases/0.45.0/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
  45. package/releases/0.45.0/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
  46. package/releases/0.45.0/surfaces/blockchain/tree/.claude/graph.md +11 -0
  47. package/releases/0.45.0/surfaces/blockchain/tree/.claude/router.md +6 -0
  48. package/releases/0.45.0/surfaces/blockchain/tree/CLAUDE.md +12 -0
  49. package/releases/0.45.0/surfaces/db/tree/.claude/agents/architect.md +5 -0
  50. package/releases/0.45.0/surfaces/db/tree/.claude/agents/dba.md +50 -0
  51. package/releases/0.45.0/surfaces/db/tree/.claude/agents/devops.md +5 -0
  52. package/releases/0.45.0/surfaces/db/tree/.claude/agents/implementer.md +2 -0
  53. package/releases/0.45.0/surfaces/db/tree/.claude/agents/integration-tester.md +10 -0
  54. package/releases/0.45.0/surfaces/db/tree/.claude/agents/planner.md +2 -0
  55. package/releases/0.45.0/surfaces/db/tree/.claude/agents/reviewer.md +22 -0
  56. package/releases/0.45.0/surfaces/db/tree/.claude/agents/secops.md +2 -0
  57. package/releases/0.45.0/surfaces/db/tree/.claude/agents-overview.md +5 -0
  58. package/releases/0.45.0/surfaces/db/tree/.claude/graph.md +8 -0
  59. package/releases/0.45.0/surfaces/db/tree/.claude/patterns.md +19 -0
  60. package/releases/0.45.0/surfaces/db/tree/.claude/pipeline.md +13 -0
  61. package/releases/0.45.0/surfaces/db/tree/.claude/retrieval.md +5 -0
  62. package/releases/0.45.0/surfaces/db/tree/.claude/router.md +4 -0
  63. package/releases/0.45.0/surfaces/db/tree/CLAUDE.md +17 -0
  64. package/releases/0.45.0/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
  65. package/releases/0.45.0/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
  66. package/releases/0.45.0/surfaces/edge-cf/tree/.claude/agents/devops.md +32 -0
  67. package/releases/0.45.0/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
  68. package/releases/0.45.0/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
  69. package/releases/0.45.0/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
  70. package/releases/0.45.0/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
  71. package/releases/0.45.0/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
  72. package/releases/0.45.0/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
  73. package/releases/0.45.0/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
  74. package/releases/0.45.0/surfaces/edge-cf/tree/CLAUDE.md +27 -0
  75. package/releases/0.45.0/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
  76. package/releases/0.45.0/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
  77. package/releases/0.45.0/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
  78. package/releases/0.45.0/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
  79. package/releases/0.45.0/surfaces/frontend/tree/.claude/agents/reviewer.md +56 -0
  80. package/releases/0.45.0/surfaces/frontend/tree/.claude/patterns.md +8 -0
  81. package/releases/0.45.0/surfaces/frontend/tree/.claude/retrieval.md +2 -0
  82. package/releases/0.45.0/surfaces/frontend/tree/.claude/router.md +14 -0
  83. package/releases/0.45.0/surfaces/frontend/tree/CLAUDE.md +5 -0
  84. package/releases/0.45.0/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
  85. package/releases/0.45.0/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
  86. package/releases/0.45.0/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
  87. package/releases/0.45.0/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
  88. package/releases/0.45.0/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
  89. package/releases/0.45.0/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
  90. package/releases/0.45.0/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
  91. package/releases/0.45.0/surfaces/integrations/tree/.claude/graph.md +10 -0
  92. package/releases/0.45.0/surfaces/integrations/tree/.claude/patterns.md +38 -0
  93. package/releases/0.45.0/surfaces/integrations/tree/.claude/pipeline.md +16 -0
  94. package/releases/0.45.0/surfaces/integrations/tree/.claude/retrieval.md +16 -0
  95. package/releases/0.45.0/surfaces/integrations/tree/.claude/router.md +6 -0
  96. package/releases/0.45.0/surfaces/integrations/tree/CLAUDE.md +17 -0
  97. package/releases/0.45.0/surfaces/money/tree/.claude/agents/architect.md +12 -0
  98. package/releases/0.45.0/surfaces/money/tree/.claude/agents/dba.md +14 -0
  99. package/releases/0.45.0/surfaces/money/tree/.claude/agents/implementer.md +11 -0
  100. package/releases/0.45.0/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
  101. package/releases/0.45.0/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
  102. package/releases/0.45.0/surfaces/money/tree/.claude/agents/secops.md +5 -0
  103. package/releases/0.45.0/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
  104. package/releases/0.45.0/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
  105. package/releases/0.45.0/surfaces/money/tree/.claude/graph.md +2 -0
  106. package/releases/0.45.0/surfaces/money/tree/.claude/patterns.md +49 -0
  107. package/releases/0.45.0/surfaces/money/tree/.claude/pipeline.md +2 -0
  108. package/releases/0.45.0/surfaces/money/tree/.claude/retrieval.md +2 -0
  109. package/releases/0.45.0/surfaces/money/tree/CLAUDE.md +11 -0
  110. package/releases/0.45.0/surfaces/pii/tree/.claude/agents/dba.md +2 -0
  111. package/releases/0.45.0/surfaces/pii/tree/.claude/agents/devops.md +8 -0
  112. package/releases/0.45.0/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
  113. package/releases/0.45.0/surfaces/pii/tree/.claude/agents/secops.md +2 -0
  114. package/releases/0.45.0/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
  115. package/releases/0.45.0/surfaces/pii/tree/CLAUDE.md +5 -0
  116. package/releases/0.45.0/surfaces/prisma/tree/.claude/agents/architect.md +2 -0
  117. package/releases/0.45.0/surfaces/prisma/tree/.claude/agents/dba.md +39 -0
  118. package/releases/0.45.0/surfaces/prisma/tree/.claude/agents/devops.md +11 -0
  119. package/releases/0.45.0/surfaces/prisma/tree/.claude/agents/implementer.md +2 -0
  120. package/releases/0.45.0/surfaces/prisma/tree/.claude/agents/integration-tester.md +2 -0
  121. package/releases/0.45.0/surfaces/prisma/tree/.claude/agents/reviewer.md +2 -0
  122. package/releases/0.45.0/surfaces/prisma/tree/.claude/patterns.md +15 -0
  123. package/releases/0.45.0/surfaces/prisma/tree/CLAUDE.md +5 -0
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Runs this project's harness drift detectors.
4
+ *
5
+ * The mechanism lives in the NINA package — what counts as drift, what counts as a check
6
+ * that could not run, and how either reaches a Claude Code hook. Only the LIST is here,
7
+ * because only this project knows which detectors it has and what to do when one fires.
8
+ *
9
+ * Usage:
10
+ * node scripts/harness-check.mjs human-readable; exit 1 on drift, 2 on error
11
+ * node scripts/harness-check.mjs --hook JSON for a Stop hook — shown to the person; always exit 0
12
+ * node scripts/harness-check.mjs --context JSON for a UserPromptSubmit hook — read by the model
13
+ * before it answers; always exit 0
14
+ */
15
+
16
+ import { dirname, resolve } from 'node:path';
17
+ import { fileURLToPath } from 'node:url';
18
+ import { runDetectors } from '@xhulz/nina/detectors';
19
+
20
+ const ROOT = resolve(dirname(dirname(fileURLToPath(import.meta.url))));
21
+
22
+ process.exit(
23
+ runDetectors(
24
+ [
25
+ // Every composed project has this one, so the core carries it rather than asking each
26
+ // project to write the same nine lines. It disables itself where the project does not
27
+ // declare the npm script, like any other detector.
28
+ {
29
+ name: 'agent specs',
30
+ declaredBy: 'harness:compose:check',
31
+ bin: 'nina',
32
+ args: ['compose', '--check', '--drift', '--quiet'],
33
+ hint: 'these files are generated — run `nina where <file>` to see which layer owns it, then `nina compose`',
34
+ ignore: /^compose: current$/,
35
+ },
36
+ // What the project has not declared yet: vocabulary, its own layer, the documents the layers
37
+ // read, the wiring. On a new project this is the whole to-do list, and it reaches the model
38
+ // before its first answer — so the first conversation starts by filling it, unasked.
39
+ {
40
+ name: 'declaration',
41
+ bin: 'nina',
42
+ args: ['check', '--detector', '--quiet'],
43
+ hint: 'each line says what is missing — `.nina/TODO.md` lists the project’s own items, and `.nina/BRIEF.md`, where the interview wrote one, says what the project is; fill them, then `nina compose`',
44
+ ignore: /^check: declaration is sound/,
45
+ },
46
+ // The one link of the learning cycle that fails in silence: a role keeps being sent back
47
+ // and nothing is written down. It fires on the event — three loop-backs since that role's
48
+ // newest lesson — not on a rate, so it says something new when it speaks. A lesson learned
49
+ // a third time it sends to the harness itself, and says so once.
50
+ {
51
+ name: 'lessons',
52
+ bin: 'nina',
53
+ args: ['learn', '--check', '--quiet'],
54
+ hint: 'each line above says what it needs — `nina learn` shows the whole cycle',
55
+ ignore: /^learn: current$/,
56
+ },
57
+ // The loop gate holds the caps in .claude/graph.md from this project's hooks, and it lets every
58
+ // call through when it fails — so a gate that stopped working looks exactly like a pipeline with
59
+ // no loops. This asks it, every turn, whether it is wired and would still hold one.
60
+ {
61
+ name: 'loop gate',
62
+ bin: 'nina',
63
+ args: ['gate', '--selftest', '--quiet'],
64
+ hint: 'the caps in .claude/graph.md are not being held — `nina wire --apply` wires the gate; `nina gate --selftest` says what else is wrong',
65
+ ignore: /^gate: current$/,
66
+ },
67
+ <!-- nina:slot project.1 detectors -->
68
+ ],
69
+ { root: ROOT, hook: process.argv.includes('--hook'), context: process.argv.includes('--context') },
70
+ ),
71
+ );
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * The loop gate, run by this project's Claude Code hooks.
4
+ *
5
+ * `.claude/graph.md` caps every loop-back edge. This holds the cap: it records the verdict each stage
6
+ * reports, each dispatch that goes out and each time the owner speaks, and sends the dispatch that would
7
+ * make a round past its edge's cap to the owner to confirm — the graph's `human`. The owner's next
8
+ * message starts every count over. It keeps metadata only, under ~/.nina/gate/.
9
+ *
10
+ * The mechanism lives in the NINA package; this file only says which project it guards. It prints
11
+ * nothing unless it has a decision to report, and it lets every call through if anything goes wrong —
12
+ * `nina gate --selftest`, run by `pnpm harness:check`, is what says so.
13
+ */
14
+
15
+ import { dirname, resolve } from 'node:path';
16
+ import { fileURLToPath } from 'node:url';
17
+ import { runGate } from '@xhulz/nina/gate';
18
+
19
+ process.exitCode = await runGate({ root: resolve(dirname(dirname(fileURLToPath(import.meta.url)))) });
@@ -0,0 +1,13 @@
1
+ {
2
+ "ARCHITECT_EFFORT": "xhigh",
3
+ "BUILD_CMD": "pnpm build",
4
+ "DEEP_EFFORT": "high",
5
+ "DEEP_MODEL": "claude-opus-5-5",
6
+ "LINT_CMD": "pnpm lint",
7
+ "RUN_READ_WARN": "40",
8
+ "STEP_FILES": "15",
9
+ "TEST_CMD": "pnpm test",
10
+ "TYPECHECK_CMD": "pnpm typecheck",
11
+ "WORK_EFFORT": "xhigh",
12
+ "WORK_MODEL": "claude-sonnet-5-5"
13
+ }
@@ -0,0 +1,5 @@
1
+ <!-- nina:slot blockchain.1 -->
2
+ | a contract, a proxy, or a contracts library component | **`develop-secure-contracts`** · **`upgrade-solidity-contracts`** when storage moves |
3
+
4
+ <!-- nina:slot blockchain.2 -->
5
+ - **Contract surface (MANDATORY when the spec reaches `{{CONTRACTS_DIR}}`).** Name which contracts are touched and whether each is deployed; for a deployed one, say whether this is an upgrade and what preserves the storage layout. State the access-control model in roles, not in adjectives: who may call each state-changing function, and how that role is granted and revoked. List the adversarial tests the implementation owes — re-entry, the unauthorized caller, the boundary amount, the second call that must fail. A spec that reaches a contract and leaves any of these to the implementer is rejected.
@@ -0,0 +1,5 @@
1
+ <!-- nina:slot blockchain.1 -->
2
+ - **`upgrade-solidity-contracts`** — before deploying a contract or executing a proxy upgrade, to check the storage layout against what is live.
3
+
4
+ <!-- nina:slot blockchain.2 -->
5
+ - **A contract deploy needs `solidity-auditor` `APPROVED` for this exact diff**, and its rollback named before it runs — which for a non-upgradeable contract is a redeploy plus a migration of state, not a revert. Say so *before* deploying. A deploy to a network carrying real value needs an explicit go from {{OWNER}} for that change, the same as production.
@@ -0,0 +1,5 @@
1
+ <!-- nina:slot blockchain.1 -->
2
+ - **The solidity-auditor gate ran and returned `APPROVED`** for any diff touching `{{CONTRACTS_DIR}}`, a deploy script, or a contracts library version. Its absence is a blocking finding on its own, whatever the diff looks like to you.
3
+
4
+ <!-- nina:slot blockchain.2 -->
5
+ - A contract change written by the implementer rather than **solidity-dev** is rejected on routing alone. The two roles carry different rules about what a defect costs, and the cheaper set is the wrong one here.
@@ -0,0 +1,18 @@
1
+ <!-- nina:slot blockchain.1 -->
2
+ | **`develop-secure-contracts`** | any library component in the diff — to check the usage against the library's own guidance, not against memory |
3
+
4
+ <!-- nina:slot blockchain.2 -->
5
+ | **`upgrade-solidity-contracts`** | a proxy, an initializer, or a storage layout change |
6
+
7
+ <!-- nina:slot blockchain.3 -->
8
+ | **`security-audit`** | the off-chain half — deploy scripts, key handling, CI, RPC endpoints (guidance mode) |
9
+
10
+ <!-- nina:slot blockchain.4 -->
11
+ - The deployed addresses and role holders in play, where the change targets a live system.
12
+
13
+ <!-- nina:slot blockchain.5 -->
14
+ - **An event for every state change**, and that what it emits matches what was written. Off-chain
15
+ reconstruction is the only history there is, and a wrong event is worse than a missing one.
16
+
17
+ <!-- nina:slot blockchain.6 -->
18
+ - Approve a change to a live contract without stating the upgrade path and naming who can execute it.
@@ -0,0 +1,16 @@
1
+ <!-- nina:slot blockchain.1 -->
2
+ | **`setup-solidity-contracts`** | a new contract project, a dependency install, or import/remapping configuration |
3
+
4
+ <!-- nina:slot blockchain.2 -->
5
+ | **`develop-secure-contracts`** | any library component — token standards, access control, pausing, reentrancy protection |
6
+
7
+ <!-- nina:slot blockchain.3 -->
8
+ | **`upgrade-solidity-contracts`** | a proxy, an initializer, or anything that moves storage |
9
+
10
+ <!-- nina:slot blockchain.4 -->
11
+ - The contract's deployed state, where one exists. A change to a live contract is an upgrade with a
12
+ storage layout to preserve, not an edit.
13
+
14
+ <!-- nina:slot blockchain.5 -->
15
+ - Widen a function's visibility, or add an address to a role, to make a test pass. The test is
16
+ describing the access control; fix the test or reject the spec.
@@ -0,0 +1,6 @@
1
+ <!-- nina:slot blockchain.1 -->
2
+ - **Contract guardrail** → deployed code is immutable and permanently callable by anyone, so a defect is not a patch away and "nothing calls this" is not a property of it. Reentrancy, a state-changing function with no access control, a storage layout broken by an upgrade, and an audited component used against its own guidance are caught here.
3
+
4
+ <!-- nina:slot blockchain.2 -->
5
+ | **solidity-dev** | Write contract sources and their adversarial tests to spec; never deploys | `.claude/agents/solidity-dev.md` |
6
+ | **solidity-auditor** | Mandatory gate on any contract diff; blocks reviewer and devops | `.claude/agents/solidity-auditor.md` |
@@ -0,0 +1,11 @@
1
+ <!-- nina:slot blockchain.1 -->
2
+ - `solidity-dev` — writes contract code, which cannot be changed once it is live
3
+ - `solidity-auditor` — gate: audits contract code as an attacker before anything deploys
4
+
5
+ <!-- nina:slot blockchain.2 -->
6
+ - `architect` → `solidity-dev` on `SPEC-READY` — the spec changes contract code
7
+ - `solidity-dev` → `solidity-auditor` on `DIFF-READY`
8
+ - `solidity-dev` → `architect` on `BLOCKED` — the change cannot be made safely as specified · max 2
9
+ - `solidity-auditor` → `reviewer` on `APPROVED`
10
+ - `solidity-auditor` → `solidity-dev` on `REJECTED` — a flaw in the contract code · max 2
11
+ - `solidity-auditor` → `architect` on `REJECTED` — a flaw in the design · max 2
@@ -0,0 +1,6 @@
1
+ <!-- nina:slot blockchain.1 -->
2
+
3
+ ### Solidity-auditor is a guardrail for contract changes
4
+ Invoke **solidity-auditor** on any diff that touches `{{CONTRACTS_DIR}}`, a deploy script, or the version of a contracts library — regardless of where you are in the pipeline. No contract change merges without its approval, and none deploys without it. Reviewer verifies it ran; devops verifies it approved.
5
+
6
+ Contract sources are written by **solidity-dev**, not the implementer. Route the work there when the spec's file list reaches `{{CONTRACTS_DIR}}`, and keep the implementer on the off-chain side of the same feature.
@@ -0,0 +1,12 @@
1
+ <!-- nina:slot blockchain.1 -->
2
+ 16. **Every contract change goes through the solidity-auditor gate, and deployed code is never patched in place.** A diff touching a contract source, a deploy script, or the version of a contracts library does not reach the reviewer without `VERDICT: APPROVED` from **solidity-auditor**, and does not reach any network without it. Contract sources are written by **solidity-dev**, not by the implementer — the implementer's rules assume a defect is a patch away, and here it is a redeploy or a proxy upgrade at best. Private keys, mnemonics and funded accounts never enter the repository in any form, test fixtures included.
3
+
4
+ <!-- nina:slot blockchain.2 -->
5
+ | **ANY step touching a contract, a deploy script, or a contracts library version** | **architect → solidity-dev → solidity-auditor → reviewer → qa** — the auditor is mandatory and blocks the reviewer |
6
+
7
+ <!-- nina:slot blockchain.3 -->
8
+ - **solidity-dev** → write contract sources and their adversarial tests strictly to spec; never deploys, never widens access control to make a test pass
9
+ - **solidity-auditor** → **mandatory gate** on any contract diff; `APPROVED` required before reviewer and before any deploy
10
+
11
+ <!-- nina:slot blockchain.4 -->
12
+ - **contract code**: a contract source, a deploy script, or the version of a contracts library
@@ -0,0 +1,5 @@
1
+ <!-- nina:slot db.2 -->
2
+ - **Tenant-scope impact** — state where `{{TENANT_KEY}}` is enforced. Every new query in app code filters by it, or reaches it through the model's foreign keys; only the auth library's own tables are exempt.
3
+
4
+ <!-- nina:slot db.3 -->
5
+ - **Database flag** — if the schema or a new query is touched, mark **"DBA REQUIRED"** prominently at the top of the spec, and name each new read's cache policy, or **`NO_CACHE`** for a read that feeds a write whose correctness depends on it being fresh — a stale row contaminates every decision made from it.
@@ -0,0 +1,50 @@
1
+ <!-- nina:slot db.3 -->
2
+ - Any change to the schema.
3
+ - Any new query, or change to one, anywhere in application code.
4
+ - Any migration.
5
+
6
+ <!-- nina:slot db.4 -->
7
+ - The schema diff, and the migration SQL it generates.
8
+ - The application code that uses the new or modified queries.
9
+
10
+ <!-- nina:slot db.5 -->
11
+ **Read `.claude/code-map.md`** when the change touches new query patterns — it lists which services consume which models, so you can spot N+1 risk and consumer-side cache implications without re-grepping. (Before there is code, fall back to `.claude/architecture.md` § *Database* for the planned schema and its cache policies.)
12
+
13
+ <!-- nina:slot db.6 -->
14
+ - **The schema validates, and you read the migration's SQL** — offline, from files, with the project's own schema tooling. A check that seems to need a database URL is one you report as impossible, not one you run.
15
+
16
+ <!-- nina:slot db.8 -->
17
+ - **Migration safety on large tables:**
18
+ - No `ALTER TABLE ... ADD COLUMN NOT NULL` without a safe default (causes lock + rewrite).
19
+ - No operations that hold long locks without an explicit batched strategy.
20
+ - Non-breaking for rolling deploy — old application code and new schema must coexist during the deploy window.
21
+
22
+ <!-- nina:slot db.9 -->
23
+ - **Index coverage.** Every new query pattern (`WHERE`, `ORDER BY`, `JOIN`, `aggregate`) must be backed by an index declared in the schema. Reject queries that would table-scan. The most-used compound index is `(<tenant>, <other>)` or, for models keyed by a parent entity, `(<parentId>, <other>)`; verify the leading column matches the query.
24
+
25
+ <!-- nina:slot db.10 -->
26
+ - **Tenant scope.** Every query in app code filters by `{{TENANT_KEY}}`, or by the foreign-key chain that resolves back to it. The auth library's own tables are the only exemption. Reject any app query missing the scope.
27
+
28
+ <!-- nina:slot db.11 -->
29
+ - **Cache discipline.** A cached read names its policy explicitly — **an implicit cache is a bug: reject it** — checked against the cache policies in `.claude/architecture.md` § *Database*. A read that feeds a write whose correctness depends on it being fresh is never cached.
30
+
31
+ <!-- nina:slot db.12 -->
32
+ - **N+1 patterns.** If the code fetches a list then queries per item, reject with "fetch the related rows with the list, or batch."
33
+
34
+ <!-- nina:slot db.13 -->
35
+ - **Secrets.** The database's connection string comes from `{{SECRETS_LOCAL}}` locally and `{{SECRETS_PROD}}` in production — never a committed `.env`, never a literal in code.
36
+
37
+ <!-- nina:slot db.14 -->
38
+ - **Run anything that can write to a real database.** You are a read-only gate: against a live database you run read-only queries and nothing else. If a check seems to require writing, it does not — report the limitation to the orchestrator instead.
39
+
40
+ <!-- nina:slot db.15 -->
41
+ - Approve without validating the schema and reading the migration's SQL, offline, as *You MUST check* says.
42
+
43
+ <!-- nina:slot db.16 -->
44
+ - Wave through "small" schema changes. Small changes cause the worst production incidents.
45
+
46
+ <!-- nina:slot db.17 -->
47
+ - Approve a query in app code missing its `{{TENANT_KEY}}` scope, or the foreign key that owns it.
48
+
49
+ <!-- nina:slot db.18 -->
50
+ **Final report format:** ≤300 words if approved. Sections: Schema validation result, Migration SQL preview (verbatim if non-trivial), Index analysis, Tenant-scope check, Cache verdict, Privacy check, Final verdict. If rejected, list each issue with required action — no length cap.
@@ -0,0 +1,5 @@
1
+ <!-- nina:slot db.2 -->
2
+ - **Migrations, in order, against the right database.** Confirm the target is the database you intend, and that the migration is backward-compatible with the code already deployed, because the two are live together during the rollout.
3
+
4
+ <!-- nina:slot db.5 -->
5
+ - **Migrations:** applied / none, with the database they hit.
@@ -0,0 +1,2 @@
1
+ <!-- nina:slot db.2 -->
2
+ - If you change the schema or write a new query, flag it explicitly in the diff summary: it triggers the **dba** gate.
@@ -0,0 +1,10 @@
1
+ <!-- nina:slot db.2 -->
2
+ - A new query on a cached read path — verify the cache's behavior live (dba covers the schema; you cover how the cache behaves at runtime)
3
+
4
+ <!-- nina:slot db.3 -->
5
+ If a query behaves differently against a real database than the dba's static analysis suggested, escalate to **dba** — do not fix it yourself.
6
+
7
+ <!-- nina:slot db.4 -->
8
+ - **The auth library's backing store is a real database** — the development database, the same
9
+ connection the implementer uses from `{{SECRETS_LOCAL}}`. A suite that is green against an
10
+ in-memory stand-in has not exercised the library, which is the whole point of this stage.
@@ -0,0 +1,2 @@
1
+ <!-- nina:slot db.1 -->
2
+ - **Database flag** — if any subtask may touch the schema or create a new query, mark **"DBA REQUIRED"** on that subtask.
@@ -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,19 @@
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.
@@ -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,4 @@
1
+ <!-- nina:slot db.1 -->
2
+
3
+ ### DBA is a guardrail, not a stage
4
+ Invoke **dba** any time the schema, a migration or a query is touched — regardless of where you are in the pipeline. No such change reaches qa without dba approval; the reviewer names it on its Gates line.
@@ -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,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` | `wrangler.toml` 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,2 @@
1
+ <!-- nina:slot edge-cf.1 -->
2
+ - **Edge runtime compatibility.** A database client that runs at the edge uses its edge build, and `compatibility_flags = ["nodejs_compat"]` is set in `wrangler.toml`.
@@ -0,0 +1,32 @@
1
+ <!-- nina:slot edge-cf.1 -->
2
+ - **`cloudflare:wrangler`** — before running ANY `wrangler` command or reading `wrangler.toml`.
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
+ - **Both targets, or neither.** A change to the API surface needs the **staging Worker** (`wrangler deploy --env staging`) *and*, where the project has one, the frontend's **Pages** deploy (`{{PAGES_PROJECT}}`). Deploying the frontend alone against an old API is the failure that renders new fields as "—". State explicitly 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
+
16
+ ## Pages: which branch, and why it matters
17
+
18
+ `{{PAGES_PROJECT}}` is a **direct-upload** Pages project whose production branch is `main`. `wrangler pages
19
+ deploy` decides production vs preview purely from `--branch`:
20
+
21
+ - **Staging FE** → `--branch staging`. A preview deployment on its own URL. Production is untouched,
22
+ and this is the FE half of the staging pair with `{{API_STAGING}}`.
23
+ - **Production FE** → `--branch main`. Only with an explicit go from {{OWNER}}, per the rule above.
24
+
25
+ Deploying staging with `--branch=main` is a production deploy wearing a staging label — the
26
+ permission classifier is right to stop it. Use the staging branch and the block disappears, because
27
+ the block was correct.
28
+
29
+ **A variable the bundle reads must be in the environment of the build command.** `[vars]` and
30
+ `[env.*.vars]` in `{{APP_DIR}}/wrangler.toml` do NOT reach the build — a bundler inlines them at build
31
+ time, and Pages vars apply at runtime to a project that has no server. A missing one ships a blank page
32
+ with no error.
@@ -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 `wrangler.toml` |
12
+
13
+ <!-- nina:slot edge-cf.5 -->
14
+ - Respect Workers conventions: secrets in `{{SECRETS_LOCAL}}` for local dev (never commit), `compatibility_flags = ["nodejs_compat"]` in `wrangler.toml`.
@@ -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
@@ -0,0 +1,3 @@
1
+ <!-- nina:slot edge-cf.1 -->
2
+ Here `$STRAY` is `vitest|workerd`: a Worker test boots a second runtime process alongside vitest,
3
+ and it is the one that holds ~2 GB and survives a killed test run.
@@ -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. `wrangler secret`/`[vars]` split 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
+ - `compatibility_flags = ["nodejs_compat"]` in `wrangler.toml` 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*, `{{API_DIR}}/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. `compatibility_flags = ["nodejs_compat"]` is required in `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 jsdom checks while being 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.